tango-app-api-payment-subscription 3.1.12 → 3.1.14-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/controllers/billing.controllers.js +278 -1
- package/src/controllers/invoice.controller.js +808 -261
- package/src/controllers/payment.controller.js +339 -0
- package/src/controllers/paymentSubscription.controllers.js +179 -48
- package/src/dtos/isUniqueEvent.js +16 -0
- package/src/dtos/validation.dtos.js +66 -7
- package/src/hbs/invoicePdf.hbs +7 -0
- package/src/hbs/invoicepaymentemail.hbs +925 -0
- package/src/routes/billing.routes.js +15 -2
- package/src/routes/invoice.routes.js +9 -3
- package/src/routes/payment.routes.js +41 -0
- package/src/routes/paymentSubscription.routes.js +2 -0
- package/src/services/billing.service.js +4 -0
- package/src/services/invoice.service.js +8 -0
- package/src/services/paymentAccount.service.js +14 -0
- package/src/services/transaction.service.js +10 -0
|
@@ -10,6 +10,10 @@ import * as invoiceService from '../services/invoice.service.js';
|
|
|
10
10
|
import * as userService from '../services/user.service.js';
|
|
11
11
|
import * as dailyPriceService from '../services/dailyPrice.service.js';
|
|
12
12
|
import * as cameraService from '../services/camera.service.js';
|
|
13
|
+
import * as billingService from '../services/billing.service.js';
|
|
14
|
+
import * as paymentAccountService from '../services/paymentAccount.service.js';
|
|
15
|
+
|
|
16
|
+
|
|
13
17
|
import dayjs from 'dayjs';
|
|
14
18
|
import Handlebars from 'handlebars';
|
|
15
19
|
import fs from 'fs';
|
|
@@ -93,14 +97,39 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
93
97
|
if ( !clientInfo ) {
|
|
94
98
|
return res.sendError( 'no data found', 204 );
|
|
95
99
|
}
|
|
96
|
-
let storeCount = await storeService.count( { clientId: clientInfo[0].clientId } );
|
|
97
|
-
let
|
|
100
|
+
let storeCount = await storeService.count( { clientId: clientInfo[0].clientId, status: 'active' } );
|
|
101
|
+
let tangoProductsList = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
|
|
102
|
+
let tangoProducts = tangoProductsList.basePricing.map( ( item ) => item.productName );
|
|
98
103
|
let activeProducts = clientInfo[0].planDetails.product;
|
|
99
104
|
let liveProducts = [];
|
|
100
105
|
let trialProducts = [];
|
|
101
106
|
let expiredProducts = [];
|
|
102
107
|
let currentDate = new Date();
|
|
103
108
|
|
|
109
|
+
let storeQuery = [
|
|
110
|
+
{
|
|
111
|
+
$match: {
|
|
112
|
+
clientId: clientInfo[0].clientId,
|
|
113
|
+
status: 'active',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{ $unwind: '$product' },
|
|
117
|
+
{
|
|
118
|
+
$group: {
|
|
119
|
+
_id: '$product',
|
|
120
|
+
total: { $sum: 1 },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
$project: {
|
|
125
|
+
_id: 0,
|
|
126
|
+
product: '$_id',
|
|
127
|
+
count: '$total',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
let productDetails = await storeService.aggregate( storeQuery );
|
|
104
133
|
// Function to calculate the difference between two dates
|
|
105
134
|
function dateDifference( date1, date2 ) {
|
|
106
135
|
// Convert both dates to milliseconds
|
|
@@ -125,22 +154,29 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
125
154
|
}
|
|
126
155
|
|
|
127
156
|
activeProducts.forEach( ( element ) => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
break;
|
|
132
|
-
case 'tangoZone':
|
|
133
|
-
element.aliseProductName = 'Tango Zone';
|
|
134
|
-
break;
|
|
135
|
-
case 'tangoSop':
|
|
136
|
-
element.aliseProductName = 'Tango SOP';
|
|
137
|
-
break;
|
|
138
|
-
case 'prioritySupport':
|
|
139
|
-
element.aliseProductName = 'Priority Support';
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
break;
|
|
157
|
+
let price = tangoProductsList.basePricing.find( ( item ) => item.productName == element.productName );
|
|
158
|
+
if ( price ) {
|
|
159
|
+
element.price = price.basePrice;
|
|
143
160
|
}
|
|
161
|
+
let getProductCount = productDetails.find( ( item ) => item.product == element.productName );
|
|
162
|
+
element.storeCount = getProductCount?.count || 0;
|
|
163
|
+
element.aliseProductName = convertTitleCase( element.productName );
|
|
164
|
+
// switch ( element.productName ) {
|
|
165
|
+
// case 'tangoTraffic':
|
|
166
|
+
// element.aliseProductName = 'Tango Traffic';
|
|
167
|
+
// break;
|
|
168
|
+
// case 'tangoZone':
|
|
169
|
+
// element.aliseProductName = 'Tango Zone';
|
|
170
|
+
// break;
|
|
171
|
+
// case 'tangoTrax':
|
|
172
|
+
// element.aliseProductName = 'Tango Trax';
|
|
173
|
+
// break;
|
|
174
|
+
// case 'prioritySupport':
|
|
175
|
+
// element.aliseProductName = 'Priority Support';
|
|
176
|
+
// break;
|
|
177
|
+
// default:
|
|
178
|
+
// break;
|
|
179
|
+
// }
|
|
144
180
|
|
|
145
181
|
if ( element.status == 'live' ) {
|
|
146
182
|
liveProducts.push( { 'productName': element.productName, 'aliseProductName': element.aliseProductName } );
|
|
@@ -226,6 +262,16 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
|
|
|
226
262
|
|
|
227
263
|
export const pricingInfo = async ( req, res ) => {
|
|
228
264
|
try {
|
|
265
|
+
if ( req.query?.clientId ) {
|
|
266
|
+
let clientDetails = await paymentService.findOne( { clientId: req.query.clientId }, { planDetails: 1, paymentInvoice: 1 } );
|
|
267
|
+
if ( clientDetails ) {
|
|
268
|
+
req.body.storesCount = clientDetails.planDetails.totalStores;
|
|
269
|
+
req.body.products = req.body?.products || clientDetails.planDetails.product.map( ( product ) => product.productName );
|
|
270
|
+
req.body.planName = req.body?.planName || clientDetails.planDetails.subscriptionPeriod;
|
|
271
|
+
req.body.currencyType = clientDetails.paymentInvoice?.currencyType ? clientDetails.paymentInvoice?.currencyType == 'inr' ? 'rupees' : 'dollar' : 'rupees';
|
|
272
|
+
req.body.camaraPerSqft = clientDetails.planDetails.storeSize;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
229
275
|
req.body.calculateSignup = true;
|
|
230
276
|
let pricingDetails = await calculatePricing( req, res );
|
|
231
277
|
return res.sendSuccess( pricingDetails );
|
|
@@ -256,6 +302,7 @@ async function calculatePricing( req, res ) {
|
|
|
256
302
|
$project: {
|
|
257
303
|
_id: 0,
|
|
258
304
|
basePricing: 1,
|
|
305
|
+
discounts: 1,
|
|
259
306
|
},
|
|
260
307
|
},
|
|
261
308
|
];
|
|
@@ -267,10 +314,15 @@ async function calculatePricing( req, res ) {
|
|
|
267
314
|
return { ...item.basePricing };
|
|
268
315
|
} );
|
|
269
316
|
let camaraCount;
|
|
317
|
+
let productWisePricing = [];
|
|
270
318
|
input.products.forEach( async ( element, index ) => {
|
|
271
319
|
let getProduct = productList.find( ( item ) => item.productName == element );
|
|
272
|
-
let
|
|
273
|
-
let
|
|
320
|
+
let basicprice;
|
|
321
|
+
let camaraPerSqft;
|
|
322
|
+
if ( input.camaraPerSqft ) {
|
|
323
|
+
camaraPerSqft = getProduct?.camaraPerStores.filter( ( data ) => data.sqft == input.camaraPerSqft );
|
|
324
|
+
basicprice = Math.round( getProduct.basePrice * ( camaraPerSqft[0].camaraCount ) );
|
|
325
|
+
}
|
|
274
326
|
if ( req.body.calculateSignup ) {
|
|
275
327
|
productDiscounts.push( getProduct.signupPercentage );
|
|
276
328
|
} else {
|
|
@@ -307,26 +359,39 @@ async function calculatePricing( req, res ) {
|
|
|
307
359
|
let discountprice = Math.round( basicprice * ( Math.pow( 0.92, stage ) )/10 )*10;
|
|
308
360
|
dummy.push( discountprice );
|
|
309
361
|
OriginalPrice = OriginalPrice + discountprice;
|
|
362
|
+
if ( index>0&&pricingDetails.length>0 ) {
|
|
363
|
+
let addOnDiscounts = 0;
|
|
364
|
+
for ( const discount of pricingDetails[0].discounts ) {
|
|
365
|
+
if ( discount.products.length === input.products.length &&
|
|
366
|
+
discount.products.every( ( product ) => input.products.includes( product ) ) ) {
|
|
367
|
+
addOnDiscounts = discount.discount;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
discountprice = discountprice-( ( discountprice*addOnDiscounts )/100 );
|
|
371
|
+
productWisePricing.push( { product: element, price: discountprice } );
|
|
372
|
+
} else {
|
|
373
|
+
productWisePricing.push( { product: element, price: discountprice } );
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
310
377
|
finalPrice = finalPrice + discountprice;
|
|
311
|
-
|
|
378
|
+
if ( camaraPerSqft&& camaraPerSqft.length>0 ) {
|
|
379
|
+
camaraArray.push( Number( Math.ceil( camaraPerSqft[0].camaraCount ) ) );
|
|
380
|
+
}
|
|
312
381
|
camaraCount = Math.max( ...camaraArray );
|
|
313
382
|
if ( dummy.length == input.products.length ) {
|
|
314
|
-
if ( input.products.length > 1 ) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
// OriginalPrice = OriginalPrice + addtionalDiscount
|
|
320
|
-
}
|
|
383
|
+
// if ( input.products.length > 1 &&pricingDetails.length>0 ) {
|
|
384
|
+
// let maxProductDiscounts = Math.max( ...productDiscounts );
|
|
385
|
+
// let addtionalDiscount = ( finalPrice * maxProductDiscounts ) / 100;
|
|
386
|
+
// finalPrice = finalPrice - addtionalDiscount;
|
|
387
|
+
// }
|
|
321
388
|
if ( input.planName == 'quarterly' ) {
|
|
322
389
|
let extraDiscount = ( finalPrice * 10 ) / 100;
|
|
323
390
|
finalPrice = finalPrice - extraDiscount;
|
|
324
|
-
// OriginalPrice = OriginalPrice + extra_discount
|
|
325
391
|
}
|
|
326
392
|
if ( input.planName == 'annual' ) {
|
|
327
393
|
let extraDiscount = ( finalPrice * 20 ) / 100;
|
|
328
394
|
finalPrice = finalPrice - extraDiscount;
|
|
329
|
-
// OriginalPrice = OriginalPrice + extra_discount
|
|
330
395
|
}
|
|
331
396
|
finalPrice = Math.ceil( finalPrice / 10 ) * 10; // for round off to 10 position
|
|
332
397
|
if ( input.currencyType && input.currencyType == 'dollar' ) {
|
|
@@ -337,7 +402,7 @@ async function calculatePricing( req, res ) {
|
|
|
337
402
|
}
|
|
338
403
|
}
|
|
339
404
|
} );
|
|
340
|
-
return ( { OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
405
|
+
return ( { productWisePricing: productWisePricing, OriginalPrice: Math.round( OriginalPrice ), price: Math.round( finalPrice ), camaraCount: camaraCount } );
|
|
341
406
|
} catch ( e ) {
|
|
342
407
|
logger.error( { error: e, function: 'calculatePricing' } );
|
|
343
408
|
return false;
|
|
@@ -1491,7 +1556,7 @@ export const productViewList = async ( req, res ) => {
|
|
|
1491
1556
|
{
|
|
1492
1557
|
$match: {
|
|
1493
1558
|
clientId: req.query.clientId,
|
|
1494
|
-
|
|
1559
|
+
status: 'active',
|
|
1495
1560
|
},
|
|
1496
1561
|
},
|
|
1497
1562
|
{ $unwind: '$product' },
|
|
@@ -1511,6 +1576,7 @@ export const productViewList = async ( req, res ) => {
|
|
|
1511
1576
|
];
|
|
1512
1577
|
|
|
1513
1578
|
let storeProductCount = await storeService.aggregate( query );
|
|
1579
|
+
|
|
1514
1580
|
let clientProduct = await paymentService.findOne( { clientId: req.query.clientId }, { 'planDetails.product': 1 } );
|
|
1515
1581
|
|
|
1516
1582
|
if ( !clientProduct.planDetails.product.length ) {
|
|
@@ -1535,6 +1601,7 @@ export const productViewList = async ( req, res ) => {
|
|
|
1535
1601
|
basePrice: price,
|
|
1536
1602
|
storeCount: count,
|
|
1537
1603
|
status: item.status,
|
|
1604
|
+
totalCost: Math.round( price*count ),
|
|
1538
1605
|
},
|
|
1539
1606
|
);
|
|
1540
1607
|
} );
|
|
@@ -1549,7 +1616,19 @@ export const productViewList = async ( req, res ) => {
|
|
|
1549
1616
|
// item.basePrice = productBasePrice.basePrice;
|
|
1550
1617
|
// }
|
|
1551
1618
|
// } );
|
|
1552
|
-
|
|
1619
|
+
let storecount = await storeService.count( {
|
|
1620
|
+
clientId: req.query.clientId,
|
|
1621
|
+
status: 'active',
|
|
1622
|
+
} );
|
|
1623
|
+
|
|
1624
|
+
let totalSum = products.reduce( ( sum, product ) => sum + product.totalCost, 0 );
|
|
1625
|
+
let transaction = await paymentAccountService.findOneAccount( { clientId: req.query.clientId } );
|
|
1626
|
+
let planDetails = {
|
|
1627
|
+
totalSum: totalSum,
|
|
1628
|
+
totalStores: storecount,
|
|
1629
|
+
accountcredit: transaction.credit?transaction.credit:0,
|
|
1630
|
+
};
|
|
1631
|
+
return res.sendSuccess( { products: products, planDetails: planDetails } );
|
|
1553
1632
|
} catch ( e ) {
|
|
1554
1633
|
logger.error( { error: e, function: 'productViewList' } );
|
|
1555
1634
|
return res.sendError( e, 500 );
|
|
@@ -2014,18 +2093,18 @@ export const invoiceList = async ( req, res ) => {
|
|
|
2014
2093
|
item.billingDate = dayjs( item.billingDate ).format( 'DD MMM, YYYY' );
|
|
2015
2094
|
if ( item.products?.length > 0 ) {
|
|
2016
2095
|
let newProducts = [];
|
|
2017
|
-
// for ( let productIndex = 0; productIndex < item.products.length; productIndex++ ) {
|
|
2018
|
-
// let [ firstWord, secondWord ] = item.products[productIndex]?.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
2019
|
-
// firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
2020
|
-
// item.products[productIndex].productName = firstWord + ' ' + secondWord;
|
|
2021
|
-
// newProducts.push( item.products[productIndex]?.productName );
|
|
2022
|
-
// }
|
|
2023
2096
|
for ( let productIndex = 0; productIndex < item.products.length; productIndex++ ) {
|
|
2024
|
-
let [ firstWord, secondWord ] = item.products[productIndex]?.
|
|
2097
|
+
let [ firstWord, secondWord ] = item.products[productIndex]?.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
2025
2098
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
2026
|
-
item.products[productIndex].
|
|
2027
|
-
newProducts.push( item.products[productIndex]?.
|
|
2099
|
+
item.products[productIndex].productName = firstWord + ' ' + secondWord;
|
|
2100
|
+
newProducts.push( item.products[productIndex]?.productName );
|
|
2028
2101
|
}
|
|
2102
|
+
// for ( let productIndex = 0; productIndex < item.products.length; productIndex++ ) {
|
|
2103
|
+
// let [ firstWord, secondWord ] = item.products[productIndex]?.product?.product.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
2104
|
+
// firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
2105
|
+
// item.products[productIndex].product.product = firstWord + ' ' + secondWord;
|
|
2106
|
+
// newProducts.push( item.products[productIndex]?.product?.product );
|
|
2107
|
+
// }
|
|
2029
2108
|
item.productList = newProducts;
|
|
2030
2109
|
}
|
|
2031
2110
|
} );
|
|
@@ -2774,7 +2853,7 @@ export const invoiceDownload = async ( req, res ) => {
|
|
|
2774
2853
|
invoiceDate,
|
|
2775
2854
|
dueDate,
|
|
2776
2855
|
};
|
|
2777
|
-
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/
|
|
2856
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf1.hbs', 'utf8' );
|
|
2778
2857
|
const template = Handlebars.compile( templateHtml );
|
|
2779
2858
|
const html = template( { ...invoiceData } );
|
|
2780
2859
|
let file = {
|
|
@@ -2791,7 +2870,7 @@ export const invoiceDownload = async ( req, res ) => {
|
|
|
2791
2870
|
};
|
|
2792
2871
|
const date = dayjs( invoiceData.monthOfbilling, 'MM' );
|
|
2793
2872
|
let monthName = date.format( 'MMMM' );
|
|
2794
|
-
|
|
2873
|
+
monthName = 'Jul-Sep';
|
|
2795
2874
|
|
|
2796
2875
|
htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
|
|
2797
2876
|
if ( req.body.sendInvoice ) {
|
|
@@ -3060,7 +3139,7 @@ export const dailyPricingInsert = async ( req, res ) => {
|
|
|
3060
3139
|
let query = [
|
|
3061
3140
|
{
|
|
3062
3141
|
$match: {
|
|
3063
|
-
clientId:
|
|
3142
|
+
clientId: getClient.clientId,
|
|
3064
3143
|
},
|
|
3065
3144
|
},
|
|
3066
3145
|
{ $unwind: '$stores' },
|
|
@@ -3085,7 +3164,21 @@ export const dailyPricingInsert = async ( req, res ) => {
|
|
|
3085
3164
|
let dailyData = await dailyPriceService.aggregate( query );
|
|
3086
3165
|
let cameraDetails = await cameraService.find( { storeId: getStore[storeIndex].storeId, clientId: requestClient[clientIndex], isActivated: true, isUp: true }, { streamName: 1 } );
|
|
3087
3166
|
let firstDate = dayjs( getStore[storeIndex]?.edge?.firstFileDate ).format( 'YYYY-MM-DD' );
|
|
3088
|
-
let workingdays
|
|
3167
|
+
let workingdays;
|
|
3168
|
+
if ( firstDate < requestData.date && getStore[storeIndex]?.status == 'active' &&
|
|
3169
|
+
dailyData[0]?.dateString != dayjs( requestData.date, 'YYYY-MM-DD' ).format( 'YYYY-MM-DD' ) ) {
|
|
3170
|
+
if ( dailyData.length && dailyData[0]?.stores?.daysDifference ) {
|
|
3171
|
+
workingdays = dailyData[0]?.stores?.daysDifference + 1;
|
|
3172
|
+
} else {
|
|
3173
|
+
workingdays = 1;
|
|
3174
|
+
}
|
|
3175
|
+
} else {
|
|
3176
|
+
if ( dailyData[0]?.stores?.daysDifference ) {
|
|
3177
|
+
workingdays = dailyData[0]?.stores?.daysDifference;
|
|
3178
|
+
} else {
|
|
3179
|
+
workingdays = 0;
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3089
3182
|
let priceDetails = getClient.priceType == 'standard' ? getBaseprice.standard : getBaseprice.step;
|
|
3090
3183
|
for ( let storeProductIndex = 0; storeProductIndex < getStore[storeIndex].product.length; storeProductIndex++ ) {
|
|
3091
3184
|
let productDetails;
|
|
@@ -3189,6 +3282,7 @@ export const dailyPricingInsertV1 = async ( req, res ) => {
|
|
|
3189
3282
|
for ( let i=1; i<requestData.days; i++ ) {
|
|
3190
3283
|
dayList.push( dayjs( requestData.date ).add( i, 'day' ).format( 'YYYY-MM-DD' ) );
|
|
3191
3284
|
}
|
|
3285
|
+
|
|
3192
3286
|
if ( requestData && requestClient.length > 0 ) {
|
|
3193
3287
|
for ( let i=0; i<dayList.length; i++ ) {
|
|
3194
3288
|
requestData.date = dayList[i];
|
|
@@ -3207,7 +3301,7 @@ export const dailyPricingInsertV1 = async ( req, res ) => {
|
|
|
3207
3301
|
let query = [
|
|
3208
3302
|
{
|
|
3209
3303
|
$match: {
|
|
3210
|
-
clientId:
|
|
3304
|
+
clientId: getClient.clientId,
|
|
3211
3305
|
},
|
|
3212
3306
|
},
|
|
3213
3307
|
{ $unwind: '$stores' },
|
|
@@ -3232,7 +3326,21 @@ export const dailyPricingInsertV1 = async ( req, res ) => {
|
|
|
3232
3326
|
let dailyData = await dailyPriceService.aggregate( query );
|
|
3233
3327
|
let cameraDetails = await cameraService.find( { storeId: getStore[storeIndex].storeId, clientId: requestClient[clientIndex], isActivated: true, isUp: true }, { streamName: 1 } );
|
|
3234
3328
|
let firstDate = dayjs( getStore[storeIndex]?.edge?.firstFileDate ).format( 'YYYY-MM-DD' );
|
|
3235
|
-
let workingdays
|
|
3329
|
+
let workingdays;
|
|
3330
|
+
if ( firstDate < requestData.date && getStore[storeIndex]?.status == 'active' &&
|
|
3331
|
+
dailyData[0]?.dateString != dayjs( requestData.date, 'YYYY-MM-DD' ).format( 'YYYY-MM-DD' ) ) {
|
|
3332
|
+
if ( dailyData.length && dailyData[0]?.stores?.daysDifference ) {
|
|
3333
|
+
workingdays = dailyData[0]?.stores?.daysDifference + 1;
|
|
3334
|
+
} else {
|
|
3335
|
+
workingdays = 1;
|
|
3336
|
+
}
|
|
3337
|
+
} else {
|
|
3338
|
+
if ( dailyData[0]?.stores?.daysDifference ) {
|
|
3339
|
+
workingdays = dailyData[0]?.stores?.daysDifference;
|
|
3340
|
+
} else {
|
|
3341
|
+
workingdays = 0;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3236
3344
|
let priceDetails = getClient.priceType == 'standard' ? getBaseprice.standard : getBaseprice.step;
|
|
3237
3345
|
for ( let storeProductIndex = 0; storeProductIndex < getStore[storeIndex].product.length; storeProductIndex++ ) {
|
|
3238
3346
|
let productDetails;
|
|
@@ -3276,13 +3384,14 @@ export const dailyPricingInsertV1 = async ( req, res ) => {
|
|
|
3276
3384
|
},
|
|
3277
3385
|
);
|
|
3278
3386
|
if ( storeIndex == getStore.length - 1 ) {
|
|
3387
|
+
let activestores = storeList.filter( ( store ) => store.status=='active' );
|
|
3279
3388
|
let params = {
|
|
3280
3389
|
clientId: requestClient[clientIndex],
|
|
3281
3390
|
stores: storeList,
|
|
3282
3391
|
dateISO: new Date( requestData.date ),
|
|
3283
3392
|
accountType: getClient?.planDetails?.subscriptionType,
|
|
3284
3393
|
status: getClient?.status,
|
|
3285
|
-
activeStores:
|
|
3394
|
+
activeStores: activestores?.length,
|
|
3286
3395
|
brandName: getClient?.clientName,
|
|
3287
3396
|
proRate: getClient?.paymentInvoice?.proRate,
|
|
3288
3397
|
dateString: dayjs( requestData.date, 'YYYY-MM-DD' ).format( 'YYYY-MM-DD' ),
|
|
@@ -3676,3 +3785,25 @@ function convertTitleCase( data ) {
|
|
|
3676
3785
|
}
|
|
3677
3786
|
|
|
3678
3787
|
|
|
3788
|
+
export async function createDefaultbillings( req, res ) {
|
|
3789
|
+
let clientlist = await paymentService.find( { 'status': 'active' } );
|
|
3790
|
+
|
|
3791
|
+
for ( let client of clientlist ) {
|
|
3792
|
+
let storeslist = await storeService.find( { clientId: client.clientId }, { storeId: 1 } );
|
|
3793
|
+
storeslist = storeslist.map( ( a ) => a.storeId );
|
|
3794
|
+
let payload = {
|
|
3795
|
+
clientId: client.clientId,
|
|
3796
|
+
registeredCompanyName: client.billingDetails.companyName,
|
|
3797
|
+
gst: client.billingDetails.gstNumber,
|
|
3798
|
+
addressLineOne: client.billingDetails.billingAddress,
|
|
3799
|
+
placeOfSupply: client.billingDetails.PlaceOfSupply,
|
|
3800
|
+
stores: storeslist,
|
|
3801
|
+
currency: client.paymentInvoice.currencyType,
|
|
3802
|
+
generateInvoiceTo: [ ...client.paymentInvoice.invoiceCC, ...client.paymentInvoice.invoiceCC.invoiceTo?client.paymentInvoice.invoiceCC.invoiceTo:[] ],
|
|
3803
|
+
isPrimary: true,
|
|
3804
|
+
|
|
3805
|
+
};
|
|
3806
|
+
await billingService.create( payload );
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
import { logger } from 'tango-app-api-middleware';
|
|
3
|
+
|
|
4
|
+
const processedEventIds = new Set();
|
|
5
|
+
|
|
6
|
+
export const checkDuplicateEvent = ( req, res, next ) => {
|
|
7
|
+
const eventId = req.headers['x-razorpay-event-id'];
|
|
8
|
+
|
|
9
|
+
if ( processedEventIds.has( eventId ) ) {
|
|
10
|
+
logger.error( { error: `Duplicate event with ID ${eventId}. Skipping processing.`, function: 'checkDuplicateEvent' } );
|
|
11
|
+
res.status( 200 ).send();
|
|
12
|
+
} else {
|
|
13
|
+
processedEventIds.add( eventId );
|
|
14
|
+
next();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -40,11 +40,11 @@ export const validateProductListParams = {
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export const validateProductsSchema = joi.object( {
|
|
43
|
-
camaraPerSqft: joi.string().
|
|
44
|
-
currencyType: joi.string().
|
|
45
|
-
planName: joi.string().
|
|
46
|
-
products: joi.array().
|
|
47
|
-
storesCount: joi.string().
|
|
43
|
+
camaraPerSqft: joi.string().optional(),
|
|
44
|
+
currencyType: joi.string().optional(),
|
|
45
|
+
planName: joi.string().optional(),
|
|
46
|
+
products: joi.array().optional(),
|
|
47
|
+
storesCount: joi.string().optional(),
|
|
48
48
|
} );
|
|
49
49
|
|
|
50
50
|
export const validateProducts = {
|
|
@@ -298,14 +298,14 @@ export const createBillingGroupBody = joi.object(
|
|
|
298
298
|
groupName: joi.string().required(),
|
|
299
299
|
groupTag: joi.string().required(),
|
|
300
300
|
registeredCompanyName: joi.string().required(),
|
|
301
|
-
gst: joi.string().
|
|
301
|
+
gst: joi.string().optional(),
|
|
302
302
|
addressLineOne: joi.string().optional(),
|
|
303
303
|
addressLineTwo: joi.string().optional(),
|
|
304
304
|
city: joi.string().optional(),
|
|
305
305
|
state: joi.string().optional(),
|
|
306
306
|
country: joi.string().optional(),
|
|
307
307
|
pinCode: joi.string().optional(),
|
|
308
|
-
placeOfSupply: joi.string().
|
|
308
|
+
placeOfSupply: joi.string().optional(),
|
|
309
309
|
po: joi.string().optional(),
|
|
310
310
|
stores: joi.array().optional(),
|
|
311
311
|
proRata: joi.string().optional(),
|
|
@@ -378,3 +378,62 @@ export const billingGroupBody = joi.object( {
|
|
|
378
378
|
export const billingGroupSchema = {
|
|
379
379
|
body: billingGroupBody,
|
|
380
380
|
};
|
|
381
|
+
|
|
382
|
+
export const getVirtualAccountQuery = {
|
|
383
|
+
clientId: joi.string().required(),
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
export const getVirtualAccountSchema = {
|
|
387
|
+
query: getVirtualAccountQuery,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
export const getInvoiceBody = joi.object( {
|
|
391
|
+
limit: joi.number().required(),
|
|
392
|
+
offset: joi.number().required(),
|
|
393
|
+
clientId: joi.string().required(),
|
|
394
|
+
sortColumn: joi.string().optional(),
|
|
395
|
+
sortBy: joi.number().optional(),
|
|
396
|
+
searchValue: joi.string().optional(),
|
|
397
|
+
isExport: joi.boolean().optional(),
|
|
398
|
+
filter: joi.string().optional(),
|
|
399
|
+
} );
|
|
400
|
+
|
|
401
|
+
export const getInvoiceSchema = {
|
|
402
|
+
body: getInvoiceBody,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
export const valletPayParam = joi.object().keys( {
|
|
406
|
+
invoice: joi.string().required(),
|
|
407
|
+
} );
|
|
408
|
+
|
|
409
|
+
export const valletPayValid = {
|
|
410
|
+
params: valletPayParam,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
export const verifyPaymentParam = joi.object().keys( {
|
|
414
|
+
invoice: joi.string().required(),
|
|
415
|
+
} );
|
|
416
|
+
|
|
417
|
+
export const verifyPaymentBody = joi.object().keys( {
|
|
418
|
+
razorpay_payment_id: joi.string().required(),
|
|
419
|
+
razorpay_order_id: joi.string().required(),
|
|
420
|
+
razorpay_signature: joi.string().required(),
|
|
421
|
+
} );
|
|
422
|
+
|
|
423
|
+
export const verifyPaymentValid = {
|
|
424
|
+
params: verifyPaymentParam,
|
|
425
|
+
body: verifyPaymentBody,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
export const onetimeFeeParam = joi.object().keys( {
|
|
429
|
+
invoice: joi.string().required(),
|
|
430
|
+
} );
|
|
431
|
+
|
|
432
|
+
export const onetimeFeeBody = joi.object().keys( {
|
|
433
|
+
oneTimeFee: joi.number().required(),
|
|
434
|
+
} );
|
|
435
|
+
|
|
436
|
+
export const onetimeFeeValid = {
|
|
437
|
+
params: onetimeFeeParam,
|
|
438
|
+
body: onetimeFeeBody,
|
|
439
|
+
};
|
package/src/hbs/invoicePdf.hbs
CHANGED
|
@@ -1498,12 +1498,19 @@
|
|
|
1498
1498
|
</div>
|
|
1499
1499
|
</div>
|
|
1500
1500
|
<div class="frame-23972">
|
|
1501
|
+
<div class="frame-2394">
|
|
1502
|
+
<div class="text9">Discount ({{discountPercentage}}%)</div>
|
|
1503
|
+
<div class="frame-9157">
|
|
1504
|
+
<div class="text10">{{currencyType}} {{discountAmount}}</div>
|
|
1505
|
+
</div>
|
|
1506
|
+
</div>
|
|
1501
1507
|
<div class="frame-2394">
|
|
1502
1508
|
<div class="text9">Sub Total</div>
|
|
1503
1509
|
<div class="frame-9157">
|
|
1504
1510
|
<div class="text10">{{currencyType}} {{amount}}</div>
|
|
1505
1511
|
</div>
|
|
1506
1512
|
</div>
|
|
1513
|
+
|
|
1507
1514
|
{{#each tax }}
|
|
1508
1515
|
<div class="frame-2392">
|
|
1509
1516
|
<div class="text9">{{type}} ({{value}}%)</div>
|