tango-app-api-payment-subscription 3.1.10 → 3.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
 
2
2
 
3
3
  import { paymentSubscriptionRouter } from './src/routes/paymentSubscription.routes.js';
4
+ import { invoiceRouter } from './src/routes/invoice.routes.js';
5
+ import { billingRouter } from './src/routes/billing.routes.js';
6
+
7
+
4
8
  import { paymentDocs } from './src/docs/payment.docs.js';
5
9
 
6
- export { paymentSubscriptionRouter, paymentDocs };
10
+ export { paymentSubscriptionRouter, paymentDocs, invoiceRouter, billingRouter };
7
11
 
8
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-payment-subscription",
3
- "version": "3.1.10",
3
+ "version": "3.1.12",
4
4
  "description": "paymentSubscription",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "mongodb": "^6.4.0",
26
26
  "nodemon": "^3.1.0",
27
27
  "swagger-ui-express": "^5.0.0",
28
- "tango-api-schema": "^2.0.115",
28
+ "tango-api-schema": "^2.0.131",
29
29
  "tango-app-api-middleware": "^3.1.19",
30
30
  "winston": "^3.12.0",
31
31
  "winston-daily-rotate-file": "^5.0.0"
@@ -0,0 +1,445 @@
1
+ import { download, logger } from 'tango-app-api-middleware';
2
+ import { aggregate } from '../services/store.service.js';
3
+ import { aggregatebilling, create, deleteOne, find, findOne, updateMany, updateOne } from '../services/billing.service.js';
4
+ import mongoose from 'mongoose';
5
+
6
+
7
+ export const subscribedStoreList = async ( req, res ) => {
8
+ try {
9
+ const matchStage = {
10
+ $match: {
11
+ clientId: req.body.clientId,
12
+ product: {
13
+ $ne: [],
14
+ },
15
+ },
16
+ };
17
+
18
+ if ( req.body.searchValue ) {
19
+ matchStage.$match.$or = [
20
+ {
21
+ storeName: {
22
+ $regex: req.body.searchValue,
23
+ $options: 'i',
24
+ },
25
+ },
26
+ {
27
+ storeId: {
28
+ $regex: req.body.searchValue,
29
+ $options: 'i',
30
+ },
31
+ },
32
+ ];
33
+ }
34
+
35
+ if ( req.body.country || req.body.state || req.body.city ) {
36
+ matchStage.$match.$and = [];
37
+
38
+ if ( req.body.country ) {
39
+ matchStage.$match.$and.push( {
40
+ 'storeProfile.country': { $in: req.body.country },
41
+ } );
42
+ }
43
+
44
+ if ( req.body.state ) {
45
+ matchStage.$match.$and.push( {
46
+ 'storeProfile.state': { $in: req.body.state },
47
+ } );
48
+ }
49
+
50
+ if ( req.body.city ) {
51
+ matchStage.$match.$and.push( {
52
+ 'storeProfile.city': { $in: req.body.city },
53
+ } );
54
+ }
55
+
56
+ if ( matchStage.$match.$and.length === 0 ) {
57
+ delete matchStage.$match.$and;
58
+ }
59
+ }
60
+
61
+
62
+ const pipeline = [
63
+ matchStage,
64
+ {
65
+ $project: {
66
+ 'storeName': 1,
67
+ 'storeId': 1,
68
+ 'product': 1,
69
+ 'storeProfile.country': 1,
70
+ 'storeProfile.state': 1,
71
+ 'storeProfile.city': 1,
72
+ },
73
+ },
74
+ ];
75
+
76
+ if ( req.body?.sortColumn && req.body?.sortBy ) {
77
+ pipeline.push(
78
+ {
79
+ $addFields: {
80
+ sortField: {
81
+ $toLower: `$${req.body.sortColumn}`,
82
+ },
83
+ },
84
+ },
85
+ {
86
+ $sort: {
87
+ [req.body.sortColumn]: req.body.sortBy,
88
+ },
89
+ },
90
+ );
91
+ }
92
+
93
+ pipeline.push(
94
+ {
95
+ $project: {
96
+ sortField: 0,
97
+ },
98
+ },
99
+
100
+ );
101
+
102
+ const facetStage = {
103
+ $facet: {
104
+ data: [
105
+ {
106
+ $skip: ( ( req.body.offset - 1 ) * req.body.limit ),
107
+ },
108
+ {
109
+ $limit: ( req.body.limit ),
110
+ },
111
+ {
112
+ $project: {
113
+ storeProfile: 0,
114
+ _id: 0,
115
+ },
116
+ },
117
+ ],
118
+ pageInfo: [
119
+ {
120
+ $count: 'count',
121
+ },
122
+ ],
123
+ },
124
+ };
125
+
126
+ if ( req.body.getFilters ) {
127
+ facetStage.$facet['countries'] = [
128
+ {
129
+ $match: {
130
+ $and: [
131
+ {
132
+ 'storeProfile.country': {
133
+ $exists: true,
134
+ },
135
+ },
136
+ {
137
+ 'storeProfile.country': {
138
+ $ne: null,
139
+ },
140
+ },
141
+ {
142
+ 'storeProfile.country': {
143
+ $ne: '',
144
+ },
145
+ },
146
+ ],
147
+ },
148
+ },
149
+ {
150
+ $group: {
151
+ _id: '$storeProfile.country',
152
+ },
153
+ },
154
+ ],
155
+ facetStage.$facet['states'] = [
156
+ {
157
+ $match: {
158
+ $and: [
159
+ {
160
+ 'storeProfile.state': {
161
+ $exists: true,
162
+ },
163
+ },
164
+ {
165
+ 'storeProfile.state': {
166
+ $ne: null,
167
+ },
168
+ },
169
+ {
170
+ 'storeProfile.state': {
171
+ $ne: '',
172
+ },
173
+ },
174
+ ],
175
+ },
176
+ },
177
+ {
178
+ $group: {
179
+ _id: '$storeProfile.state',
180
+ },
181
+ },
182
+ ],
183
+ facetStage.$facet['cities'] = [
184
+ {
185
+ $match: {
186
+ $and: [
187
+ {
188
+ 'storeProfile.city': {
189
+ $exists: true,
190
+ },
191
+ },
192
+ {
193
+ 'storeProfile.city': {
194
+ $ne: null,
195
+ },
196
+ },
197
+ {
198
+ 'storeProfile.city': {
199
+ $ne: '',
200
+ },
201
+ },
202
+ ],
203
+ },
204
+ },
205
+ {
206
+ $group: {
207
+ _id: '$storeProfile.city',
208
+ },
209
+ },
210
+ ];
211
+ }
212
+
213
+ pipeline.push( facetStage );
214
+
215
+ pipeline.push( {
216
+ $unwind: {
217
+ path: '$pageInfo',
218
+ },
219
+ } );
220
+
221
+ const storeList = await aggregate( pipeline );
222
+
223
+ if ( !storeList[0] ) {
224
+ return res.sendError( 'No data found', 204 );
225
+ }
226
+
227
+ return res.sendSuccess( storeList[0] );
228
+ } catch ( error ) {
229
+ logger.error( { error: error, function: 'subscribedStoreList' } );
230
+ return res.sendError( error, 500 );
231
+ }
232
+ };
233
+
234
+ export const getAllBillingGroups = async ( req, res ) => {
235
+ try {
236
+ const billingGroups = await find( { clientId: req.query.clientId } );
237
+
238
+ return res.sendSuccess( billingGroups );
239
+ } catch ( error ) {
240
+ logger.error( { error: error, function: 'getBillingGroups' } );
241
+ return res.sendError( error, 500 );
242
+ }
243
+ };
244
+
245
+
246
+ export const createBillingGroup = async ( req, res ) => {
247
+ try {
248
+ if ( req.body?.stores?.length ) {
249
+ await updateMany( { clientId: req.body.clientId }, { $pull: { stores: { $in: req.body.stores } } } );
250
+ }
251
+
252
+ const createGroup = await create( req.body );
253
+
254
+ return res.sendSuccess( createGroup );
255
+ } catch ( error ) {
256
+ logger.error( { error: error, function: 'createBillingGroup' } );
257
+ return res.sendError( error, 500 );
258
+ }
259
+ };
260
+
261
+ export const updateBillingGroup = async ( req, res ) => {
262
+ try {
263
+ const previousStores = await findOne( { _id: new mongoose.Types.ObjectId( req.body._id ) }, { stores: 1, isPrimary: 1 } );
264
+
265
+ if ( req.body?.stores && !req.body?.isPrimary ) {
266
+ const removedStores = previousStores?.stores?.filter( ( val ) => !req.body?.stores.includes( val ) );
267
+
268
+ if ( removedStores.length ) {
269
+ await updateOne( { _id: new mongoose.Types.ObjectId( req.body._id ) }, { $pull: { stores: { $in: removedStores } } } );
270
+ await updateOne( { clientId: req.body.clientId, isPrimary: true }, { $push: { stores: { $each: removedStores } } } );
271
+ }
272
+
273
+ const addedStores = req.body?.stores?.filter( ( val ) => !previousStores?.stores.includes( val ) );
274
+
275
+ if ( addedStores.length ) {
276
+ await updateMany( { clientId: req.body.clientId, _id: { $ne: new mongoose.Types.ObjectId( req.body._id ) } }, { $pull: { stores: { $in: req.body.stores } } } );
277
+ await updateOne( { _id: new mongoose.Types.ObjectId( req.body._id ) }, { $push: { stores: { $each: addedStores } } } );
278
+ }
279
+ }
280
+
281
+ delete req.body?.stores;
282
+
283
+ const update = await updateOne( { _id: new mongoose.Types.ObjectId( req.body._id ) }, req.body );
284
+
285
+ return res.sendSuccess( update );
286
+ } catch ( error ) {
287
+ logger.error( { error: error, function: 'updateBillingGroup' } );
288
+ return res.sendError( error, 500 );
289
+ }
290
+ };
291
+
292
+ export const deleteBillingGroup = async ( req, res ) => {
293
+ try {
294
+ const previousGroup = await findOne( { _id: new mongoose.Types.ObjectId( req.query._id ) }, { stores: 1, clientId: 1 } );
295
+
296
+ if ( previousGroup?.stores?.length ) {
297
+ await updateOne( { clientId: previousGroup.clientId, isPrimary: true }, { $push: { stores: { $each: previousGroup?.stores } } } );
298
+ }
299
+
300
+ const deletedGroup = await deleteOne( { _id: new mongoose.Types.ObjectId( req.query._id ), isPrimary: false } );
301
+
302
+ return res.sendSuccess( deletedGroup );
303
+ } catch ( error ) {
304
+ logger.error( { error: error, function: 'deleteBillingGroup' } );
305
+ return res.sendError( error, 500 );
306
+ }
307
+ };
308
+
309
+ export const getBillingGroups = async ( req, res ) => {
310
+ try {
311
+ const matchStage = {
312
+ $match: {
313
+ clientId: req.body.clientId,
314
+ },
315
+ };
316
+
317
+ if ( req.body.searchValue ) {
318
+ matchStage.$match.$or = [
319
+ {
320
+ groupName: {
321
+ $regex: req.body.searchValue,
322
+ $options: 'i',
323
+ },
324
+ },
325
+ ];
326
+ }
327
+
328
+
329
+ const pipeline = [
330
+ matchStage,
331
+ {
332
+ $project: {
333
+ _id: 1,
334
+ groupName: 1,
335
+ groupTag: 1,
336
+ registeredCompanyName: 1,
337
+ gst: 1,
338
+ addressLineOne: 1,
339
+ addressLineTwo: 1,
340
+ city: 1,
341
+ state: 1,
342
+ country: 1,
343
+ pinCode: 1,
344
+ placeOfSupply: 1,
345
+ po: 1,
346
+ stores: { $size: '$stores' },
347
+ proRata: 1,
348
+ paymentCategory: 1,
349
+ currency: 1,
350
+ isInstallationOneTime: 1,
351
+ installationFee: 1,
352
+ paymentCycle: 1,
353
+ paymentTerm: 1,
354
+ generateInvoiceTo: 1,
355
+ attachAnnexure: 1,
356
+ isPrimary: 1,
357
+ },
358
+ },
359
+ ];
360
+
361
+ if ( req.body?.sortColumn && req.body?.sortBy ) {
362
+ pipeline.push(
363
+ {
364
+ $addFields: {
365
+ sortField: {
366
+ $toLower: `$${req.body.sortColumn}`,
367
+ },
368
+ },
369
+ },
370
+ {
371
+ $sort: {
372
+ [req.body.sortColumn]: req.body.sortBy,
373
+ },
374
+ },
375
+ );
376
+ }
377
+
378
+ pipeline.push(
379
+ {
380
+ $project: {
381
+ sortField: 0,
382
+ },
383
+ },
384
+
385
+ );
386
+
387
+ const facetStage = {
388
+ $facet: {
389
+ data: [
390
+ {
391
+ $skip: ( ( req.body.offset - 1 ) * req.body.limit ),
392
+ },
393
+ {
394
+ $limit: ( req.body.limit ),
395
+ },
396
+ ],
397
+ pageInfo: [
398
+ {
399
+ $count: 'count',
400
+ },
401
+ ],
402
+ },
403
+ };
404
+
405
+ if ( req.body?.isExport ) {
406
+ facetStage.$facet.data = [];
407
+ }
408
+
409
+ pipeline.push( facetStage );
410
+
411
+ pipeline.push( {
412
+ $unwind: {
413
+ path: '$pageInfo',
414
+ },
415
+ } );
416
+
417
+ const groupList = await aggregatebilling( pipeline );
418
+
419
+ if ( !groupList[0] ) {
420
+ return res.sendError( 'No data found', 204 );
421
+ }
422
+
423
+ if ( req.body.isExport ) {
424
+ const exportResult = [];
425
+ for ( let group of groupList[0].data ) {
426
+ exportResult.push( {
427
+ 'Group name': group.groupName||'',
428
+ 'Stores': group.stores||'',
429
+ 'GST number': group.gst||'',
430
+ 'Place of supply': group.placeOfSupply || '',
431
+ 'Tag': group.groupTag||'',
432
+ } );
433
+ }
434
+ await download( exportResult, res );
435
+ return;
436
+ }
437
+
438
+ return res.sendSuccess( groupList[0] );
439
+ } catch ( error ) {
440
+ logger.error( { error: error, function: 'subscribedStoreList' } );
441
+ return res.sendError( error, 500 );
442
+ }
443
+ };
444
+
445
+