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
|
@@ -1,274 +1,147 @@
|
|
|
1
|
-
import { aggregatebilling } from '../services/billing.service.js';
|
|
2
1
|
import * as invoiceService from '../services/invoice.service.js';
|
|
3
2
|
import * as dailyPricingService from '../services/dailyPrice.service.js';
|
|
4
3
|
import * as clientService from '../services/clientPayment.services.js';
|
|
5
|
-
|
|
4
|
+
import * as billingService from '../services/billing.service.js';
|
|
6
5
|
import dayjs from 'dayjs';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import { logger } from 'tango-app-api-middleware';
|
|
6
|
+
import { logger, checkFileExist, signedUrl, download, sendEmailWithSES } from 'tango-app-api-middleware';
|
|
10
7
|
import Handlebars from 'handlebars';
|
|
11
8
|
import fs from 'fs';
|
|
12
9
|
import path from 'path';
|
|
13
10
|
import htmlpdf from 'html-pdf-node';
|
|
11
|
+
import * as basepricingService from '../services/basePrice.service.js';
|
|
12
|
+
import * as paymentAccountService from '../services/paymentAccount.service.js';
|
|
13
|
+
|
|
14
14
|
export async function createInvoice( req, res ) {
|
|
15
15
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for ( let group of invoiceGroup ) {
|
|
25
|
-
let Finacialyear = getCurrentFinancialYear();
|
|
26
|
-
let previousinvoice = await invoiceService.findandsort( {}, {}, { invoiceIndex: -1 } );
|
|
27
|
-
let invoiceNo = '00001';
|
|
28
|
-
if ( previousinvoice && previousinvoice.length > 0 ) {
|
|
29
|
-
invoiceNo = Number( previousinvoice[0].invoiceIndex ) + 1;
|
|
30
|
-
invoiceNo = invoiceNo.toString().padStart( 5, '0' );
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let address = group.addressLineOne + group.addressLineTwo + group.city + ',' + group.state + ',' + group.country + ' -' + group.pinCode;
|
|
34
|
-
|
|
16
|
+
let invoiceGroupList = [];
|
|
17
|
+
if ( req.body.allClient ) {
|
|
18
|
+
if ( req.body.clientList && req.body.clientList.length > 0 ) {
|
|
19
|
+
req.body.clientList = req.body.clientList;
|
|
20
|
+
} else {
|
|
21
|
+
let clients = await clientService.find( { status: 'active' } );
|
|
22
|
+
req.body.clientList = clients.map( ( client ) => client.clientId );
|
|
23
|
+
}
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
let
|
|
25
|
+
for ( let client of req.body.clientList ) {
|
|
26
|
+
let invoiceGroup = await billingService.aggregatebilling( [
|
|
38
27
|
{
|
|
39
28
|
$match: {
|
|
40
29
|
clientId: client,
|
|
41
30
|
},
|
|
42
31
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
$lookup: {
|
|
94
|
-
from: 'basepricings',
|
|
95
|
-
let: { clientId: client },
|
|
96
|
-
pipeline: [
|
|
97
|
-
{
|
|
98
|
-
$match: {
|
|
99
|
-
$expr: {
|
|
100
|
-
$eq: [ '$clientId', '$$clientId' ],
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
$project: {
|
|
106
|
-
standard: 1,
|
|
107
|
-
step: 1,
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
as: 'basepricing',
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
$unwind: { path: '$basepricing', preserveNullAndEmptyArrays: true },
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
$project: {
|
|
119
|
-
productName: 1,
|
|
120
|
-
workingdays: 1,
|
|
121
|
-
storeCount: 1,
|
|
122
|
-
standard: {
|
|
123
|
-
$filter: {
|
|
124
|
-
input: '$basepricing.standard',
|
|
125
|
-
as: 'standard',
|
|
126
|
-
cond: { $eq: [ '$$standard.productName', '$productName' ] },
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
step: '$basepricing.step',
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
$unwind: { path: '$standard', preserveNullAndEmptyArrays: true },
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
$project: {
|
|
137
|
-
productName: 1,
|
|
138
|
-
workingdays: 1,
|
|
139
|
-
period: {
|
|
140
|
-
$cond: {
|
|
141
|
-
if: { $lt: [ '$workingdays', currentMonthDays ] },
|
|
142
|
-
then: 'prorate',
|
|
143
|
-
else: 'fullmonth',
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
storeCount: 1,
|
|
147
|
-
standardPrice: '$standard.negotiatePrice',
|
|
148
|
-
runningCost: {
|
|
149
|
-
$round: [
|
|
150
|
-
{
|
|
151
|
-
$multiply: [
|
|
152
|
-
{ $divide: [ '$standard.negotiatePrice', currentMonthDays ] },
|
|
153
|
-
'$workingdays',
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
2,
|
|
157
|
-
],
|
|
158
|
-
},
|
|
159
|
-
perstorecost: {
|
|
160
|
-
$cond: {
|
|
161
|
-
if: { $eq: [ '$workingdays', currentMonthDays ] },
|
|
162
|
-
then: {
|
|
163
|
-
$multiply: [ '$standard.negotiatePrice', '$storeCount' ] },
|
|
164
|
-
else: {
|
|
165
|
-
$round: [
|
|
166
|
-
{
|
|
167
|
-
$multiply: [
|
|
168
|
-
{
|
|
169
|
-
$multiply: [
|
|
170
|
-
{ $divide: [ '$standard.negotiatePrice', currentMonthDays ] },
|
|
171
|
-
'$workingdays',
|
|
172
|
-
],
|
|
173
|
-
},
|
|
174
|
-
'$storeCount',
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
2,
|
|
178
|
-
] },
|
|
179
|
-
},
|
|
180
|
-
},
|
|
32
|
+
] );
|
|
33
|
+
for ( let invGrp of invoiceGroup ) {
|
|
34
|
+
invoiceGroupList.push( invGrp );
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if ( req.body.invoiceId ) {
|
|
39
|
+
let findInvoice = await invoiceService.findOne( { invoice: req.body.invoiceId } );
|
|
40
|
+
let invoiceGroup = await billingService.findOne( { _id: findInvoice.groupId } );
|
|
41
|
+
invoiceGroupList.push( invoiceGroup );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for ( let group of invoiceGroupList ) {
|
|
45
|
+
let Finacialyear = getCurrentFinancialYear();
|
|
46
|
+
let previousinvoice = await invoiceService.findandsort( {}, {}, { invoiceIndex: -1 } );
|
|
47
|
+
let invoiceNo = '00001';
|
|
48
|
+
if ( previousinvoice && previousinvoice.length > 0 ) {
|
|
49
|
+
invoiceNo = Number( previousinvoice[0].invoiceIndex ) + 1;
|
|
50
|
+
invoiceNo = invoiceNo.toString().padStart( 5, '0' );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let address = group.addressLineOne + group.addressLineTwo + group.city + ',' + group.state + ',' + group.country + ' -' + group.pinCode;
|
|
54
|
+
let getClient = await clientService.findOne( { clientId: group.clientId, status: 'active' } );
|
|
55
|
+
let products;
|
|
56
|
+
|
|
57
|
+
if ( getClient.priceType === 'standard' ) {
|
|
58
|
+
products = await standardPrice( group );
|
|
59
|
+
} else {
|
|
60
|
+
products = await stepPrice( group );
|
|
61
|
+
}
|
|
62
|
+
let amount = products.reduce( ( sum, product ) => sum + product.amount, 0 );
|
|
63
|
+
let taxList = [];
|
|
64
|
+
let totalAmount = 0;
|
|
65
|
+
if ( group.gst && group.gst.slice( 0, 2 ) == '33' ) {
|
|
66
|
+
let taxAmount = ( amount * 18 ) / 100;
|
|
67
|
+
totalAmount = Math.round( amount + taxAmount );
|
|
68
|
+
taxList.push(
|
|
69
|
+
{
|
|
70
|
+
'currency': '₹',
|
|
71
|
+
'type': 'CGST',
|
|
72
|
+
'value': 9,
|
|
73
|
+
'taxAmount': ( ( amount * 9 ) / 100 ).toFixed( 2 ),
|
|
74
|
+
}, {
|
|
75
|
+
'currency': '₹',
|
|
76
|
+
'type': 'SGST',
|
|
77
|
+
'value': 9,
|
|
78
|
+
'taxAmount': ( ( amount * 9 ) / 100 ).toFixed( 2 ),
|
|
181
79
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
let taxAmount = ( amount * 18 ) / 100;
|
|
83
|
+
totalAmount = Math.round( amount + taxAmount );
|
|
84
|
+
taxList.push(
|
|
85
|
+
{
|
|
86
|
+
'currency': '₹',
|
|
87
|
+
'type': 'IGST',
|
|
88
|
+
'value': 18,
|
|
89
|
+
'taxAmount': ( taxAmount ).toFixed( 2 ),
|
|
191
90
|
},
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let totalStoreCount = await dailyPricingService.aggregate( [
|
|
95
|
+
{
|
|
96
|
+
$match: {
|
|
97
|
+
clientId: group.clientId,
|
|
192
98
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
then: 'Product category/section analytics',
|
|
206
|
-
else: 'Customer Footfall Analytics',
|
|
207
|
-
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
$sort: { dateISO: -1 },
|
|
102
|
+
},
|
|
103
|
+
{ $limit: 1 },
|
|
104
|
+
{
|
|
105
|
+
$project: {
|
|
106
|
+
stores: {
|
|
107
|
+
$filter: {
|
|
108
|
+
input: '$stores',
|
|
109
|
+
as: 'item',
|
|
110
|
+
cond: { $gt: [ '$$item.daysDifference', 0 ] },
|
|
208
111
|
},
|
|
209
|
-
HsnNumber: '998314',
|
|
210
|
-
month: { $literal: dayjs().format( 'MMM YYYY' ) },
|
|
211
112
|
},
|
|
212
113
|
},
|
|
213
|
-
|
|
114
|
+
},
|
|
214
115
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
'type': 'IGST',
|
|
241
|
-
'value': 18,
|
|
242
|
-
'taxAmount': ( taxAmount ).toFixed( 2 ),
|
|
243
|
-
},
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
let data = {
|
|
247
|
-
invoice: `INV-${Finacialyear}-${invoiceNo}`,
|
|
248
|
-
products: products,
|
|
249
|
-
status: 'pending',
|
|
250
|
-
amount: amount,
|
|
251
|
-
invoiceIndex: invoiceNo,
|
|
252
|
-
tax: taxList,
|
|
253
|
-
companyName: group.registeredCompanyName,
|
|
254
|
-
companyAddress: address,
|
|
255
|
-
PlaceOfSupply: group.placeOfSupply,
|
|
256
|
-
GSTNumber: group.gst,
|
|
257
|
-
totalAmount: totalAmount,
|
|
258
|
-
clientId: client,
|
|
259
|
-
paymentMethod: '',
|
|
260
|
-
billingDate: new Date(),
|
|
261
|
-
stores: group.stores.length,
|
|
262
|
-
};
|
|
263
|
-
let getClient = await clientService.findOne( { clientId: client, status: 'active' } );
|
|
264
|
-
let invoiceExists = await invoiceService.findOne( { monthOfbilling: req.body.monthOfbilling, clientId: getClient.clientId } );
|
|
265
|
-
if ( invoiceExists ) {
|
|
266
|
-
logger.info( `invoice already exist for the month${req.body.monthOfbilling} for ${getClient.clientId}` );
|
|
267
|
-
} else {
|
|
268
|
-
await invoiceService.create( data );
|
|
269
|
-
}
|
|
116
|
+
] );
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
let data = {
|
|
120
|
+
groupName: group.groupName,
|
|
121
|
+
groupId: group._id,
|
|
122
|
+
invoice: `INV-${Finacialyear}-${invoiceNo}`,
|
|
123
|
+
products: products,
|
|
124
|
+
status: 'pending',
|
|
125
|
+
amount: Math.round( amount ),
|
|
126
|
+
invoiceIndex: invoiceNo,
|
|
127
|
+
tax: taxList,
|
|
128
|
+
companyName: group.registeredCompanyName,
|
|
129
|
+
companyAddress: address,
|
|
130
|
+
PlaceOfSupply: group.placeOfSupply,
|
|
131
|
+
GSTNumber: group.gst,
|
|
132
|
+
totalAmount: Math.round( totalAmount ),
|
|
133
|
+
clientId: group.clientId,
|
|
134
|
+
paymentMethod: '',
|
|
135
|
+
billingDate: new Date(),
|
|
136
|
+
stores: totalStoreCount.length?totalStoreCount[0].stores.length:0,
|
|
137
|
+
currency: group.currency ? group.currency : 'inr',
|
|
138
|
+
};
|
|
139
|
+
if ( req.body.invoiceId ) {
|
|
140
|
+
await invoiceService.deleteRecord( { invoice: req.body.invoiceId } );
|
|
270
141
|
}
|
|
142
|
+
await invoiceService.create( data );
|
|
271
143
|
}
|
|
144
|
+
|
|
272
145
|
res.sendSuccess( 'Invoice Created SuccessFully' );
|
|
273
146
|
} catch ( error ) {
|
|
274
147
|
logger.error( { error: error, function: 'createInvoice' } );
|
|
@@ -294,7 +167,7 @@ export async function invoiceDownload( req, res ) {
|
|
|
294
167
|
if ( invoiceInfo ) {
|
|
295
168
|
let clientDetails = await clientService.findOne( { clientId: invoiceInfo.clientId } );
|
|
296
169
|
invoiceInfo.products.forEach( ( item, index ) => {
|
|
297
|
-
item.index = index+1;
|
|
170
|
+
item.index = index + 1;
|
|
298
171
|
let [ firstWord, secondWord ] = item.productName.replace( /([a-z])([A-Z])/g, '$1 $2' ).split( ' ' );
|
|
299
172
|
firstWord = firstWord.charAt( 0 ).toUpperCase() + firstWord.slice( 1 );
|
|
300
173
|
item.productName = firstWord + ' ' + secondWord;
|
|
@@ -330,8 +203,12 @@ export async function invoiceDownload( req, res ) {
|
|
|
330
203
|
totalAmount: invoiceInfo.totalAmount.toLocaleString( 'en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ),
|
|
331
204
|
invoiceDate,
|
|
332
205
|
dueDate,
|
|
206
|
+
discountPercentage: invoiceInfo.discountPercentage ? invoiceInfo.discountPercentage : 0,
|
|
207
|
+
discountAmount: invoiceInfo.discountAmount ? invoiceInfo.discountAmount : 0,
|
|
208
|
+
logo: `${JSON.parse( process.env.URL ).apiDomain}/logo.png`,
|
|
333
209
|
};
|
|
334
210
|
const currentMonthDays = dayjs().daysInMonth();
|
|
211
|
+
let getgroup = await billingService.findOne( { _id: invoiceInfo.groupId } );
|
|
335
212
|
let annuxureData = await dailyPricingService.aggregate( [
|
|
336
213
|
{
|
|
337
214
|
$match: {
|
|
@@ -342,6 +219,17 @@ export async function invoiceDownload( req, res ) {
|
|
|
342
219
|
$sort: { dateISO: -1 },
|
|
343
220
|
},
|
|
344
221
|
{ $limit: 1 },
|
|
222
|
+
{
|
|
223
|
+
$project: {
|
|
224
|
+
stores: {
|
|
225
|
+
$filter: {
|
|
226
|
+
input: '$stores',
|
|
227
|
+
as: 'item',
|
|
228
|
+
cond: { $in: [ '$$item.storeId', getgroup.stores ] },
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
345
233
|
{
|
|
346
234
|
$unwind: {
|
|
347
235
|
path: '$stores',
|
|
@@ -370,6 +258,10 @@ export async function invoiceDownload( req, res ) {
|
|
|
370
258
|
workingdays: -1,
|
|
371
259
|
},
|
|
372
260
|
},
|
|
261
|
+
{
|
|
262
|
+
$match: { workingdays: { $gt: 0 } },
|
|
263
|
+
},
|
|
264
|
+
|
|
373
265
|
{
|
|
374
266
|
$lookup: {
|
|
375
267
|
from: 'basepricings',
|
|
@@ -453,7 +345,8 @@ export async function invoiceDownload( req, res ) {
|
|
|
453
345
|
],
|
|
454
346
|
);
|
|
455
347
|
invoiceData.annuxureData = annuxureData;
|
|
456
|
-
|
|
348
|
+
let virtualAccount = await paymentAccountService.findOneAccount( { clientId: invoiceInfo.clientId } );
|
|
349
|
+
invoiceData.virtualAccount = virtualAccount;
|
|
457
350
|
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicePdf.hbs', 'utf8' );
|
|
458
351
|
const template = Handlebars.compile( templateHtml );
|
|
459
352
|
const html = template( { ...invoiceData } );
|
|
@@ -476,25 +369,18 @@ export async function invoiceDownload( req, res ) {
|
|
|
476
369
|
htmlpdf.generatePdf( file, options ).then( async function( pdfBuffer ) {
|
|
477
370
|
if ( req.body.sendInvoice ) {
|
|
478
371
|
let mailSubject = `Invoice for ${monthName} - Tango/${clientDetails.clientName}`;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
</div>
|
|
483
|
-
<div style="margin-top:10px">
|
|
484
|
-
Best Regards,
|
|
485
|
-
</div>
|
|
486
|
-
<div style="margin-top:5px">
|
|
487
|
-
Tango Finance
|
|
488
|
-
</div>`;
|
|
489
|
-
|
|
372
|
+
const templateHtml = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/invoicepaymentemail.hbs', 'utf8' );
|
|
373
|
+
const template = Handlebars.compile( templateHtml );
|
|
374
|
+
const mailbody = template( { ...invoiceData } );
|
|
490
375
|
let filename = `${clientDetails.clientName}-${invoiceData.invoice}-${monthName}.pdf`;
|
|
491
376
|
let attachments = {
|
|
492
377
|
filename: `${filename}`,
|
|
493
378
|
content: pdfBuffer,
|
|
494
379
|
contentType: 'application/pdf', // e.g., 'application/pdf'
|
|
495
380
|
};
|
|
496
|
-
|
|
497
|
-
|
|
381
|
+
let billingGroup = await billingService.findOne( { _id: invoiceInfo.groupId } );
|
|
382
|
+
|
|
383
|
+
const result = await sendEmailWithSES( billingGroup.generateInvoiceTo, mailSubject, mailbody, attachments, 'no-reply@tangotech.ai' );
|
|
498
384
|
if ( result ) {
|
|
499
385
|
await invoiceService.updateOne( { _id: req.params.invoiceId }, { status: req.body.status } );
|
|
500
386
|
return res.sendSuccess( result );
|
|
@@ -524,3 +410,664 @@ function inWords( num ) {
|
|
|
524
410
|
|
|
525
411
|
return str.toLowerCase().split( ' ' ).map( ( word ) => word.charAt( 0 ).toUpperCase() + word.slice( 1 ) ).join( ' ' );
|
|
526
412
|
}
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
async function standardPrice( group ) {
|
|
416
|
+
const currentMonthDays = dayjs().daysInMonth();
|
|
417
|
+
let products = await dailyPricingService.aggregate( [
|
|
418
|
+
{
|
|
419
|
+
$match: {
|
|
420
|
+
clientId: group.clientId,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
$sort: { dateISO: -1 },
|
|
425
|
+
},
|
|
426
|
+
{ $limit: 1 },
|
|
427
|
+
{
|
|
428
|
+
$project: {
|
|
429
|
+
stores: {
|
|
430
|
+
$filter: {
|
|
431
|
+
input: '$stores',
|
|
432
|
+
as: 'item',
|
|
433
|
+
cond: { $in: [ '$$item.storeId', group.stores ] },
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
$unwind: {
|
|
440
|
+
path: '$stores',
|
|
441
|
+
preserveNullAndEmptyArrays: false,
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
$unwind: {
|
|
446
|
+
path: '$stores.products',
|
|
447
|
+
preserveNullAndEmptyArrays: false,
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
$project: {
|
|
452
|
+
productName: '$stores.products.productName',
|
|
453
|
+
storeId: '$stores.storeId',
|
|
454
|
+
workingDays: '$stores.products.workingdays',
|
|
455
|
+
storeStatus: '$stores.status',
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
$match: {
|
|
460
|
+
workingDays: { $gt: 0 },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
$project: {
|
|
465
|
+
productName: 1,
|
|
466
|
+
storeId: 1,
|
|
467
|
+
prorate: { $literal: group.proRata },
|
|
468
|
+
workingDays: 1,
|
|
469
|
+
storeStatus: 1,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
$project: {
|
|
474
|
+
productName: 1,
|
|
475
|
+
storeId: 1,
|
|
476
|
+
workingDays: {
|
|
477
|
+
$cond: { if: { $and: [ { $gte: [ '$workingDays', 15 ] }, { $eq: [ '$storeStatus', 'active' ] }, { $eq: [ '$prorate', 'before15' ] } ] }, then: currentMonthDays, else: '$workingDays' },
|
|
478
|
+
},
|
|
479
|
+
storeStatus: 1,
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
$group: {
|
|
484
|
+
_id: {
|
|
485
|
+
productName: '$productName',
|
|
486
|
+
storeId: '$storeId',
|
|
487
|
+
},
|
|
488
|
+
workingdays: { $first: '$workingDays' },
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
$group: {
|
|
493
|
+
_id: {
|
|
494
|
+
productName: '$_id.productName',
|
|
495
|
+
workingdays: '$workingdays',
|
|
496
|
+
},
|
|
497
|
+
storeCount: { $sum: 1 },
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
$project: {
|
|
502
|
+
_id: 0,
|
|
503
|
+
productName: '$_id.productName',
|
|
504
|
+
workingdays: '$_id.workingdays',
|
|
505
|
+
storeCount: '$storeCount',
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
$lookup: {
|
|
510
|
+
from: 'basepricings',
|
|
511
|
+
let: { clientId: group.clientId },
|
|
512
|
+
pipeline: [
|
|
513
|
+
{
|
|
514
|
+
$match: {
|
|
515
|
+
$expr: {
|
|
516
|
+
$eq: [ '$clientId', '$$clientId' ],
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
$project: {
|
|
522
|
+
standard: 1,
|
|
523
|
+
step: 1,
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
as: 'basepricing',
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
$unwind: { path: '$basepricing', preserveNullAndEmptyArrays: true },
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
$project: {
|
|
535
|
+
productName: 1,
|
|
536
|
+
workingdays: 1,
|
|
537
|
+
storeCount: 1,
|
|
538
|
+
standard: {
|
|
539
|
+
$filter: {
|
|
540
|
+
input: '$basepricing.standard',
|
|
541
|
+
as: 'standard',
|
|
542
|
+
cond: { $eq: [ '$$standard.productName', '$productName' ] },
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
step: '$basepricing.step',
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
$unwind: { path: '$standard', preserveNullAndEmptyArrays: true },
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
$project: {
|
|
553
|
+
productName: 1,
|
|
554
|
+
workingdays: 1,
|
|
555
|
+
period: {
|
|
556
|
+
$cond: {
|
|
557
|
+
if: { $lt: [ '$workingdays', currentMonthDays ] },
|
|
558
|
+
then: 'prorate',
|
|
559
|
+
else: 'fullmonth',
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
storeCount: 1,
|
|
563
|
+
standardPrice: '$standard.negotiatePrice',
|
|
564
|
+
runningCost: {
|
|
565
|
+
$round: [
|
|
566
|
+
{
|
|
567
|
+
$multiply: [
|
|
568
|
+
{ $divide: [ '$standard.negotiatePrice', currentMonthDays ] },
|
|
569
|
+
'$workingdays',
|
|
570
|
+
],
|
|
571
|
+
},
|
|
572
|
+
2,
|
|
573
|
+
],
|
|
574
|
+
},
|
|
575
|
+
perstorecost: {
|
|
576
|
+
$cond: {
|
|
577
|
+
if: { $eq: [ '$workingdays', currentMonthDays ] },
|
|
578
|
+
then: {
|
|
579
|
+
$multiply: [ '$standard.negotiatePrice', '$storeCount' ],
|
|
580
|
+
},
|
|
581
|
+
else: {
|
|
582
|
+
$round: [
|
|
583
|
+
{
|
|
584
|
+
$multiply: [
|
|
585
|
+
{
|
|
586
|
+
$multiply: [
|
|
587
|
+
{ $divide: [ '$standard.negotiatePrice', currentMonthDays ] },
|
|
588
|
+
'$workingdays',
|
|
589
|
+
],
|
|
590
|
+
},
|
|
591
|
+
'$storeCount',
|
|
592
|
+
],
|
|
593
|
+
},
|
|
594
|
+
2,
|
|
595
|
+
],
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
$group: {
|
|
603
|
+
_id: {
|
|
604
|
+
productName: '$productName',
|
|
605
|
+
period: '$period',
|
|
606
|
+
},
|
|
607
|
+
storeCount: { $sum: '$storeCount' },
|
|
608
|
+
amount: { $sum: '$perstorecost' },
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
$project: {
|
|
613
|
+
_id: 0,
|
|
614
|
+
productName: '$_id.productName',
|
|
615
|
+
period: '$_id.period',
|
|
616
|
+
price: {
|
|
617
|
+
$round: [ { $divide: [ '$amount', '$storeCount' ] }, 2 ],
|
|
618
|
+
},
|
|
619
|
+
storeCount: 1,
|
|
620
|
+
amount: 1,
|
|
621
|
+
description: {
|
|
622
|
+
$cond: {
|
|
623
|
+
if: { $eq: [ '$_id.productName', 'tangoZone' ] },
|
|
624
|
+
then: 'Product category/section analytics',
|
|
625
|
+
else: 'Customer Footfall Analytics',
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
HsnNumber: '998314',
|
|
629
|
+
month: { $literal: dayjs().format( 'MMM YYYY' ) },
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
] );
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
return products;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
async function stepPrice( group ) {
|
|
640
|
+
const currentMonthDays = dayjs().daysInMonth();
|
|
641
|
+
let products = await dailyPricingService.aggregate( [
|
|
642
|
+
{
|
|
643
|
+
$match: {
|
|
644
|
+
clientId: group.clientId,
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
$sort: { dateISO: -1 },
|
|
649
|
+
},
|
|
650
|
+
{ $limit: 1 },
|
|
651
|
+
{
|
|
652
|
+
$project: {
|
|
653
|
+
stores: {
|
|
654
|
+
$filter: {
|
|
655
|
+
input: '$stores',
|
|
656
|
+
as: 'item',
|
|
657
|
+
cond: { $in: [ '$$item.storeId', group.stores ] },
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
$unwind: {
|
|
664
|
+
path: '$stores',
|
|
665
|
+
preserveNullAndEmptyArrays: false,
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
$unwind: {
|
|
670
|
+
path: '$stores.products',
|
|
671
|
+
preserveNullAndEmptyArrays: false,
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
$project: {
|
|
676
|
+
productName: '$stores.products.productName',
|
|
677
|
+
storeId: '$stores.storeId',
|
|
678
|
+
workingDays: '$stores.products.workingdays',
|
|
679
|
+
storeStatus: '$stores.status',
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
$match: {
|
|
684
|
+
workingDays: { $gt: 0 },
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
$project: {
|
|
689
|
+
productName: 1,
|
|
690
|
+
storeId: 1,
|
|
691
|
+
prorate: { $literal: group.proRata },
|
|
692
|
+
workingDays: 1,
|
|
693
|
+
storeStatus: 1,
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
$project: {
|
|
698
|
+
productName: 1,
|
|
699
|
+
storeId: 1,
|
|
700
|
+
workingDays: {
|
|
701
|
+
$cond: { if: { $and: [ { $gte: [ '$workingDays', 15 ] }, { $eq: [ '$storeStatus', 'active' ] }, { $eq: [ '$prorate', 'before15' ] } ] }, then: currentMonthDays, else: '$workingDays' },
|
|
702
|
+
},
|
|
703
|
+
storeStatus: 1,
|
|
704
|
+
},
|
|
705
|
+
}, {
|
|
706
|
+
$group: {
|
|
707
|
+
_id: {
|
|
708
|
+
productName: '$productName',
|
|
709
|
+
storeId: '$storeId',
|
|
710
|
+
},
|
|
711
|
+
workingdays: { $first: '$workingDays' },
|
|
712
|
+
},
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
$group: {
|
|
716
|
+
_id: {
|
|
717
|
+
productName: '$_id.productName',
|
|
718
|
+
workingdays: '$workingdays',
|
|
719
|
+
},
|
|
720
|
+
storeCount: { $sum: 1 },
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
$project: {
|
|
725
|
+
_id: 0,
|
|
726
|
+
productName: '$_id.productName',
|
|
727
|
+
workingdays: '$_id.workingdays',
|
|
728
|
+
storeCount: '$storeCount',
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
$sort: {
|
|
733
|
+
workingdays: -1,
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
] );
|
|
739
|
+
let stepPrice = await basepricingService.findOne( { clientId: group.clientId } );
|
|
740
|
+
let data = products;
|
|
741
|
+
let pricing = stepPrice.step;
|
|
742
|
+
|
|
743
|
+
const applyPricing = ( data, pricing ) => {
|
|
744
|
+
let totalcount = 0;
|
|
745
|
+
return data.map( ( item ) => {
|
|
746
|
+
totalcount = totalcount + item.storeCount;
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
let applicablePricing = pricing.find( ( price ) => {
|
|
750
|
+
const [ minRange, maxRange ] = price.storeRange.split( '-' ).map( Number );
|
|
751
|
+
return totalcount >= minRange && totalcount <= maxRange;
|
|
752
|
+
} );
|
|
753
|
+
if ( item.workingdays === currentMonthDays ) {
|
|
754
|
+
item.period = 'fullMonth';
|
|
755
|
+
item.runningCost = item.storeCount * applicablePricing.negotiatePrice;
|
|
756
|
+
} else {
|
|
757
|
+
item.period = 'proRate';
|
|
758
|
+
item.runningCost = ( item.storeCount * ( applicablePricing.negotiatePrice / currentMonthDays ) * item.workingdays ).toFixed( 2 );
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return {
|
|
762
|
+
...item,
|
|
763
|
+
negotiatePrice: applicablePricing.negotiatePrice,
|
|
764
|
+
perstorecost: ( ( applicablePricing.negotiatePrice / currentMonthDays ) * item.workingdays ).toFixed( 2 ),
|
|
765
|
+
};
|
|
766
|
+
} );
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
const result = applyPricing( data, pricing );
|
|
770
|
+
const groupedData = result.reduce( ( acc, item ) => {
|
|
771
|
+
const { productName, period, runningCost } = item;
|
|
772
|
+
const key = `${productName}_${period}`;
|
|
773
|
+
if ( !acc[key] ) {
|
|
774
|
+
acc[key] = {
|
|
775
|
+
productName,
|
|
776
|
+
period,
|
|
777
|
+
totalRunningCost: 0,
|
|
778
|
+
count: 0,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
acc[key].totalRunningCost += parseFloat( runningCost );
|
|
783
|
+
acc[key].count += item.storeCount;
|
|
784
|
+
|
|
785
|
+
return acc;
|
|
786
|
+
}, {} );
|
|
787
|
+
|
|
788
|
+
// Calculating average running cost
|
|
789
|
+
const finalresult = Object.values( groupedData ).map( ( group ) => {
|
|
790
|
+
let description = '';
|
|
791
|
+
if ( group.productName === 'tangoTraffic' ) {
|
|
792
|
+
description = 'Customer Footfall Analytics';
|
|
793
|
+
} else if ( group.productName === 'tangoZone' ) {
|
|
794
|
+
description = 'Product category/section analytics';
|
|
795
|
+
} else {
|
|
796
|
+
description = '';
|
|
797
|
+
}
|
|
798
|
+
return {
|
|
799
|
+
productName: group.productName,
|
|
800
|
+
period: group.period,
|
|
801
|
+
storeCount: group.count,
|
|
802
|
+
description: description,
|
|
803
|
+
HsnNumber: '998314',
|
|
804
|
+
amount: group.totalRunningCost,
|
|
805
|
+
month: dayjs().format( 'MMM YYYY' ),
|
|
806
|
+
price: ( group.totalRunningCost / group.count ).toFixed( 2 ),
|
|
807
|
+
};
|
|
808
|
+
} );
|
|
809
|
+
|
|
810
|
+
return finalresult;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
export async function clientInvoiceList( req, res ) {
|
|
815
|
+
try {
|
|
816
|
+
let findClients = await clientService.find( { clientId: { $in: req.body.clientId }, status: 'active' }, { clientId: 1 } );
|
|
817
|
+
findClients = findClients.map( ( a ) => {
|
|
818
|
+
return a.clientId;
|
|
819
|
+
} );
|
|
820
|
+
let query = [ {
|
|
821
|
+
$match: {
|
|
822
|
+
clientId: { $in: findClients },
|
|
823
|
+
},
|
|
824
|
+
}, {
|
|
825
|
+
$lookup: {
|
|
826
|
+
from: 'clients',
|
|
827
|
+
let: { clientId: '$clientId' },
|
|
828
|
+
pipeline: [
|
|
829
|
+
{
|
|
830
|
+
$match: {
|
|
831
|
+
$expr: {
|
|
832
|
+
$and: [
|
|
833
|
+
{ $eq: [ '$clientId', '$$clientId' ] },
|
|
834
|
+
],
|
|
835
|
+
},
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
$project: {
|
|
840
|
+
clientName: 1,
|
|
841
|
+
logo: '$profileDetails.logo',
|
|
842
|
+
currencyType: '$paymentInvoice.currencyType',
|
|
843
|
+
},
|
|
844
|
+
},
|
|
845
|
+
],
|
|
846
|
+
as: 'clientDetails',
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
$unwind: { path: '$clientDetails', preserveNullAndEmptyArrays: true },
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
$project: {
|
|
854
|
+
clientName: '$clientDetails.clientName',
|
|
855
|
+
logo: '$clientDetails.logo',
|
|
856
|
+
currencyType: '$currency',
|
|
857
|
+
invoice: 1,
|
|
858
|
+
stores: 1,
|
|
859
|
+
totalAmount: 1,
|
|
860
|
+
groupName: 1,
|
|
861
|
+
status: 1,
|
|
862
|
+
paymentStatus: 1,
|
|
863
|
+
clientId: 1,
|
|
864
|
+
billingDate: 1,
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
];
|
|
868
|
+
if ( req.body.searchValue && req.body.searchValue != '' ) {
|
|
869
|
+
query.push( {
|
|
870
|
+
$match: {
|
|
871
|
+
$or: [
|
|
872
|
+
{ groupName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
873
|
+
{ clientName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
874
|
+
{ paymentStatus: { $regex: req.body.searchValue, $options: 'i' } },
|
|
875
|
+
],
|
|
876
|
+
},
|
|
877
|
+
} );
|
|
878
|
+
}
|
|
879
|
+
if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
880
|
+
query.push( {
|
|
881
|
+
$sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
882
|
+
} );
|
|
883
|
+
}
|
|
884
|
+
let count = await invoiceService.aggregate( query );
|
|
885
|
+
if ( count.length == 0 ) {
|
|
886
|
+
return res.sendError( 'No data', 204 );
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if ( req.body.export ) {
|
|
890
|
+
const exportdata = [];
|
|
891
|
+
count.forEach( ( element ) => {
|
|
892
|
+
exportdata.push( {
|
|
893
|
+
'Client': element.clientName,
|
|
894
|
+
'Invoice #': element.invoice,
|
|
895
|
+
'Billing date': dayjs( element.billingDate ).format( 'DD MMM, YYYY' ),
|
|
896
|
+
'Group Name': element.groupName,
|
|
897
|
+
'Amount': element.totalAmount,
|
|
898
|
+
'Stores': element.stores,
|
|
899
|
+
'Payment Status': element.paymentStatus,
|
|
900
|
+
'Approval Status': element.status,
|
|
901
|
+
|
|
902
|
+
} );
|
|
903
|
+
} );
|
|
904
|
+
await download( exportdata, res );
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
908
|
+
query.push(
|
|
909
|
+
{ $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
910
|
+
{ $limit: Number( req.body.limit ) },
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
let invoiceList = await invoiceService.aggregate( query );
|
|
914
|
+
const bucket = JSON.parse( process.env.BUCKET );
|
|
915
|
+
for ( let client of invoiceList ) {
|
|
916
|
+
if ( client?.logo && client?.logo != '' ) {
|
|
917
|
+
const isLogoExist = await checkFileExist( { Bucket: bucket.assets, Key: `${client.clientId}/logo/${client?.logo}` } );
|
|
918
|
+
if ( isLogoExist ) {
|
|
919
|
+
client.logo = await signedUrl( { Bucket: bucket.assets, file_path: `${client.clientId}/logo/${client?.logo}` } );
|
|
920
|
+
} else {
|
|
921
|
+
client.logo = '';
|
|
922
|
+
}
|
|
923
|
+
} else {
|
|
924
|
+
client.logo = '';
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
res.sendSuccess( { count: count.length, data: invoiceList } );
|
|
928
|
+
} catch ( error ) {
|
|
929
|
+
logger.error( { error: error, function: 'clientInvoiceList' } );
|
|
930
|
+
return res.sendError( error, 500 );
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
export async function creditTransactionlist( req, res ) {
|
|
936
|
+
try {
|
|
937
|
+
let findClients = await clientService.find( { clientId: { $in: req.body.clientId }, status: 'active' }, { clientId: 1 } );
|
|
938
|
+
findClients = findClients.map( ( a ) => {
|
|
939
|
+
return a.clientId;
|
|
940
|
+
} );
|
|
941
|
+
let query = [ {
|
|
942
|
+
$match: {
|
|
943
|
+
clientId: { $in: findClients },
|
|
944
|
+
},
|
|
945
|
+
}, {
|
|
946
|
+
$lookup: {
|
|
947
|
+
from: 'clients',
|
|
948
|
+
let: { clientId: '$clientId' },
|
|
949
|
+
pipeline: [
|
|
950
|
+
{
|
|
951
|
+
$match: {
|
|
952
|
+
$expr: {
|
|
953
|
+
$and: [
|
|
954
|
+
{ $eq: [ '$clientId', '$$clientId' ] },
|
|
955
|
+
],
|
|
956
|
+
},
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
$project: {
|
|
961
|
+
clientName: 1,
|
|
962
|
+
logo: '$profileDetails.logo',
|
|
963
|
+
currencyType: '$paymentInvoice.currencyType',
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
],
|
|
967
|
+
as: 'clientDetails',
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
$unwind: { path: '$clientDetails', preserveNullAndEmptyArrays: true },
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
$project: {
|
|
975
|
+
clientName: '$clientDetails.clientName',
|
|
976
|
+
logo: '$clientDetails.logo',
|
|
977
|
+
currencyType: '$clientDetails.currencyType',
|
|
978
|
+
clientId: 1,
|
|
979
|
+
credit: 1,
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
];
|
|
983
|
+
if ( req.body.searchValue && req.body.searchValue != '' ) {
|
|
984
|
+
query.push( {
|
|
985
|
+
$match: {
|
|
986
|
+
$or: [
|
|
987
|
+
{ clientName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
988
|
+
{ paymentStatus: { $regex: req.body.searchValue, $options: 'i' } },
|
|
989
|
+
],
|
|
990
|
+
},
|
|
991
|
+
} );
|
|
992
|
+
}
|
|
993
|
+
if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
994
|
+
query.push( {
|
|
995
|
+
$sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
996
|
+
} );
|
|
997
|
+
}
|
|
998
|
+
let count = await paymentAccountService.aggregate( query );
|
|
999
|
+
if ( req.body.export ) {
|
|
1000
|
+
const exportdata = [];
|
|
1001
|
+
count.forEach( ( element ) => {
|
|
1002
|
+
exportdata.push( {
|
|
1003
|
+
'Client': element.clientName,
|
|
1004
|
+
'Balance Credits': element.credit,
|
|
1005
|
+
} );
|
|
1006
|
+
} );
|
|
1007
|
+
await download( exportdata, res );
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
1011
|
+
query.push(
|
|
1012
|
+
{ $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
1013
|
+
{ $limit: Number( req.body.limit ) },
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
let TransactionsList = await paymentAccountService.aggregate( query );
|
|
1017
|
+
const bucket = JSON.parse( process.env.BUCKET );
|
|
1018
|
+
for ( let client of TransactionsList ) {
|
|
1019
|
+
if ( client?.logo && client?.logo != '' ) {
|
|
1020
|
+
const isLogoExist = await checkFileExist( { Bucket: bucket.assets, Key: `${client.clientId}/logo/${client?.logo}` } );
|
|
1021
|
+
if ( isLogoExist ) {
|
|
1022
|
+
client.logo = await signedUrl( { Bucket: bucket.assets, file_path: `${client.clientId}/logo/${client?.logo}` } );
|
|
1023
|
+
} else {
|
|
1024
|
+
client.logo = '';
|
|
1025
|
+
}
|
|
1026
|
+
} else {
|
|
1027
|
+
client.logo = '';
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
res.sendSuccess( { count: count.length, data: TransactionsList } );
|
|
1033
|
+
} catch ( error ) {
|
|
1034
|
+
logger.error( { error: error, function: 'creditTransactionlist' } );
|
|
1035
|
+
return res.sendError( error, 500 );
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
export async function pendingInvoices( req, res ) {
|
|
1040
|
+
try {
|
|
1041
|
+
let invoicelist = await invoiceService.find( { clientId: req.body.clientId, paymentStatus: 'unpaid' } );
|
|
1042
|
+
res.sendSuccess( invoicelist );
|
|
1043
|
+
} catch ( error ) {
|
|
1044
|
+
logger.error( { error: error, function: 'pendingInvoices' } );
|
|
1045
|
+
return res.sendError( error, 500 );
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
export async function applyDiscount( req, res ) {
|
|
1050
|
+
try {
|
|
1051
|
+
let invoice = await invoiceService.findOne( { invoice: req.body.invoice } );
|
|
1052
|
+
if ( invoice ) {
|
|
1053
|
+
invoice.discountAmount = ( ( invoice.amount * req.body.discount ) / 100 ).toFixed( 2 );
|
|
1054
|
+
invoice.amount = invoice.amount - invoice.discountAmount;
|
|
1055
|
+
invoice.discountPercentage = req.body.discount;
|
|
1056
|
+
if ( invoice.currency === 'inr' ) {
|
|
1057
|
+
if ( invoice.tax.length ) {
|
|
1058
|
+
for ( let i = 0; i < invoice.tax.length; i++ ) {
|
|
1059
|
+
invoice.tax[i].taxAmount = ( ( invoice.amount * invoice.tax[i].value ) / 100 ).toFixed( 2 );
|
|
1060
|
+
invoice.totalAmount = ( Number( invoice.amount ) + Number( invoice.tax[i].taxAmount ) ).toFixed( 2 );
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
await invoiceService.updateOne( { invoice: req.body.invoice }, invoice );
|
|
1067
|
+
}
|
|
1068
|
+
res.sendSuccess( 'updated Successfully' );
|
|
1069
|
+
} catch ( error ) {
|
|
1070
|
+
logger.error( { error: error, function: 'applyDiscount' } );
|
|
1071
|
+
return res.sendError( error, 500 );
|
|
1072
|
+
}
|
|
1073
|
+
}
|