tango-app-api-payment-subscription 3.0.26-dev → 3.0.27-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-payment-subscription",
3
- "version": "3.0.26-dev",
3
+ "version": "3.0.27-dev",
4
4
  "description": "paymentSubscription",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "nodemon": "^3.1.0",
26
26
  "pdfmake": "^0.2.10",
27
27
  "swagger-ui-express": "^5.0.0",
28
- "tango-api-schema": "^2.0.62",
28
+ "tango-api-schema": "^2.0.78",
29
29
  "tango-app-api-middleware": "^1.0.49-dev",
30
30
  "winston": "^3.12.0",
31
31
  "winston-daily-rotate-file": "^5.0.0"
@@ -2,11 +2,13 @@
2
2
  /* eslint-disable new-cap */
3
3
  import { logger, download, sendEmailWithSES, appConfig, insertOpenSearchData } from 'tango-app-api-middleware';
4
4
  import * as paymentService from '../services/clientPayment.services.js';
5
+ import * as basePriceService from '../services/basePrice.service.js';
5
6
  import * as storeService from '../services/store.service.js';
6
7
  import * as basePricingService from '../services/basePrice.service.js';
7
8
  import * as clientRequestService from '../services/clientRequest.service.js';
8
9
  import * as invoiceService from '../services/invoice.service.js';
9
10
  import * as userService from '../services/user.service.js';
11
+ import * as dailyPriceService from '../services/dailyPrice.service.js';
10
12
  import dayjs from 'dayjs';
11
13
  import Handlebars from 'handlebars';
12
14
  import fs from 'fs';
@@ -73,7 +75,7 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
73
75
  price: 1,
74
76
  priceType: 1,
75
77
  virtualAccount: 1,
76
- // paymentInvoice: 1,
78
+ paymentInvoice: 1,
77
79
  },
78
80
  },
79
81
  {
@@ -199,7 +201,8 @@ export const clientBillingSubscriptionInfo = async ( req, res, next ) => {
199
201
  currentPlanInfo.expiredProducts = expiredProducts || '--';
200
202
  currentPlanInfo.product = activeProducts || '--';
201
203
  currentPlanInfo.pendingClientRequest = getPCR;
202
-
204
+ currentPlanInfo.currencyType = clientInfo[0].paymentInvoice.currencyType;
205
+ currentPlanInfo.dollarPrice = await convertINRtoUSD( clientInfo[0].price );
203
206
  let data = {
204
207
  _id: clientInfo[0]._id,
205
208
  clientId: clientInfo[0].clientId,
@@ -472,6 +475,8 @@ export const updateSubscription = async ( req, res ) => {
472
475
  await storeService.updateMany( { clientId: req.params.clientId, status: 'active' }, { product: storeProduct } );
473
476
 
474
477
  if ( result.modifiedCount ) {
478
+ req.body.clientId = req.params.clientId;
479
+ updatePricing( req, res, true );
475
480
  return res.sendSuccess( { message: 'Subscription Updated Successfully' } );
476
481
  } else {
477
482
  return res.sendError( 'Something went wrong', 500 );
@@ -939,6 +944,7 @@ export const productSubscribe = async ( req, res ) => {
939
944
  if ( productList.includes( item.name ) && item.type =='unsubscribe' ) {
940
945
  // let findIndex = product.findIndex( ( product ) => product.productName );
941
946
  // product.splice( findIndex, 1 );
947
+ // let requestDetails = await clientRequestService.findOne({});
942
948
  await storeService.addremoveElement( { clientId: clientInfo.clientId, status: 'active', product: { $in: item.name } }, { $pull: { product: item.name } } );
943
949
  }
944
950
  if ( !productList.includes( item.name ) && [ 'trial', 'subscription' ].includes( item.type ) ) {
@@ -1627,6 +1633,11 @@ export const pricingListUpdate = async ( req, res ) => {
1627
1633
  getPriceInfo.step = req.body.products;
1628
1634
  }
1629
1635
  getPriceInfo.save().then( async () => {
1636
+ let clientDetails = await paymentService.findOne( { clientId: req.body.clientId }, { priceType: 1 } );
1637
+ clientDetails.priceType = req.body.type;
1638
+
1639
+ clientDetails.save();
1640
+
1630
1641
  let userDetails= await userService.findOne( { clientId: req.body.clientId, role: 'superadmin' } );
1631
1642
  if ( userDetails ) {
1632
1643
  const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/revisedPriceEmail.hbs', 'utf8' );
@@ -1670,6 +1681,7 @@ export const pricingListUpdate = async ( req, res ) => {
1670
1681
 
1671
1682
  async function updatePricing( req, res, update ) {
1672
1683
  let baseProduct = await basePricingService.findOne( { clientId: { $exists: false } }, { basePricing: 1 } );
1684
+ let getPriceInfo = await basePricingService.findOne( { clientId: { $exists: true }, clientId: req.body.clientId }, { standard: 1, step: 1 } );
1673
1685
  let clientDetails = await paymentService.findOne( { clientId: req.body.clientId } );
1674
1686
  if ( clientDetails ) {
1675
1687
  let products = clientDetails.planDetails.product.map( ( item ) => item.productName );
@@ -1701,7 +1713,7 @@ async function updatePricing( req, res, update ) {
1701
1713
  step: stepList,
1702
1714
  clientId: req.body.clientId,
1703
1715
  };
1704
- if ( !update ) {
1716
+ if ( !getPriceInfo ) {
1705
1717
  await basePricingService.create( data );
1706
1718
  } else {
1707
1719
  delete data.clientId;
@@ -1788,6 +1800,7 @@ export const unpaidInvoiceList = async ( req, res ) => {
1788
1800
  if ( !invoiceDetails.length ) {
1789
1801
  return res.sendError( 'no data found', 204 );
1790
1802
  }
1803
+
1791
1804
  return res.sendSuccess( invoiceDetails );
1792
1805
  } catch ( e ) {
1793
1806
  logger.error( { error: e, function: 'unpaidInvoiceList' } );
@@ -1930,9 +1943,32 @@ export const getExpiredClients = async ( req, res ) => {
1930
1943
 
1931
1944
  export const invoiceDownload = async ( req, res ) => {
1932
1945
  try {
1946
+ let invoiceDetails;
1947
+ let invoiceInfo = await invoiceService.findOne( { _id: req.params.invoiceId } );
1948
+ if ( invoiceInfo ) {
1949
+ let clientDetails = await paymentService.findOne( { clientId: invoiceInfo.clientId } );
1950
+ let amount = 0;
1951
+ invoiceInfo.products.forEach( ( item ) => {
1952
+ amount = amount + item.price;
1953
+ } );
1954
+ let invoiceDate= dayjs( invoiceInfo.createdAt ).format( 'DD MMM, YYYY' );
1955
+ let days = clientDetails?.paymentInvoice?.extendPaymentPeriodDays || 10;
1956
+ let dueDate = invoiceInfo?.dueDate ? dayjs( invoiceInfo?.dueDate ).format( 'DD MMM, YYYY' ) : dayjs().add( days, 'days' ).format( 'DD MMM, YYYY' );
1957
+ invoiceDetails = {
1958
+ ...invoiceInfo._doc,
1959
+ clientName: clientDetails.clientName,
1960
+ extendDays: clientDetails.paymentInvoice.extendPaymentPeriodDays,
1961
+ address: clientDetails.billingDetails.billingAddress,
1962
+ amount: amount,
1963
+ // total: ( amount + ( parseFloat( amount ) * 0.19 ) ).toFixed( 2 ),
1964
+ total: amount.toFixed( 2 ),
1965
+ invoiceDate,
1966
+ dueDate,
1967
+ };
1968
+ }
1933
1969
  const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
1934
1970
  const template = Handlebars.compile( templateHtml );
1935
- const html = template( { data: '' } );
1971
+ const html = template( { data: invoiceDetails } );
1936
1972
  let fonts = {
1937
1973
  Helvetica: {
1938
1974
  normal: 'Helvetica',
@@ -1945,8 +1981,8 @@ export const invoiceDownload = async ( req, res ) => {
1945
1981
 
1946
1982
  let { window } = new JSDOM( '' );
1947
1983
  const htmlTemplate = htmlToPdfmake( html, { window: window, tableAutoSize: true } );
1948
-
1949
1984
  const docDefinition = {
1985
+ pageMargins: [ 40, 0, 40, 40 ],
1950
1986
  content: htmlTemplate,
1951
1987
  defaultStyle: {
1952
1988
  font: 'Helvetica',
@@ -1973,12 +2009,25 @@ export const updateInvoiceStatus = async ( req, res ) => {
1973
2009
  return res.sendError( 'no data found', 204 );
1974
2010
  }
1975
2011
  invoiceDetails.status = req.body?.status || 'Payment Received';
2012
+ invoiceDetails.paymentType = req.body?.paymentType;
2013
+ invoiceDetails.paymentReferenceId = req.body?.paymentReferenceId || '';
1976
2014
  invoiceDetails.save().then( async () => {
1977
2015
  let clientInfo = await paymentService.findOne( { clientId: invoiceDetails.clientId } );
1978
2016
  if ( clientInfo ) {
1979
2017
  clientInfo.planDetails.paymentStatus = 'paid';
1980
2018
  clientInfo.save();
1981
2019
  }
2020
+ const logObj = {
2021
+ clientId: req.body.clientId,
2022
+ userName: req.user?.userName,
2023
+ email: req.user?.email,
2024
+ date: new Date(),
2025
+ logType: 'invoice',
2026
+ logSubType: 'Invoice Status Updated to Paid',
2027
+ changes: [ 'amount', 'paymentType', 'status' ],
2028
+ eventType: 'update',
2029
+ };
2030
+ insertOpenSearchData( 'tango-retail-activity-logs', logObj );
1982
2031
  return res.sendSuccess( 'Invoice updated Successfully' );
1983
2032
  } );
1984
2033
  } catch ( e ) {
@@ -2023,3 +2072,195 @@ export const invoiceCreate = async ( req, res ) => {
2023
2072
  return res.sendError( e, 500 );
2024
2073
  }
2025
2074
  };
2075
+
2076
+ export const dailyPricingInsert = async ( req, res ) => {
2077
+ try {
2078
+ let requestData = req.body;
2079
+ let requestClient = requestData.clientId;
2080
+ if ( requestData.clientId && requestClient.length > 0 ) {
2081
+ for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
2082
+ let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
2083
+ if ( getClient ) {
2084
+ let getBaseprice = await basePriceService.findOne( { clientId: requestClient[clientIndex] } );
2085
+ let getStore = await storeService.find( { clientId: requestClient[clientIndex], status: 'active' } );
2086
+ if ( getStore.length ) {
2087
+ let storeList = [];
2088
+ for ( let storeIndex = 0; storeIndex < getStore.length; storeIndex++ ) {
2089
+ let productList = [];
2090
+ let priceDetails = getClient.priceType == 'standard' ? getBaseprice.standard : getBaseprice.step;
2091
+ for ( let storeProductIndex = 0; storeProductIndex < getStore[storeIndex].product.length; storeProductIndex++ ) {
2092
+ let productDetails;
2093
+ if ( getClient.priceType == 'standard' ) {
2094
+ productDetails = priceDetails.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2095
+ } else {
2096
+ productDetails = priceDetails.find( ( item ) => {
2097
+ let range = item.storeRange.split( '-' );
2098
+ if ( parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) ) {
2099
+ return ( item.productName == getStore[storeIndex].product[storeProductIndex] && parseInt( range[0] ) <= ( storeIndex + 1 ) && parseInt( range[1] ) >= ( storeIndex + 1 ) );
2100
+ }
2101
+ } );
2102
+ if ( !productDetails ) {
2103
+ let stepProductDetails = priceDetails.filter( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2104
+ productDetails = stepProductDetails[stepProductDetails.length -1];
2105
+ }
2106
+ }
2107
+ let productStatus = getClient.planDetails.product.find( ( item ) => item.productName == getStore[storeIndex].product[storeProductIndex] );
2108
+ if ( productDetails ) {
2109
+ let newObject = {
2110
+ productName: productDetails.productName,
2111
+ status: productStatus ? productStatus.status : '',
2112
+ price: productStatus ? [ 'trial', 'free' ].includes( productStatus.status ) ? 0 : productDetails.negotiatePrice : 0,
2113
+ featureStoreCount: storeIndex+1,
2114
+ basePrice: productDetails.negotiatePrice,
2115
+ ...( getClient.priceType == 'step' ? { storeRange: productDetails.storeRange } : { storeRange: 'standard' } ),
2116
+ };
2117
+ productList.push( newObject );
2118
+ }
2119
+ }
2120
+ storeList.push(
2121
+ {
2122
+ storeId: getStore[storeIndex].storeId,
2123
+ products: productList,
2124
+ },
2125
+ );
2126
+ if ( storeIndex == getStore.length-1 ) {
2127
+ let params = {
2128
+ clientId: requestClient[clientIndex],
2129
+ stores: storeList,
2130
+ dateISO: new Date( requestData.date ),
2131
+ dateString: dayjs( requestData.date ).format( 'YYYY-MM-DD' ),
2132
+ };
2133
+ await dailyPriceService.create( params );
2134
+ }
2135
+ }
2136
+ }
2137
+ }
2138
+ if ( clientIndex == requestClient.length-1 ) {
2139
+ return res.sendSuccess( 'Price Details Inserted Successfully' );
2140
+ }
2141
+ }
2142
+ }
2143
+ } catch ( e ) {
2144
+ logger.error( { error: e, function: 'invoiceCreate' } );
2145
+ return res.sendError( e, 500 );
2146
+ }
2147
+ };
2148
+
2149
+ export const invoiceGenerate = async ( req, res ) => {
2150
+ try {
2151
+ let requestData = req.body;
2152
+ let requestClient = requestData.clientId;
2153
+ let start = new Date( requestData.fromDate );
2154
+ let end = new Date( requestData.toDate );
2155
+ let userTimezoneOffset = end.getTimezoneOffset() * 60000;
2156
+ end = new Date( end.getTime() - userTimezoneOffset );
2157
+ end.setUTCHours( 23, 59, 59, 59 );
2158
+ for ( let clientIndex = 0; clientIndex < requestClient.length; clientIndex++ ) {
2159
+ let getClient = await paymentService.findOne( { clientId: requestClient[clientIndex], status: 'active' } );
2160
+ if ( getClient ) {
2161
+ let query = [
2162
+ {
2163
+ $match: {
2164
+ $and: [
2165
+ { clientId: requestClient[clientIndex] },
2166
+ { dateISO: { $gte: start, $lte: end } },
2167
+ ],
2168
+ },
2169
+ },
2170
+ {
2171
+ $unwind: '$stores',
2172
+ },
2173
+ {
2174
+ $unwind: '$stores.products',
2175
+ },
2176
+ {
2177
+ $group: {
2178
+ _id: { product: '$stores.products.productName', storeRange: '$stores.products.storeRange' },
2179
+ price: { $sum: '$stores.products.price' },
2180
+ count: { $sum: 1 },
2181
+ basePrice: { $first: '$stores.products.basePrice' },
2182
+ },
2183
+ },
2184
+ {
2185
+ $project: {
2186
+ _id: 0,
2187
+ product: '$_id',
2188
+ price: 1,
2189
+ count: 1,
2190
+ basePrice: 1,
2191
+ },
2192
+ },
2193
+ ];
2194
+ // {
2195
+ // $facet: {
2196
+ // data: [
2197
+ // {
2198
+ // $unwind: '$stores',
2199
+ // },
2200
+ // {
2201
+ // $unwind: '$stores.products',
2202
+ // },
2203
+ // {
2204
+ // $group: {
2205
+ // _id: { product: '$stores.products.productName', storeRange: '$stores.products.storeRange' },
2206
+ // price: { $sum: '$stores.products.price' },
2207
+ // },
2208
+ // },
2209
+ // {
2210
+ // $project: {
2211
+ // _id: 0,
2212
+ // product: '$_id',
2213
+ // price: 1,
2214
+ // },
2215
+ // },
2216
+ // ],
2217
+ // count: [
2218
+ // {
2219
+ // $unwind: '$stores',
2220
+ // },
2221
+ // { $sort: { 'stores.products._id': -1 } },
2222
+ // { $project: { lastElement: { $arrayElemAt: [ '$stores.products', -1 ] } } },
2223
+ // { $project: {
2224
+ // count: '$lastElement.featureStoreCount',
2225
+ // },
2226
+ // },
2227
+ // ],
2228
+ // },
2229
+ // },
2230
+ let invoiceDetails = await dailyPriceService.aggregate( query );
2231
+ if ( !invoiceDetails.length ) {
2232
+ return res.sendError( 'no data found', 204 );
2233
+ }
2234
+ let amount = 0;
2235
+ invoiceDetails.forEach( ( item ) => {
2236
+ amount = item.price + amount;
2237
+ } );
2238
+ let data = {
2239
+ invoice: `invoice#${requestClient[clientIndex]}-${dayjs().format( 'MMM YYYY' )}`,
2240
+ products: invoiceDetails,
2241
+ status: 'pending',
2242
+ amount: amount,
2243
+ clientId: requestClient[clientIndex],
2244
+ paymentMethod: getClient?.paymentInvoice?.paymentType || 'online',
2245
+ billingDate: new Date(),
2246
+ };
2247
+
2248
+ await invoiceService.create( data );
2249
+ if ( clientIndex == requestClient.length-1 ) {
2250
+ return res.sendSuccess( 'invocie Generated Succesfully' );
2251
+ }
2252
+ }
2253
+ }
2254
+ } catch ( e ) {
2255
+ logger.error( { error: e, function: 'invoiceCreate' } );
2256
+ return res.sendError( e, 500 );
2257
+ }
2258
+ };
2259
+
2260
+ function convertINRtoUSD( amountINR ) {
2261
+ // Current exchange rate (1 INR = 0.012 USD)
2262
+ const exchangeRate = 0.012;
2263
+ // Convert INR to USD
2264
+ const amountUSD = amountINR * exchangeRate;
2265
+ return amountUSD;
2266
+ }
@@ -367,5 +367,307 @@ export const paymentDocs = {
367
367
  },
368
368
  },
369
369
  },
370
+ '/paymentSubscription/admin/getStoreLocationList': {
371
+ get: {
372
+ tags: [ 'payment' ],
373
+ description: 'Store Location List',
374
+ operationId: '',
375
+ parameters: [
376
+ {
377
+ in: 'query',
378
+ name: 'clientId',
379
+ schema: { type: 'string' },
380
+ required: true,
381
+ },
382
+ ],
383
+ responses: {
384
+ 200: { description: 'Store Location List view Details Successfully' },
385
+ 401: { description: 'Incorrect Update' },
386
+ 422: { description: 'Field Error' },
387
+ },
388
+ },
389
+ },
390
+ '/paymentSubscription/admin/addStoreProduct': {
391
+ post: {
392
+ tags: [ 'payment' ],
393
+ description: 'Add Store Product',
394
+ operationId: '',
395
+ parameters: [
396
+ {
397
+ in: 'query',
398
+ name: 'clientId',
399
+ schema: { type: 'string' },
400
+ required: true,
401
+ },
402
+ ],
403
+ requestBody: {
404
+ content: {
405
+ 'application/json': {
406
+ schema: j2s( schema.validateAddStoreProductSchema ).swagger,
407
+ },
408
+ },
409
+ },
410
+ responses: {
411
+ 200: { description: 'Store Product Added Successfully' },
412
+ 401: { description: 'Incorrect Update' },
413
+ 422: { description: 'Field Error' },
414
+ },
415
+ },
416
+ },
417
+ '/paymentSubscription/invoiceList': {
418
+ post: {
419
+ tags: [ 'payment' ],
420
+ description: 'Invoice List',
421
+ operationId: '',
422
+ parameters: [
423
+ {
424
+ in: 'query',
425
+ name: 'clientId',
426
+ schema: { type: 'string' },
427
+ required: true,
428
+ },
429
+ ],
430
+ requestBody: {
431
+ content: {
432
+ 'application/json': {
433
+ schema: j2s( schema.validateInvoiceSchema ).swagger,
434
+ },
435
+ },
436
+ },
437
+ responses: {
438
+ 200: { description: '' },
439
+ 401: { description: 'Incorrect Update' },
440
+ 422: { description: 'Field Error' },
441
+ },
442
+ },
443
+ },
444
+ '/paymentSubscription/admin/priceList': {
445
+ post: {
446
+ tags: [ 'payment' ],
447
+ description: 'Price List',
448
+ operationId: '',
449
+ parameters: [
450
+ {
451
+ in: 'query',
452
+ name: 'clientId',
453
+ schema: { type: 'string' },
454
+ required: true,
455
+ },
456
+ ],
457
+ requestBody: {
458
+ content: {
459
+ 'application/json': {
460
+ schema: j2s( schema.validatePriceListSchema ).swagger,
461
+ },
462
+ },
463
+ },
464
+ responses: {
465
+ 200: { description: 'PriceList Added Successfully' },
466
+ 401: { description: 'Incorrect Update' },
467
+ 422: { description: 'Field Error' },
468
+ },
469
+ },
470
+ },
471
+ '/paymentSubscription/admin/pricing': {
472
+ post: {
473
+ tags: [ 'payment' ],
474
+ description: 'Pricing',
475
+ operationId: '',
476
+ parameters: [
477
+ {
478
+ in: 'query',
479
+ name: 'clientId',
480
+ schema: { type: 'string' },
481
+ required: true,
482
+ },
483
+ ],
484
+ requestBody: {
485
+ content: {
486
+ 'application/json': {
487
+ schema: j2s( schema.validatePriceSchema ).swagger,
488
+ },
489
+ },
490
+ },
491
+ responses: {
492
+ 200: { description: 'Pricing Added Successfully' },
493
+ 401: { description: 'Incorrect Update' },
494
+ 422: { description: 'Field Error' },
495
+ },
496
+ },
497
+ },
498
+ '/paymentSubscription/creditNotes': {
499
+ post: {
500
+ tags: [ 'payment' ],
501
+ description: 'Pricing',
502
+ operationId: '',
503
+ parameters: [
504
+ {
505
+ in: 'query',
506
+ name: 'clientId',
507
+ schema: { type: 'string' },
508
+ required: true,
509
+ },
510
+ ],
511
+ requestBody: {
512
+ content: {
513
+ 'application/json': {
514
+ schema: j2s( schema.revisedSchema ).swagger,
515
+ },
516
+ },
517
+ },
518
+ responses: {
519
+ 200: { description: 'CreditNoted Updated Successfully' },
520
+ 401: { description: 'Incorrect Update' },
521
+ 422: { description: 'Field Error' },
522
+ },
523
+ },
524
+ },
525
+ '/paymentSubscription/creditNotes/invoiceList/{clientId}': {
526
+ get: {
527
+ tags: [ 'payment' ],
528
+ description: 'get Invoice List',
529
+ operationId: '',
530
+ parameters: [ {
531
+ in: 'path',
532
+ name: 'clientId',
533
+ schema: { type: 'string' },
534
+ required: true,
535
+ } ],
536
+ responses: {
537
+ 200: { description: 'Get Invoice Details Successfully' },
538
+ 401: { description: 'Incorrect Update' },
539
+ 422: { description: 'Field Error' },
540
+ },
541
+ },
542
+ },
543
+ '/paymentSubscription/admin/getStoreProducts': {
544
+ post: {
545
+ tags: [ 'payment' ],
546
+ description: 'Get Store Products',
547
+ operationId: '',
548
+ parameters: [
549
+ {
550
+ in: 'query',
551
+ name: 'clientId',
552
+ schema: { type: 'string' },
553
+ required: true,
554
+ },
555
+ ],
556
+ requestBody: {
557
+ content: {
558
+ 'application/json': {
559
+ schema: j2s( schema.validateStoreProductsSchema ).swagger,
560
+ },
561
+ },
562
+ },
563
+ responses: {
564
+ 200: { description: 'Store Products Updated Successfully' },
565
+ 401: { description: 'Incorrect Update' },
566
+ 422: { description: 'Field Error' },
567
+ },
568
+ },
569
+ },
570
+ '/paymentSubscription/admin/getStoreProducts': {
571
+ post: {
572
+ tags: [ 'payment' ],
573
+ description: 'Get Store Products',
574
+ operationId: '',
575
+ parameters: [
576
+ {
577
+ in: 'query',
578
+ name: 'clientId',
579
+ schema: { type: 'string' },
580
+ required: true,
581
+ },
582
+ ],
583
+ requestBody: {
584
+ content: {
585
+ 'application/json': {
586
+ schema: j2s( schema.validateStoreProductsSchema ).swagger,
587
+ },
588
+ },
589
+ },
590
+ responses: {
591
+ 200: { description: 'Store Products Updated Successfully' },
592
+ 401: { description: 'Incorrect Update' },
593
+ 422: { description: 'Field Error' },
594
+ },
595
+ },
596
+ },
597
+ '/v3/paymentSubscription/trialRemaindList': {
598
+ get: {
599
+ tags: [ 'payment' ],
600
+ description: 'Get Trail Remind List',
601
+ operationId: '',
602
+ parameters: [],
603
+ requestBody: {
604
+ },
605
+ responses: {
606
+ 200: { description: 'Trail Remind List Details Successfully' },
607
+ 401: { description: 'Incorrect Update' },
608
+ 422: { description: 'Field Error' },
609
+ },
610
+ },
611
+ },
612
+ '/v3/paymentSubscription/trialExpiredList': {
613
+ get: {
614
+ tags: [ 'payment' ],
615
+ description: 'Get Trail Expired List',
616
+ operationId: '',
617
+ parameters: [],
618
+ requestBody: {
619
+ },
620
+ responses: {
621
+ 200: { description: 'Trail Expired List Details Successfully' },
622
+ 401: { description: 'Incorrect Update' },
623
+ 422: { description: 'Field Error' },
624
+ },
625
+ },
626
+ },
627
+ '/v3/paymentSubscription/invoiceDownload': {
628
+ post: {
629
+ tags: [ 'payment' ],
630
+ description: 'Get Invoice Download',
631
+ operationId: '',
632
+ parameters: [],
633
+ requestBody: {
634
+ },
635
+ responses: {
636
+ 200: { description: 'Invoice Downloaded Successfully' },
637
+ 401: { description: 'Incorrect Update' },
638
+ 422: { description: 'Field Error' },
639
+ },
640
+ },
641
+ },
642
+ '/v3/paymentSubscription/invoiceStatusUpdate/{invoiceId}': {
643
+ post: {
644
+ tags: [ 'payment' ],
645
+ description: 'Get Invoice Status Update',
646
+ operationId: '',
647
+ parameters: [],
648
+ requestBody: {
649
+ },
650
+ responses: {
651
+ 200: { description: 'Invoice Status Updated Successfully' },
652
+ 401: { description: 'Incorrect Update' },
653
+ 422: { description: 'Field Error' },
654
+ },
655
+ },
656
+ },
657
+ '/v3/paymentSubscription/invoice/create': {
658
+ post: {
659
+ tags: [ 'payment' ],
660
+ description: 'Get Invoice Create',
661
+ operationId: '',
662
+ parameters: [],
663
+ requestBody: {
664
+ },
665
+ responses: {
666
+ 200: { description: 'Invoice Created Successfully' },
667
+ 401: { description: 'Incorrect Update' },
668
+ 422: { description: 'Field Error' },
669
+ },
670
+ },
671
+ },
370
672
  };
371
673
 
@@ -191,3 +191,32 @@ export const validateUpdateSubscriptionSchema =joi.object( {
191
191
  export const validateUpdateSubscriptionParams = {
192
192
  body: validateUpdateSubscriptionSchema,
193
193
  };
194
+
195
+ export const dailyPricingSchema = {
196
+ clientId: joi.array().required(),
197
+ date: joi.string().required(),
198
+ };
199
+
200
+ export const dailyPricingParams = {
201
+ body: dailyPricingSchema,
202
+ };
203
+
204
+ export const invoiceGenerateSchema = {
205
+ clientId: joi.array().required(),
206
+ fromDate: joi.string().required(),
207
+ toDate: joi.string().required(),
208
+ };
209
+
210
+ export const invoiceGenerateParams = {
211
+ body: invoiceGenerateSchema,
212
+ };
213
+
214
+ export const invoiceUpdateSchema = {
215
+ amount: joi.number().required(),
216
+ paymentType: joi.string().required(),
217
+ paymentReferenceId: joi.string().required(),
218
+ };
219
+ export const invoiceUpdateParams = {
220
+ body: invoiceUpdateSchema,
221
+ };
222
+
@@ -8,8 +8,8 @@
8
8
 
9
9
  <body>
10
10
 
11
- <div class="frame-2372">
12
- <div>
11
+ <div>
12
+ <div class="header">
13
13
  <svg class="tango-eye-logo-color-option-03" width="230" height="57" viewBox="0 0 230 57" fill="none"
14
14
  xmlns="http://www.w3.org/2000/svg">
15
15
  <path d="M63.8252 13.75H83.127V15.9331H74.6799V40.3642H72.2484V15.9331H63.8252V13.75Z" fill="#0066CC"
@@ -46,47 +46,43 @@
46
46
  </linearGradient>
47
47
  </defs>
48
48
  </svg>
49
- </div>
50
- <div class="header">
51
- <table border="0" style="border:none">
49
+ <table border="0" style="border:none; margin-top:0px; !important">
52
50
  <tr style="border:none">
53
51
  <td style="width:50%;border:none">
54
52
  <div style="font-size:20px;"><b>Tango Eye IT Solutions</b></div>
55
- <div class="margin-top:10px;">2nd Floor, Apeejay Business Centre,</div><div style="margin-top:10px;">No 12 Haddows Road, Nungambakkam</div>
56
- <div class="margin-top:10px;">Chennai Tamil Nadu 600006</div>
57
- <div class="margin-top:10px;">GSTIN 33AAGCT3124R1Z2</div>
53
+ <div class="margin-top:0px;">2nd Floor, Apeejay Business Centre,</div>
54
+ <div style="margin-top:0px;">No 12 Haddows Road, Nungambakkam</div>
55
+ <div class="margin-top:0px;">Chennai Tamil Nadu 600006</div>
56
+ <div class="margin-top:0px;">GSTIN 33AAGCT3124R1Z2</div>
58
57
  </td>
59
58
  <td style="width:50%;border:none;">
60
- <div style="font-size:20px"><b><span>Invoice</span><span style="font-color:#009bf3;"> #LK 0001/08/2023</span></b></div>
61
- <div style="font-size:15px;margin-left:60px;margin-top:10px;"><span>Invoice Date:</span><span>01 March , 2023</span></div>
62
- <div style="font-size:15px;margin-left:60px;margin-top:10px;"><span>Terms :</span><span>Net 30</span></div>
63
- <div style="font-size:15px;margin-left:60px;margin-top:10px;"><span>Due Date :</span><span>03 March , 2023</span></div>
59
+ <div style="font-size:20px;margin-left:80px">
60
+ <b> <span>Invoice </span>
61
+ <span style="color:#009bf3 !important">{{data.invoice}}</span></b>
62
+ </div>
63
+ <div style="font-size:15px;margin-left:130px;margin-top:0px;"><span>Invoice Date: </span><span>{{data.invoiceDate}}</span></div>
64
+ <div style="font-size:15px;margin-left:130px;margin-top:0px;"><span>Terms : </span><span>Due on Receipt</span></div>
65
+ <div style="font-size:15px;margin-left:130px;margin-top:0px;"><span>Due Date : </span><span>{{data.dueDate}}</span></div>
64
66
  </td>
65
67
  </tr>
66
68
  </table>
67
- </div>
68
- <div class="content-body"style="margin-top:0px;">
69
- <table style="border:none">
70
- <tr style="border:none">
71
- <td style="width:50%;border:1px solid grey;">
72
- <div style="font-size:20px;">Bill to</div>
73
- <div class="margin-top:10px;">Tango Eye IT Solutions</div>
74
- <div style="margin-top:10px;">4517 Washington Ave Manchester , Kentucky 397899</div>
75
- <div class="margin-top:10px;">Issued On:</div>
76
- <div class="margin-top:10px;">11 March , 2023</div>
69
+ <table>
70
+ <tr style="gap:60px;">
71
+ <td style="width:50%;border:1px solid grey;margin-left:20px;border-radius:12px !important; position:relative">
72
+ <div style="font-size:20px"><b>Bill to</b></div>
73
+ <div style="font-size:15px;margin-top:0px">{{data.clientName}}</div>
74
+ <div style="font-size:15px;margin-top:0px;line-height:2px;">{{data.address}}</div>
75
+ <div style="font-size:15px;margin-top:0px">Issued On: {{data.invoiceDate}}</div>
77
76
  </td>
78
- <td style="width:50%;border:1px solid grey;margin-left:30px;">
79
- <div style="font-size:20px;">Ship to</div>
80
- <div class="margin-top:10px;">Lenskart</div>
81
- <div style="margin-top:10px;">4517 Washington Ave Manchester , Kentucky 397899</div>
82
- <div class="margin-top:10px;">Issued On:</div>
83
- <div class="margin-top:10px;">11 March , 2023</div>
77
+ <td style="width:50%;border:1px solid grey;margin-left:20px;border-radius:50%">
78
+ <div style="font-size:20px"><b>Ship to</b></div>
79
+ <div style="font-size:15px;margin-top:0px">{{data.clientName}}</div>
80
+ <div style="font-size:15px;margin-top:0px;line-height:2px;">{{data.address}}</div>
81
+ <div style="font-size:15px;margin-top:0px">Issued On: {{data.invoiceDate}}</div>
84
82
  </td>
85
83
  </tr>
86
84
  </table>
87
- </div>
88
- <div class="content-table" style="margin-top:0px;">
89
- <div class="font-weight:100px">Invoice Details:</div>
85
+ <div class="font-weight:100px"><b>Invoice Details:</b></div>
90
86
  <table style="border:none">
91
87
  <tr style="border:none;height:40px;margin-top:10px;">
92
88
  <th style="width:300px;">Description</th>
@@ -94,78 +90,108 @@
94
90
  <th style="width:120px;">Stores</th>
95
91
  <th style="width:120px;">Total Amount</th>
96
92
  </tr>
97
- <tr style="border:none;margin-top:30px;">
98
- <td>Traffic</td>
99
- <td>$0</td>
100
- <td>1</td>
101
- <td>$0</td>
93
+ {{#each data.products}}
94
+ <tr style="border:none;margin-top:10px;">
95
+ <td>{{product.product}}</td>
96
+ <td>${{basePrice}}</td>
97
+ <td>{{count}}</td>
98
+ <td>${{price}}</td>
102
99
  </tr>
103
- <tr style="border:none;margin-top:30px;">
100
+ {{/each}}
101
+ <tr style="border:none;">
102
+ <td></td>
103
+ <td></td>
104
+ <td></td>
105
+ <td></td>
106
+ </tr>
107
+ <tr style="border:none;border-top: 5px solid #eaecf0;margin-top:10px;">
104
108
  <td>Sub Total</td>
105
109
  <td></td>
106
110
  <td></td>
107
- <td>$0</td>
111
+ <td>${{data.amount}}</td>
108
112
  </tr>
109
- <tr style="border:none;margin-top:30px;">
113
+ <tr style="border:none;margin-top:10px;">
110
114
  <td>IGST</td>
111
115
  <td></td>
112
116
  <td></td>
113
- <td>9%</td>
117
+ <td>0%</td>
114
118
  </tr>
115
- <tr style="border:none;margin-top:30px;">
119
+ <tr style="border:none;margin-top:10px;">
116
120
  <td>Total</td>
117
121
  <td></td>
118
122
  <td></td>
119
- <td>$0</td>
123
+ <td>${{data.total}}</td>
120
124
  </tr>
121
- <tr style="border:none;margin-top:30px;">
125
+ <tr style="border:none;margin-top:10px;">
122
126
  <td>Discount</td>
123
127
  <td></td>
124
128
  <td></td>
125
129
  <td>0%(-$0)</td>
126
130
  </tr>
127
- <tr style="border:none;margin-top:30px;">
131
+ <tr style="border:none;">
132
+ <td></td>
133
+ <td></td>
134
+ <td></td>
135
+ <td></td>
136
+ </tr>
137
+ <tr style="border:none;border-top: 5px solid #eaecf0;margin-top:10px;">
128
138
  <td>Final Value</td>
129
139
  <td></td>
130
140
  <td></td>
131
- <td>0%</td>
141
+ <td>${{data.total}}</td>
142
+ </tr>
143
+ </table>
144
+ <br/>
145
+ <br/>
146
+ <br/>
147
+ <br/>
148
+ <br/>
149
+ <br/>
150
+ <br/>
151
+ <br/>
152
+ <br/>
153
+ <table style="border:none">
154
+ <tr style="border:none;margin-top:10px;">
155
+ <td><b>Thanks for your business</b></td>
156
+ <td></td>
157
+ <td></td>
158
+ </tr>
159
+ <tr style="border:none;margin-top:10px;">
160
+ <td>Beneficiary Name</td>
161
+ <td></td>
162
+ <td></td>
163
+ </tr>
164
+ <tr style="border:none;margin-top:10px;">
165
+ <td><b>Tango IT Solutions India Private Limited</b></td>
166
+ <td></td>
167
+ <td></td>
168
+ </tr>
169
+ <tr style="border:none;margin-top:10px;">
170
+ <td></td>
171
+ <td></td>
172
+ <td></td>
173
+ </tr>
174
+ <tr style="border:none;margin-top:10px;">
175
+ <td>Account Number</td>
176
+ <td>IFSC Code</td>
177
+ <td>Payment Type</td>
178
+ </tr>
179
+ <tr style="border:none;margin-top:10px;">
180
+ <td><b>50200027441433</b></td>
181
+ <td><b>HDFC0000269</b></td>
182
+ <td><b>Virtual Ac</b></td>
183
+ </tr>
184
+ <tr style="border:none;margin-top:10px;">
185
+ <td>SWIFT Code</td>
186
+ <td>Branch</td>
187
+ <td></td>
188
+ </tr>
189
+ <tr style="border:none;margin-top:10px;">
190
+ <td><b>HDFCINBBCHE</b></td>
191
+ <td><b>RS Puram, Coimbatore</b></td>
192
+ <td></td>
132
193
  </tr>
133
194
  </table>
134
- </div>
135
- <div class="content-footer" style="margin-top:0px;">
136
- <div class="font-weight:100px">Thanks for your business</div>
137
- <div class="font-weight:100px">Beneficiary Name</div>
138
- <div class="font-weight:100px">Tango IT Solutions India Private Limited</div>
139
- <div class="font-weight:100px;margin-top:0px;">
140
- <table style="border:none;margin-top:0px;">
141
- <tr style="border:none">
142
- <td style="width:200px;">
143
- <div>Account Number </div>
144
- <div>50200027441433</div>
145
- </td>
146
- <td>
147
- <div>IFSC Code</div>
148
- <div>HDFC0000269</div>
149
- </td>
150
- <td>
151
- <div>Payment Type</div>
152
- <div>Virtual Acc</div>
153
- </td>
154
- </tr>
155
- <tr style="border:none">
156
- <td>
157
- <div>SWIFT Code</div>
158
- <div>HDFCINBBCHE</div>
159
- </td>
160
- <td>
161
- <div>Branch</div>
162
- <div>RS Puram, Coimbatore</div>
163
- </td>
164
- <td>
165
- </td>
166
- </tr>
167
- </table>
168
- </div>
169
195
  </div>
170
196
  </div>
171
197
  </body>
@@ -90,7 +90,7 @@ paymentSubscriptionRouter.post( '/admin/unsubscribeApproval', isAllowedSessionHa
90
90
  userType: [ 'tango' ], access: [
91
91
  { featureName: 'settings', name: 'paymentSubscriptions', permissions: [ 'isEdit' ] },
92
92
  ],
93
- } ), validate( validationDtos.validateTrialandUnsubscribeParams ), validateClient, paymentController.unsubscribeApproval );
93
+ } ), validate( validationDtos.validateTrialandUnsubscribeParams ), paymentController.unsubscribeApproval );
94
94
 
95
95
  paymentSubscriptionRouter.get( '/admin/getProductViewList', isAllowedSessionHandler, authorize( {
96
96
  userType: [ 'tango' ], access: [
@@ -154,8 +154,8 @@ paymentSubscriptionRouter.post( '/admin/getStoreProducts', isAllowedSessionHandl
154
154
 
155
155
  paymentSubscriptionRouter.get( '/trialRemaindList', paymentController.getRemindClients );
156
156
  paymentSubscriptionRouter.get( '/trialExpiredList', paymentController.getExpiredClients );
157
- paymentSubscriptionRouter.post( '/invoiceDownload', paymentController.invoiceDownload );
158
- paymentSubscriptionRouter.post( '/invoiceStatusUpdate/:invoiceId', paymentController.updateInvoiceStatus );
157
+ paymentSubscriptionRouter.post( '/invoiceDownload/:invoiceId', paymentController.invoiceDownload );
158
+ paymentSubscriptionRouter.post( '/invoiceStatusUpdate/:invoiceId', validate( validationDtos.invoiceUpdateParams ), paymentController.updateInvoiceStatus );
159
159
  paymentSubscriptionRouter.post( '/invoice/create', validateClient, paymentController.invoiceCreate );
160
-
161
-
160
+ paymentSubscriptionRouter.post( '/dailyPricing/insert', validate( validationDtos.dailyPricingParams ), paymentController.dailyPricingInsert );
161
+ paymentSubscriptionRouter.post( '/invoiceGenerate', validate( validationDtos.invoiceGenerateParams ), paymentController.invoiceGenerate );
@@ -0,0 +1,22 @@
1
+ import model from 'tango-api-schema';
2
+
3
+ export const aggregate = ( query = {} ) => {
4
+ return model.dailyPricingModel.aggregate( query );
5
+ };
6
+
7
+ export const find = ( query = {}, record = {} ) => {
8
+ return model.dailyPricingModel.find( query, record );
9
+ };
10
+
11
+ export const findOne = ( query = {}, record = {} ) => {
12
+ return model.dailyPricingModel.findOne( query, record );
13
+ };
14
+
15
+ export const create = ( record ) => {
16
+ return model.dailyPricingModel.create( record );
17
+ };
18
+
19
+ export const updateOne = ( query, record ) => {
20
+ return model.dailyPricingModel.updateOne( query, { $set: record } );
21
+ };
22
+
@@ -14,7 +14,6 @@ export const validateClient = async ( req, res, next ) => {
14
14
  if ( req?.params?.clientId ) {
15
15
  data = req.params.clientId;
16
16
  }
17
-
18
17
  let clientDetails = await clientPayment.findOne( { clientId: data, status: 'active' } );
19
18
  if ( !clientDetails ) {
20
19
  return res.sendError( 'no data found', 204 );