tango-app-api-client 3.0.45-dev → 3.0.47-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-client",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.47-dev",
|
|
4
4
|
"description": "client",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"mongodb": "^6.3.0",
|
|
26
26
|
"nodemon": "^3.0.3",
|
|
27
27
|
"swagger-ui-express": "^5.0.0",
|
|
28
|
-
"tango-api-schema": "^2.0.
|
|
29
|
-
"tango-app-api-middleware": "^1.0.
|
|
28
|
+
"tango-api-schema": "^2.0.105",
|
|
29
|
+
"tango-app-api-middleware": "^1.0.73-test",
|
|
30
30
|
"winston": "^3.11.0",
|
|
31
31
|
"winston-daily-rotate-file": "^5.0.0"
|
|
32
32
|
},
|
|
@@ -10,7 +10,7 @@ import { aggregateTickets } from '../service/tangoticket.service.js';
|
|
|
10
10
|
import { join } from 'path';
|
|
11
11
|
import { readFileSync } from 'fs';
|
|
12
12
|
import handlebars from 'handlebars';
|
|
13
|
-
import { countDocumentsGroup } from '../service/group.service.js';
|
|
13
|
+
import { countDocumentsGroup, createGroupModel, findOneGroup } from '../service/group.service.js';
|
|
14
14
|
import { deleteOneAuthentication } from '../service/authentication.service.js';
|
|
15
15
|
|
|
16
16
|
|
|
@@ -71,9 +71,33 @@ export async function create( req, res ) {
|
|
|
71
71
|
|
|
72
72
|
const insertClient = await insert( record );
|
|
73
73
|
|
|
74
|
+
const insertedClientRecord = await findOneClient( { clientId: record.clientId }, {} );
|
|
75
|
+
|
|
76
|
+
const defaultGroup = {
|
|
77
|
+
'groupName': 'All stores',
|
|
78
|
+
'description': 'Contains all the onboarded stores',
|
|
79
|
+
'storeList': [],
|
|
80
|
+
'clientId': insertedClientRecord.clientId,
|
|
81
|
+
'isDefault': true,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
await createGroupModel( defaultGroup );
|
|
85
|
+
|
|
86
|
+
const createdGroup = await findOneGroup( { clientId: insertedClientRecord.clientId, isDefault: true }, {} );
|
|
87
|
+
|
|
88
|
+
let oldGroup = {
|
|
89
|
+
'_id': createdGroup._id,
|
|
90
|
+
'client_id': createdGroup.clientId,
|
|
91
|
+
'groupName': createdGroup.groupName,
|
|
92
|
+
'description': createdGroup.description,
|
|
93
|
+
'storeList': createdGroup.storeList,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
await postApi( `${appConfig.url.oldapidomain}/oldGroupAdd`, [ oldGroup ] );
|
|
98
|
+
|
|
74
99
|
// For old dashboard insert
|
|
75
100
|
|
|
76
|
-
const insertedClientRecord = await findOneClient( { clientId: record.clientId }, {} );
|
|
77
101
|
|
|
78
102
|
const oldBrandInsertData = {
|
|
79
103
|
'_id': insertedClientRecord._id,
|
|
@@ -93,9 +117,7 @@ export async function create( req, res ) {
|
|
|
93
117
|
'birdsEye',
|
|
94
118
|
'tangoZone',
|
|
95
119
|
'tangoTraffic',
|
|
96
|
-
'
|
|
97
|
-
'footFallDirectory',
|
|
98
|
-
'tangoSOP',
|
|
120
|
+
'support',
|
|
99
121
|
],
|
|
100
122
|
'planType': insertedClientRecord.planDetails.subscriptionPeriod,
|
|
101
123
|
'storeCount': insertedClientRecord.planDetails.totalStores,
|
|
@@ -296,10 +318,12 @@ export async function getClients( req, res ) {
|
|
|
296
318
|
assignedType: { $eq: 'client' },
|
|
297
319
|
$expr: {
|
|
298
320
|
$cond: {
|
|
299
|
-
if: {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
321
|
+
if: {
|
|
322
|
+
$and: [
|
|
323
|
+
{ $eq: [ '$userType', 'tango' ] },
|
|
324
|
+
{ $eq: [ '$tangoUserType', 'csm' ] },
|
|
325
|
+
],
|
|
326
|
+
},
|
|
303
327
|
then: { $eq: [ '$isClientApproved', true ] },
|
|
304
328
|
else: true,
|
|
305
329
|
},
|
|
@@ -437,18 +461,30 @@ export async function detailedAllClientCount( req, res ) {
|
|
|
437
461
|
{
|
|
438
462
|
$project: {
|
|
439
463
|
activeClient: { $cond: [ { $eq: [ '$status', 'active' ] }, 1, 0 ] },
|
|
440
|
-
paidClient: {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
464
|
+
paidClient: {
|
|
465
|
+
$cond: [ {
|
|
466
|
+
$and: [
|
|
467
|
+
{ $eq: [ '$status', 'active' ] },
|
|
468
|
+
{ $in: [ '$planDetails.paymentStatus', [ 'paid', 'unbilled', 'due' ] ] },
|
|
469
|
+
],
|
|
470
|
+
}, 1, 0 ],
|
|
471
|
+
},
|
|
472
|
+
trialClient: {
|
|
473
|
+
$cond: [ {
|
|
474
|
+
$and: [
|
|
475
|
+
{ $eq: [ '$status', 'active' ] },
|
|
476
|
+
{ $eq: [ '$planDetails.paymentStatus', 'trial' ] },
|
|
477
|
+
],
|
|
478
|
+
}, 1, 0 ],
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
freeClient: {
|
|
482
|
+
$cond: [ {
|
|
483
|
+
$and: [
|
|
484
|
+
{ $eq: [ '$status', 'active' ] }, { $eq: [ '$planDetails.paymentStatus', 'free' ] },
|
|
485
|
+
],
|
|
486
|
+
}, 1, 0 ],
|
|
487
|
+
},
|
|
452
488
|
holdClient: { $cond: [ { $eq: [ '$status', 'hold' ] }, 1, 0 ] },
|
|
453
489
|
suspendClient: { $cond: [ { $eq: [ '$status', 'suspended' ] }, 1, 0 ] },
|
|
454
490
|
deactiveClient: { $cond: [ { $eq: [ '$status', 'deactive' ] }, 1, 0 ] },
|
|
@@ -1081,9 +1117,11 @@ export async function clientList( req, res ) {
|
|
|
1081
1117
|
userType: 'tango',
|
|
1082
1118
|
$expr: {
|
|
1083
1119
|
$cond: {
|
|
1084
|
-
if: {
|
|
1085
|
-
|
|
1086
|
-
|
|
1120
|
+
if: {
|
|
1121
|
+
$and: [
|
|
1122
|
+
{ $eq: [ '$tangoUserType', 'csm' ] },
|
|
1123
|
+
],
|
|
1124
|
+
},
|
|
1087
1125
|
then: { $eq: [ '$isClientApproved', true ] },
|
|
1088
1126
|
else: true,
|
|
1089
1127
|
},
|
|
@@ -1100,7 +1138,7 @@ export async function clientList( req, res ) {
|
|
|
1100
1138
|
];
|
|
1101
1139
|
const clientIdList = await aggregateUserAssignedStore( query );
|
|
1102
1140
|
logger.info( { message: clientIdList, value: 'clientIdList' } );
|
|
1103
|
-
if ( clientIdList.length ==0 ) {
|
|
1141
|
+
if ( clientIdList.length == 0 ) {
|
|
1104
1142
|
return res.sendError( 'No Data Found', 204 );
|
|
1105
1143
|
}
|
|
1106
1144
|
|
|
@@ -1369,7 +1407,7 @@ export async function clientList( req, res ) {
|
|
|
1369
1407
|
'client Id': chunk[i]?.clientId,
|
|
1370
1408
|
'Installation Stores Count': chunk[i]?.installedStores || 0,
|
|
1371
1409
|
'Onboarded Stores Count': chunk[i]?.totalStores,
|
|
1372
|
-
'Store Progress': chunk[i]?.installedStores? `${
|
|
1410
|
+
'Store Progress': chunk[i]?.installedStores ? `${( ( chunk[i]?.installedStores / chunk[i]?.totalStores ) * 100 ).toFixed( 0 )}%` : '0%',
|
|
1373
1411
|
'Active Store': chunk[i]?.activeStoreCount || 0,
|
|
1374
1412
|
'Active Camera': chunk[i]?.activeCameraCount && chunk[i]?.activeCameraCount != undefined ? chunk[i]?.activeCameraCount : 0,
|
|
1375
1413
|
'Pending Stores': chunk[i]?.pendingStores || 0,
|
|
@@ -1392,7 +1430,242 @@ export async function clientList( req, res ) {
|
|
|
1392
1430
|
return res.sendError( 'Internal Server Error', 500 );
|
|
1393
1431
|
}
|
|
1394
1432
|
}
|
|
1433
|
+
export async function clientListV1( req, res ) {
|
|
1434
|
+
try {
|
|
1435
|
+
const inputData = req.body;
|
|
1436
|
+
let clientQuery = [];
|
|
1437
|
+
logger.info( { message: req?.user?.role } );
|
|
1438
|
+
if ( req?.user?.role !== 'superadmin' ) {
|
|
1439
|
+
const query = [
|
|
1440
|
+
{
|
|
1441
|
+
$match: {
|
|
1442
|
+
userEmail: { $eq: req?.user?.email },
|
|
1443
|
+
userType: 'tango',
|
|
1444
|
+
$expr: {
|
|
1445
|
+
$cond: {
|
|
1446
|
+
if: {
|
|
1447
|
+
$and: [
|
|
1448
|
+
{ $eq: [ '$tangoUserType', 'csm' ] },
|
|
1449
|
+
],
|
|
1450
|
+
},
|
|
1451
|
+
then: { $eq: [ '$isClientApproved', true ] },
|
|
1452
|
+
else: true,
|
|
1453
|
+
},
|
|
1454
|
+
},
|
|
1455
|
+
|
|
1456
|
+
},
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
$group: {
|
|
1460
|
+
_id: null,
|
|
1461
|
+
clientList: { $push: '$assignedValue' },
|
|
1462
|
+
},
|
|
1463
|
+
},
|
|
1464
|
+
];
|
|
1465
|
+
const clientIdList = await aggregateUserAssignedStore( query );
|
|
1466
|
+
logger.info( { message: clientIdList, value: 'clientIdList' } );
|
|
1467
|
+
if ( clientIdList.length == 0 ) {
|
|
1468
|
+
return res.sendError( 'No Data Found', 204 );
|
|
1469
|
+
}
|
|
1470
|
+
clientQuery.push(
|
|
1471
|
+
{
|
|
1472
|
+
$match: {
|
|
1473
|
+
clientId: { $in: clientIdList[0].clientList },
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
clientQuery.push(
|
|
1479
|
+
{
|
|
1480
|
+
$project: {
|
|
1481
|
+
status: 1,
|
|
1482
|
+
clientName: 1,
|
|
1483
|
+
clientId: 1,
|
|
1484
|
+
subscriptionType: '$planDetails.subscriptionType',
|
|
1485
|
+
PaymentPlan: '$planDetails.paymentStatus',
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
);
|
|
1489
|
+
if ( inputData.searchValue && inputData.searchValue != '' ) {
|
|
1490
|
+
clientQuery.push( {
|
|
1491
|
+
$match: {
|
|
1492
|
+
$or: [
|
|
1493
|
+
{ clientId: { $regex: inputData.searchValue, $options: 'i' } },
|
|
1494
|
+
{ clientName: { $regex: inputData.searchValue, $options: 'i' } },
|
|
1495
|
+
{ subscriptionType: { $regex: inputData.searchValue, $options: 'i' } },
|
|
1496
|
+
{ status: { $regex: inputData.searchValue, $options: 'i' } },
|
|
1497
|
+
],
|
|
1498
|
+
},
|
|
1499
|
+
} );
|
|
1500
|
+
}
|
|
1501
|
+
clientQuery.push( {
|
|
1502
|
+
$lookup: {
|
|
1503
|
+
from: 'stores',
|
|
1504
|
+
let: { clientId: '$clientId' },
|
|
1505
|
+
pipeline: [
|
|
1506
|
+
{
|
|
1507
|
+
$match: {
|
|
1508
|
+
$expr: {
|
|
1509
|
+
$and: [
|
|
1510
|
+
{ $eq: [ '$clientId', '$$clientId' ] },
|
|
1511
|
+
],
|
|
1512
|
+
},
|
|
1513
|
+
},
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
$project: {
|
|
1517
|
+
edge: 1,
|
|
1518
|
+
status: 1,
|
|
1519
|
+
clientId: 1,
|
|
1520
|
+
storeId: 1,
|
|
1521
|
+
},
|
|
1522
|
+
},
|
|
1523
|
+
], as: 'stores',
|
|
1524
|
+
},
|
|
1525
|
+
},
|
|
1526
|
+
|
|
1527
|
+
{
|
|
1528
|
+
$unwind: { path: '$stores', preserveNullAndEmptyArrays: true },
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
$project: {
|
|
1532
|
+
clientId: 1,
|
|
1533
|
+
status: 1,
|
|
1534
|
+
clientName: 1,
|
|
1535
|
+
subscriptionType: 1,
|
|
1536
|
+
PaymentPlan: 1,
|
|
1537
|
+
installed: {
|
|
1538
|
+
$cond: [ { $and: [ { $eq: [ '$stores.edge.firstFile', true ] } ] }, 1, 0,
|
|
1539
|
+
],
|
|
1540
|
+
},
|
|
1541
|
+
activeStores: {
|
|
1542
|
+
$cond: [ { $and: [ { $eq: [ '$stores.status', 'active' ] }, { $eq: [ '$stores.edge.firstFile', true ] } ] }, 1, 0,
|
|
1543
|
+
],
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
$group: {
|
|
1549
|
+
_id: '$clientId',
|
|
1550
|
+
clientId: { $first: '$clientId' },
|
|
1551
|
+
onboardedStores: { $sum: 1 },
|
|
1552
|
+
installedStore: { $sum: '$installed' },
|
|
1553
|
+
activeStores: { $sum: '$activeStores' },
|
|
1554
|
+
ProcessingStatus: { $first: '$status' },
|
|
1555
|
+
clientName: { $first: '$clientName' },
|
|
1556
|
+
subscriptionPlan: { $first: '$subscriptionType' },
|
|
1557
|
+
PaymentPlan: { $first: '$PaymentPlan' },
|
|
1558
|
+
},
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
$lookup: {
|
|
1562
|
+
from: 'tangoTicket',
|
|
1563
|
+
let: { clientId: '$clientId' },
|
|
1564
|
+
pipeline: [
|
|
1565
|
+
{
|
|
1566
|
+
$match: {
|
|
1567
|
+
$expr: {
|
|
1568
|
+
$and: [
|
|
1569
|
+
{ $eq: [ '$issueType', 'installation' ] },
|
|
1570
|
+
{ $eq: [ '$basicDetails.clientId', '$$clientId' ] },
|
|
1571
|
+
],
|
|
1572
|
+
},
|
|
1573
|
+
},
|
|
1395
1574
|
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
$project: {
|
|
1578
|
+
ticketDetails: 1,
|
|
1579
|
+
status: 1,
|
|
1580
|
+
basicDetails: 1,
|
|
1581
|
+
},
|
|
1582
|
+
},
|
|
1583
|
+
], as: 'ticket',
|
|
1584
|
+
},
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
$unwind: { path: '$ticket', preserveNullAndEmptyArrays: true },
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
$project: {
|
|
1591
|
+
clientId: 1,
|
|
1592
|
+
ProcessingStatus: 1,
|
|
1593
|
+
onboardedStores: 1,
|
|
1594
|
+
clientName: 1,
|
|
1595
|
+
PaymentPlan: 1,
|
|
1596
|
+
subscriptionPlan: 1,
|
|
1597
|
+
installedStore: 1,
|
|
1598
|
+
activeStores: 1,
|
|
1599
|
+
installedPending: {
|
|
1600
|
+
$cond: [ { $and: [ { $ne: [ '$ticket.status', 'closed' ] } ] }, 1, 0 ],
|
|
1601
|
+
},
|
|
1602
|
+
installedFailed: {
|
|
1603
|
+
$cond: [ { $and: [ { $ne: [ '$ticket.status', 'closed' ] }, { $eq: [ '$ticket.ticketDetails.issueStatus', 'identified' ] } ] }, 1, 0 ],
|
|
1604
|
+
},
|
|
1605
|
+
},
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
$group: {
|
|
1609
|
+
_id: '$clientId',
|
|
1610
|
+
clientId: { $first: '$clientId' },
|
|
1611
|
+
onboardedStores: { $first: '$onboardedStores' },
|
|
1612
|
+
installedStore: { $first: '$installedStore' },
|
|
1613
|
+
activeStores: { $first: '$activeStores' },
|
|
1614
|
+
ProcessingStatus: { $first: '$ProcessingStatus' },
|
|
1615
|
+
clientName: { $first: '$clientName' },
|
|
1616
|
+
subscriptionPlan: { $first: '$subscriptionPlan' },
|
|
1617
|
+
PaymentPlan: { $first: '$PaymentPlan' },
|
|
1618
|
+
installedPending: { $sum: '$installedPending' },
|
|
1619
|
+
installedFailed: { $sum: '$installedFailed' },
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
);
|
|
1623
|
+
|
|
1624
|
+
const clientCount = await aggregateClient( clientQuery );
|
|
1625
|
+
if ( clientCount.length == 0 ) {
|
|
1626
|
+
return res.sendError( 'No Data Found', 204 );
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
if ( inputData.sortColumName ) {
|
|
1630
|
+
clientQuery.push( {
|
|
1631
|
+
$sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
1632
|
+
},
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
if ( req.body.limit && req.body.offset && !req.body.isExport ) {
|
|
1636
|
+
clientQuery.push(
|
|
1637
|
+
{ $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
1638
|
+
{ $limit: Number( req.body.limit ) },
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
const clientList = await aggregateClient( clientQuery );
|
|
1642
|
+
|
|
1643
|
+
if ( inputData.isExport ) {
|
|
1644
|
+
const exportResult = [];
|
|
1645
|
+
for ( let client of clientList ) {
|
|
1646
|
+
exportResult.push( {
|
|
1647
|
+
'client Name': client.clientName,
|
|
1648
|
+
'client Id': client.clientId,
|
|
1649
|
+
'Installation Stores Count': client.installedStore || 0,
|
|
1650
|
+
'Onboarded Stores Count': client.onboardedStores,
|
|
1651
|
+
'Store Progress': client.onboardedStores ? `${Math.round( ( client.onboardedStores / client.installedStore ) * 100 )}%` : '0%',
|
|
1652
|
+
'Active Store': client.activeStores || 0,
|
|
1653
|
+
'installedPending Stores': client.installedPending || 0,
|
|
1654
|
+
'installedFailed Stores': client.installedFailed || 0,
|
|
1655
|
+
'PaymentPlan': client.PaymentPlan,
|
|
1656
|
+
'subscriptionPlan': client.subscriptionPlan,
|
|
1657
|
+
'ProcessingStatus': client.ProcessingStatus,
|
|
1658
|
+
} );
|
|
1659
|
+
}
|
|
1660
|
+
await download( exportResult, res );
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
return res.sendSuccess( { result: clientList, count: clientCount.length } );
|
|
1664
|
+
} catch ( error ) {
|
|
1665
|
+
logger.error( { error: error, function: 'clientList' } );
|
|
1666
|
+
return res.sendError( 'Internal Server Error', 500 );
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1396
1669
|
export async function getOpsUsers( req, res ) {
|
|
1397
1670
|
try {
|
|
1398
1671
|
const users = await OpsUsersGet();
|
|
@@ -5,7 +5,7 @@ import { auditConfiguration, changeStatus, clientCsmAssignAction, clientDetails,
|
|
|
5
5
|
import { authorize, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
6
6
|
import { clientListValid, detailedClientCountValid } from '../dtos/client.dtos.js';
|
|
7
7
|
import { isclientIdExists, isclientNameExists } from '../validations/client.validations.js';
|
|
8
|
-
import { detailedAllClientCount, detailedClientCount, clientList } from '../controllers/client.controllers.js';
|
|
8
|
+
import { detailedAllClientCount, detailedClientCount, clientList, clientListV1 } from '../controllers/client.controllers.js';
|
|
9
9
|
|
|
10
10
|
export const clientRouter = express.Router();
|
|
11
11
|
|
|
@@ -83,7 +83,9 @@ clientRouter.get( '/detailed-all-client-count', isAllowedSessionHandler,
|
|
|
83
83
|
clientRouter.post( '/client-list', isAllowedSessionHandler,
|
|
84
84
|
authorize( { userType: [ 'tango' ], access: [ { featureName: 'manage', name: 'brands', permissions: [ 'isView' ] } ] } ),
|
|
85
85
|
validate( clientListValid ), clientList );
|
|
86
|
-
|
|
86
|
+
clientRouter.post( '/client-list_v1', isAllowedSessionHandler,
|
|
87
|
+
authorize( { userType: [ 'tango' ], access: [ { featureName: 'manage', name: 'brands', permissions: [ 'isView' ] } ] } ),
|
|
88
|
+
validate( clientListValid ), clientListV1 );
|
|
87
89
|
clientRouter.get( '/detailed-client-count', isAllowedSessionHandler,
|
|
88
90
|
authorize( { userType: [ 'tango', 'client' ] } ),
|
|
89
91
|
validate( detailedClientCountValid ), isclientIdExists, detailedClientCount );
|
|
@@ -3,3 +3,11 @@ import groupModel from 'tango-api-schema/schema/group.model.js';
|
|
|
3
3
|
export function countDocumentsGroup( query ) {
|
|
4
4
|
return groupModel.countDocuments( query );
|
|
5
5
|
}
|
|
6
|
+
|
|
7
|
+
export async function createGroupModel( query ={}, field={} ) {
|
|
8
|
+
return await groupModel.create( query, field );
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function findOneGroup( query ={}, field={} ) {
|
|
12
|
+
return await groupModel.findOne( query, field );
|
|
13
|
+
};
|