tango-app-api-store-zone 3.3.1-beta.19 → 3.3.1-beta.20
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 +5 -2
- package/src/controllers/zoneTagging.controller.js +1452 -28
- package/src/dtos/validation.dtos.js +131 -0
- package/src/routes/zoneTagging.routes.js +15 -0
- package/src/services/customzonegrouping.service.js +55 -0
- package/src/services/customzonetag.service.js +56 -0
- package/src/services/tagging.service.js +3 -0
- package/src/validations/zone.validations.js +127 -0
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { logger } from 'tango-app-api-middleware';
|
|
2
2
|
import * as cameraService from '../services/camera.service.js';
|
|
3
3
|
import * as taggingService from '../services/tagging.service.js';
|
|
4
|
+
import * as customZoneTagService from '../services/customzonetag.service.js';
|
|
5
|
+
import * as customzonegrouping from '../services/customzonegrouping.service.js';
|
|
4
6
|
import * as storeService from '../services/store.service.js';
|
|
5
7
|
import * as clientService from '../services/client.service.js';
|
|
6
8
|
import * as externalService from '../services/external.service.js';
|
|
7
|
-
import { signedUrl, listFileByPath, fileUpload, insertOpenSearchData } from 'tango-app-api-middleware';
|
|
9
|
+
import { signedUrl, listFileByPath, fileUpload, insertOpenSearchData, download } from 'tango-app-api-middleware';
|
|
8
10
|
import * as checklistconfigService from '../services/checklistconfig.services.js';
|
|
9
|
-
|
|
10
11
|
import axios from 'axios';
|
|
12
|
+
import _ from 'lodash';
|
|
13
|
+
|
|
11
14
|
export const addCustomTag = async ( req, res ) => {
|
|
12
15
|
try {
|
|
13
16
|
let inputData = req.body;
|
|
14
17
|
let taggingDetails = await taggingService.findOne( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName, isDeleted: false } );
|
|
18
|
+
let findgroup = await customZoneTagService.findOne( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName } );
|
|
15
19
|
if ( !taggingDetails ) {
|
|
16
20
|
let data = {
|
|
17
21
|
clientId: inputData.clientId,
|
|
@@ -20,6 +24,7 @@ export const addCustomTag = async ( req, res ) => {
|
|
|
20
24
|
rgbColor: inputData.rgbColor,
|
|
21
25
|
productName: inputData.productName,
|
|
22
26
|
rgbBorderColor: inputData.rgbBorderColor,
|
|
27
|
+
groupName: findgroup.groupName,
|
|
23
28
|
};
|
|
24
29
|
await taggingService.deleteMany( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName, isDeleted: true } );
|
|
25
30
|
await taggingService.create( data );
|
|
@@ -193,7 +198,6 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
193
198
|
{ streamName: 1 },
|
|
194
199
|
),
|
|
195
200
|
] );
|
|
196
|
-
console.log( req.query.clientId, camList.length );
|
|
197
201
|
|
|
198
202
|
// Step 3: Add conditional tags
|
|
199
203
|
if ( clientDetails?.featureConfigs?.isExcludedArea ) {
|
|
@@ -214,7 +218,7 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
214
218
|
// Step 4: Fetch existing tags
|
|
215
219
|
const tagInfo = await taggingService.find(
|
|
216
220
|
{ clientId: req.query.clientId, productName: req.query.selectedProduct },
|
|
217
|
-
{ tagName: 1, rgbColor: 1, rgbBorderColor: 1, productName: 1 },
|
|
221
|
+
{ tagName: 1, rgbColor: 1, rgbBorderColor: 1, productName: 1, groupName: 1 },
|
|
218
222
|
);
|
|
219
223
|
|
|
220
224
|
// Merge all tags
|
|
@@ -226,7 +230,6 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
226
230
|
// Step 6: Query counts per tag
|
|
227
231
|
const tagNames = uniqueTags.map( ( t ) => t.tagName );
|
|
228
232
|
let activeCam = camList.map( ( data ) => data.streamName );
|
|
229
|
-
console.log( '🚀 ~ customTagListv2 ~ activeCam:', activeCam );
|
|
230
233
|
const taggingDetails = await taggingService.aggregate( [
|
|
231
234
|
{
|
|
232
235
|
$match: {
|
|
@@ -255,18 +258,340 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
255
258
|
'Tracker-out': { rgbColor: 'rgba(12, 195, 111, 0.5)', rgbBorderColor: 'rgb(22, 205, 125)' },
|
|
256
259
|
};
|
|
257
260
|
|
|
261
|
+
|
|
258
262
|
// Step 8: Final processing
|
|
259
263
|
const finalTags = uniqueTags.map( ( tag ) => ( {
|
|
260
264
|
tagName: tag.tagName,
|
|
261
265
|
productName: tag.productName,
|
|
266
|
+
groupName: tag.groupName ? tag.groupName : '',
|
|
262
267
|
count: countMap.get( tag.tagName ) || 0,
|
|
263
268
|
rgbColor: tag.rgbColor || tagColors[tag.tagName]?.rgbColor,
|
|
264
269
|
rgbBorderColor: tag.rgbBorderColor || tagColors[tag.tagName]?.rgbBorderColor,
|
|
265
270
|
} ) );
|
|
271
|
+
const groupedData = finalTags.reduce( ( acc, item ) => {
|
|
272
|
+
if ( item.groupName === '' ) {
|
|
273
|
+
item.isGroup = false;
|
|
274
|
+
// If groupName is empty, push the item as a standalone object
|
|
275
|
+
acc.push( item );
|
|
276
|
+
} else {
|
|
277
|
+
// If groupName exists, find if we already started a group for it
|
|
278
|
+
let group = acc.find( ( i ) => i.isGroup && i.groupName === item.groupName );
|
|
279
|
+
|
|
280
|
+
if ( !group ) {
|
|
281
|
+
// Create a new group container
|
|
282
|
+
group = {
|
|
283
|
+
groupName: item.groupName,
|
|
284
|
+
isGroup: true, // flag to identify this is a group
|
|
285
|
+
children: [],
|
|
286
|
+
};
|
|
287
|
+
acc.push( group );
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Push the current item into the group's children
|
|
291
|
+
group.children.push( item );
|
|
292
|
+
}
|
|
293
|
+
return acc;
|
|
294
|
+
}, [] );
|
|
266
295
|
|
|
267
296
|
|
|
268
297
|
// Step 9: Sort by count (desc)
|
|
269
|
-
|
|
298
|
+
groupedData.sort( ( a, b ) => b.count - a.count );
|
|
299
|
+
|
|
300
|
+
if ( req.query.selectedProduct && req.query.selectedProduct === 'tangoTrax' ) {
|
|
301
|
+
let Query = [ {
|
|
302
|
+
$match: {
|
|
303
|
+
client_id: req.query.clientId,
|
|
304
|
+
publish: true,
|
|
305
|
+
checkListType: { $ne: 'custom' },
|
|
306
|
+
},
|
|
307
|
+
}, {
|
|
308
|
+
$group: {
|
|
309
|
+
_id: '$_id',
|
|
310
|
+
sourceCheckList_id: { $last: '$_id' },
|
|
311
|
+
tagName: { $last: '$checkListName' },
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
$lookup: {
|
|
316
|
+
from: 'cameras',
|
|
317
|
+
let: { checkListName: '$tagName' },
|
|
318
|
+
pipeline: [
|
|
319
|
+
{
|
|
320
|
+
$match: {
|
|
321
|
+
$expr: {
|
|
322
|
+
$anyElementTrue: {
|
|
323
|
+
$map: {
|
|
324
|
+
input: { $ifNull: [ '$taggedChecklist', [] ] }, // ✅ default to empty array
|
|
325
|
+
as: 'tc',
|
|
326
|
+
in: { $eq: [ '$$tc.checkListName', '$$checkListName' ] },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
$project: {
|
|
334
|
+
storeId: 1,
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
], as: 'cameraList',
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
$project: {
|
|
342
|
+
tagName: 1,
|
|
343
|
+
type: 'checklist',
|
|
344
|
+
cameraList: {
|
|
345
|
+
$filter: {
|
|
346
|
+
input: '$cameraList',
|
|
347
|
+
as: 'item',
|
|
348
|
+
cond: {
|
|
349
|
+
$eq: [ '$$item.storeId', req.query.storeId ],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
$project: {
|
|
357
|
+
tagName: 1,
|
|
358
|
+
type: 1,
|
|
359
|
+
count: { $size: '$cameraList' },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
$sort: {
|
|
364
|
+
count: -1,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
];
|
|
369
|
+
let getChecklistData = await checklistconfigService.aggregate( Query );
|
|
370
|
+
if ( finalTags && finalTags.length > 0 ) {
|
|
371
|
+
let merged = getChecklistData.map( ( item ) => {
|
|
372
|
+
let match = finalTags.find( ( a ) => a.tagName === item.tagName );
|
|
373
|
+
if ( match ) {
|
|
374
|
+
return { ...item, ...match, count: item.count + match.count }; // add counts
|
|
375
|
+
}
|
|
376
|
+
return item;
|
|
377
|
+
} );
|
|
378
|
+
// also include any arr1 items not in arr2
|
|
379
|
+
finalTags.forEach( ( a ) => {
|
|
380
|
+
if ( !merged.find( ( m ) => m.tagName === a.tagName ) ) {
|
|
381
|
+
merged.push( a );
|
|
382
|
+
}
|
|
383
|
+
} );
|
|
384
|
+
merged.sort( ( a, b ) => b.count - a.count );
|
|
385
|
+
return res.sendSuccess( merged );
|
|
386
|
+
} else {
|
|
387
|
+
return res.sendSuccess( getChecklistData );
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
return res.sendSuccess( groupedData );
|
|
391
|
+
}
|
|
392
|
+
} catch ( e ) {
|
|
393
|
+
logger.error( { error: e, function: 'customTagList' } );
|
|
394
|
+
return res.sendError( e, 500 );
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
export const customTagListv3 = async ( req, res ) => {
|
|
398
|
+
try {
|
|
399
|
+
// Step 1: Base tag list
|
|
400
|
+
let customTagList = [
|
|
401
|
+
{ tagName: 'Front', productName: 'tangoTraffic' },
|
|
402
|
+
{ tagName: 'Back', productName: 'tangoTraffic' },
|
|
403
|
+
{ tagName: 'Tracker-in', productName: 'tangoTracker' },
|
|
404
|
+
{ tagName: 'Tracker-out', productName: 'tangoTracker' },
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
// Step 2: Fetch store & client details in parallel
|
|
408
|
+
const [ storeDetails, clientDetails, camList ] = await Promise.all( [
|
|
409
|
+
storeService.findOne(
|
|
410
|
+
{ storeId: req.query.storeId },
|
|
411
|
+
{ product: 1 },
|
|
412
|
+
),
|
|
413
|
+
clientService.findOne(
|
|
414
|
+
{ clientId: req.query.clientId },
|
|
415
|
+
{ featureConfigs: 1 },
|
|
416
|
+
),
|
|
417
|
+
cameraService.find(
|
|
418
|
+
{ storeId: req.query.storeId, isActivated: true, isUp: true },
|
|
419
|
+
{ streamName: 1 },
|
|
420
|
+
),
|
|
421
|
+
] );
|
|
422
|
+
|
|
423
|
+
// Step 3: Add conditional tags
|
|
424
|
+
if ( clientDetails?.featureConfigs?.isExcludedArea ) {
|
|
425
|
+
customTagList.push( { tagName: 'Excluded Area', productName: 'tangoTraffic' } );
|
|
426
|
+
}
|
|
427
|
+
if ( clientDetails?.featureConfigs?.isPasserByData ) {
|
|
428
|
+
customTagList.push( { tagName: 'Passer By', productName: 'tangoTraffic' } );
|
|
429
|
+
}
|
|
430
|
+
if ( storeDetails?.product?.includes( 'tangoZone' ) ) {
|
|
431
|
+
customTagList.push(
|
|
432
|
+
{ tagName: 'Entry/Exit', productName: 'tangoZone' },
|
|
433
|
+
{ tagName: 'Billing', productName: 'tangoZone' },
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if ( req.query.selectedProduct && req.query.selectedProduct != '' ) {
|
|
437
|
+
customTagList = customTagList.filter( ( ele ) => ele.productName === req.query.selectedProduct );
|
|
438
|
+
}
|
|
439
|
+
// Step 4: Fetch existing tags
|
|
440
|
+
const tagInfo = await taggingService.find(
|
|
441
|
+
{ clientId: req.query.clientId, productName: req.query.selectedProduct },
|
|
442
|
+
{ tagName: 1, rgbColor: 1, rgbBorderColor: 1, productName: 1, groupName: 1 },
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// Merge all tags
|
|
446
|
+
const mergedTags = [ ...customTagList, ...tagInfo ];
|
|
447
|
+
const uniqueTags = [ ...new Map( mergedTags.map( ( tag ) => [ tag.tagName, tag ] ) ).values() ];
|
|
448
|
+
|
|
449
|
+
// Step 6: Query counts per tag
|
|
450
|
+
const tagNames = uniqueTags.map( ( t ) => t.tagName );
|
|
451
|
+
let activeCam = camList.map( ( data ) => data.streamName );
|
|
452
|
+
const taggingDetails = await taggingService.aggregate( [
|
|
453
|
+
{
|
|
454
|
+
$match: {
|
|
455
|
+
productName: req.query.selectedProduct,
|
|
456
|
+
clientId: req.query.clientId,
|
|
457
|
+
storeId: req.query.storeId,
|
|
458
|
+
coordinates: { $exists: true, $ne: [] },
|
|
459
|
+
tagName: { $in: tagNames },
|
|
460
|
+
streamName: { $in: activeCam },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
{ $group: { _id: '$tagName', count: { $sum: 1 } } },
|
|
464
|
+
] );
|
|
465
|
+
|
|
466
|
+
const countMap = new Map( taggingDetails.map( ( t ) => [ t._id, t.count ] ) );
|
|
467
|
+
|
|
468
|
+
// Step 7: Tag colors config (instead of many ifs)
|
|
469
|
+
const tagColors = {
|
|
470
|
+
'Front': { rgbColor: 'rgba(89, 80, 5, 0.5)', rgbBorderColor: 'rgb(99, 90, 15)' },
|
|
471
|
+
'Back': { rgbColor: 'rgba(94, 60, 107, 0.5)', rgbBorderColor: 'rgb(104, 70, 117)' },
|
|
472
|
+
'Excluded Area': { rgbColor: 'rgba(186, 60, 214, 0.5)', rgbBorderColor: 'rgb(196, 70, 224)' },
|
|
473
|
+
'Passer By': { rgbColor: 'rgba(81, 153, 247, 0.5)', rgbBorderColor: 'rgb(91, 163, 257)' },
|
|
474
|
+
'Entry/Exit': { rgbColor: 'rgba(224, 43, 170, 0.5)', rgbBorderColor: 'rgb(234, 53, 180)' },
|
|
475
|
+
'Billing': { rgbColor: 'rgba(193, 214, 114, 0.5)', rgbBorderColor: 'rgb(203, 224, 124)' },
|
|
476
|
+
'Tracker-in': { rgbColor: 'rgba(123, 95, 105, 0.5)', rgbBorderColor: 'rgb(133, 105, 115)' },
|
|
477
|
+
'Tracker-out': { rgbColor: 'rgba(12, 195, 111, 0.5)', rgbBorderColor: 'rgb(22, 205, 125)' },
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
// Step 8: Final processing
|
|
482
|
+
const finalTags = uniqueTags.map( ( tag ) => ( {
|
|
483
|
+
tagName: tag.tagName,
|
|
484
|
+
productName: tag.productName,
|
|
485
|
+
groupName: tag.groupName ? tag.groupName : '',
|
|
486
|
+
count: countMap.get( tag.tagName ) || 0,
|
|
487
|
+
rgbColor: tag.rgbColor || tagColors[tag.tagName]?.rgbColor,
|
|
488
|
+
rgbBorderColor: tag.rgbBorderColor || tagColors[tag.tagName]?.rgbBorderColor,
|
|
489
|
+
} ) );
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
console.log( '🚀 ~ customTagListv3 ~ mergedTags:', mergedTags );
|
|
493
|
+
|
|
494
|
+
if ( req.query.selectedProduct === 'tangoZone' ) {
|
|
495
|
+
const zonefinalTags = await customZoneTagService.aggregate( [
|
|
496
|
+
{
|
|
497
|
+
$match: {
|
|
498
|
+
clientId: req.query.clientId,
|
|
499
|
+
productName: req.query.selectedProduct,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// Count lookup
|
|
504
|
+
{
|
|
505
|
+
$lookup: {
|
|
506
|
+
from: 'taggings',
|
|
507
|
+
let: { tagName: '$tagName', clientId: '$clientId' },
|
|
508
|
+
pipeline: [
|
|
509
|
+
{
|
|
510
|
+
$match: {
|
|
511
|
+
$expr: {
|
|
512
|
+
$and: [
|
|
513
|
+
{ $eq: [ '$tagName', '$$tagName' ] },
|
|
514
|
+
{ $eq: [ '$clientId', '$$clientId' ] },
|
|
515
|
+
{ $in: [ '$streamName', activeCam ] },
|
|
516
|
+
{ $ne: [ '$coordinates', [] ] },
|
|
517
|
+
],
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{ $count: 'count' },
|
|
522
|
+
],
|
|
523
|
+
as: 'tagsCount',
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
// Add count
|
|
528
|
+
{
|
|
529
|
+
$addFields: {
|
|
530
|
+
count: {
|
|
531
|
+
$ifNull: [ { $arrayElemAt: [ '$tagsCount.count', 0 ] }, 0 ],
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
{
|
|
537
|
+
$project: {
|
|
538
|
+
_id: 0,
|
|
539
|
+
tagName: 1,
|
|
540
|
+
productName: 1,
|
|
541
|
+
groupName: 1,
|
|
542
|
+
rgbColor: 1,
|
|
543
|
+
rgbBorderColor: 1,
|
|
544
|
+
count: 1,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
// Split grouped vs ungrouped
|
|
549
|
+
{
|
|
550
|
+
$facet: {
|
|
551
|
+
grouped: [
|
|
552
|
+
{ $match: { groupName: { $nin: [ null, '' ] } } },
|
|
553
|
+
{
|
|
554
|
+
$group: {
|
|
555
|
+
_id: '$groupName',
|
|
556
|
+
children: { $push: '$$ROOT' },
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
$project: {
|
|
561
|
+
_id: 0,
|
|
562
|
+
groupName: '$_id',
|
|
563
|
+
isGroup: { $literal: true },
|
|
564
|
+
children: 1,
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
|
|
569
|
+
ungrouped: [
|
|
570
|
+
{ $match: { groupName: { $in: [ null, '' ] } } },
|
|
571
|
+
{
|
|
572
|
+
$addFields: { isGroup: { $literal: false } },
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
// Merge results
|
|
579
|
+
{
|
|
580
|
+
$project: {
|
|
581
|
+
result: { $concatArrays: [ '$ungrouped', '$grouped' ] },
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
{ $unwind: '$result' },
|
|
586
|
+
{ $replaceRoot: { newRoot: '$result' } },
|
|
587
|
+
] );
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
console.log( '🚀 ~ customTagListv3 ~ taggingDetails:', zonefinalTags );
|
|
591
|
+
return res.sendSuccess( zonefinalTags );
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
|
|
270
595
|
if ( req.query.selectedProduct && req.query.selectedProduct === 'tangoTrax' ) {
|
|
271
596
|
let Query = [ {
|
|
272
597
|
$match: {
|
|
@@ -337,7 +662,6 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
337
662
|
|
|
338
663
|
];
|
|
339
664
|
let getChecklistData = await checklistconfigService.aggregate( Query );
|
|
340
|
-
console.log( getChecklistData, finalTags );
|
|
341
665
|
if ( finalTags && finalTags.length > 0 ) {
|
|
342
666
|
let merged = getChecklistData.map( ( item ) => {
|
|
343
667
|
let match = finalTags.find( ( a ) => a.tagName === item.tagName );
|
|
@@ -346,7 +670,6 @@ export const customTagListv2 = async ( req, res ) => {
|
|
|
346
670
|
}
|
|
347
671
|
return item;
|
|
348
672
|
} );
|
|
349
|
-
console.log( merged );
|
|
350
673
|
// also include any arr1 items not in arr2
|
|
351
674
|
finalTags.forEach( ( a ) => {
|
|
352
675
|
if ( !merged.find( ( m ) => m.tagName === a.tagName ) ) {
|
|
@@ -525,20 +848,6 @@ export const tagging = async ( req, res ) => {
|
|
|
525
848
|
|
|
526
849
|
await externalService.create( data );
|
|
527
850
|
}
|
|
528
|
-
|
|
529
|
-
let cameraDetails = await cameraService.findOne( { _id: InputData.cameraId, streamName: InputData.streamName }, { productModule: 1 } );
|
|
530
|
-
if ( cameraDetails ) {
|
|
531
|
-
if ( !cameraDetails?.productModule.includes( 'tangoZone' ) ) {
|
|
532
|
-
cameraDetails.productModule.push( { productName: 'tangoZone', checked: true } );
|
|
533
|
-
cameraDetails.save();
|
|
534
|
-
} else {
|
|
535
|
-
let camIndex = cameraDetails.productModule.findIndex( ( ele ) => ele.productName == 'tangoZone' );
|
|
536
|
-
if ( !cameraDetails?.productModule?.[camIndex]?.checked ) {
|
|
537
|
-
cameraDetails.productModule[camIndex].checked = true;
|
|
538
|
-
cameraDetails.save();
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
851
|
await updatezoneTagging( req, res );
|
|
543
852
|
} catch ( e ) {
|
|
544
853
|
logger.error( { error: e, function: 'tagging' } );
|
|
@@ -1090,11 +1399,13 @@ export const getCameraStreamList = async ( req, res ) => {
|
|
|
1090
1399
|
if ( !cameraDetails.length ) {
|
|
1091
1400
|
return res.sendError( 'no data found', 204 );
|
|
1092
1401
|
}
|
|
1093
|
-
const folderPath = {
|
|
1402
|
+
const folderPath = {
|
|
1403
|
+
file_path: `${req.body.storeId}/zone_base_images/`,
|
|
1094
1404
|
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage, MaxKeys: 1000,
|
|
1095
1405
|
};
|
|
1096
1406
|
let fileList = await listFileByPath( folderPath );
|
|
1097
|
-
const TaggedfolderPath = {
|
|
1407
|
+
const TaggedfolderPath = {
|
|
1408
|
+
file_path: `${req.body.storeId}/zone_tagged_image/`,
|
|
1098
1409
|
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage, MaxKeys: 1000,
|
|
1099
1410
|
};
|
|
1100
1411
|
let tagFileList = await listFileByPath( TaggedfolderPath );
|
|
@@ -1120,7 +1431,7 @@ export const getCameraStreamList = async ( req, res ) => {
|
|
|
1120
1431
|
tagFileList.data.forEach( ( item ) => {
|
|
1121
1432
|
if ( item.Key.length > 1 ) {
|
|
1122
1433
|
let splitStream = item.Key.split( '/' );
|
|
1123
|
-
let getStream = splitStream[splitStream.length -1].split( '.' );
|
|
1434
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
1124
1435
|
|
|
1125
1436
|
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
1126
1437
|
tagPath = item.Key;
|
|
@@ -1131,21 +1442,23 @@ export const getCameraStreamList = async ( req, res ) => {
|
|
|
1131
1442
|
if ( fileList?.data.length ) {
|
|
1132
1443
|
fileList.data.forEach( ( ele ) => {
|
|
1133
1444
|
let splitStream = ele.Key.split( '/' );
|
|
1134
|
-
let getStream = splitStream[splitStream.length -1].split( '.' );
|
|
1445
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
1135
1446
|
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
1136
1447
|
imgPath = ele.Key;
|
|
1137
1448
|
}
|
|
1138
1449
|
} );
|
|
1139
1450
|
}
|
|
1140
1451
|
if ( tagPath ) {
|
|
1141
|
-
const params = {
|
|
1452
|
+
const params = {
|
|
1453
|
+
file_path: tagPath,
|
|
1142
1454
|
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage,
|
|
1143
1455
|
};
|
|
1144
1456
|
const cameraTagImage = await signedUrl( params );
|
|
1145
1457
|
camera.tagImg = cameraTagImage;
|
|
1146
1458
|
}
|
|
1147
1459
|
if ( imgPath ) {
|
|
1148
|
-
const baseParams = {
|
|
1460
|
+
const baseParams = {
|
|
1461
|
+
file_path: imgPath,
|
|
1149
1462
|
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage,
|
|
1150
1463
|
};
|
|
1151
1464
|
const cameraBaseImage = await signedUrl( baseParams );
|
|
@@ -1161,3 +1474,1114 @@ export const getCameraStreamList = async ( req, res ) => {
|
|
|
1161
1474
|
return res.sendError( e, 500 );
|
|
1162
1475
|
}
|
|
1163
1476
|
};
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
// setting config - zone - get tagging details
|
|
1480
|
+
export const getZoneTaggingDetails = async ( req, res ) => {
|
|
1481
|
+
try {
|
|
1482
|
+
const {
|
|
1483
|
+
clientId,
|
|
1484
|
+
searchValue,
|
|
1485
|
+
sortColumName,
|
|
1486
|
+
sortBy,
|
|
1487
|
+
limit = 10,
|
|
1488
|
+
offset = 1,
|
|
1489
|
+
export: isExport,
|
|
1490
|
+
} = req.body;
|
|
1491
|
+
|
|
1492
|
+
if ( !clientId ) {
|
|
1493
|
+
return res.sendError( 'clientId is required', 400 );
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
const matchStage = {
|
|
1497
|
+
clientId,
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
const searchStage = searchValue ?
|
|
1501
|
+
{
|
|
1502
|
+
$or: [
|
|
1503
|
+
{ tagName: { $regex: searchValue, $options: 'i' } },
|
|
1504
|
+
{ groupName: { $regex: searchValue, $options: 'i' } },
|
|
1505
|
+
],
|
|
1506
|
+
} :
|
|
1507
|
+
null;
|
|
1508
|
+
|
|
1509
|
+
const pipeline = [
|
|
1510
|
+
{ $match: matchStage },
|
|
1511
|
+
|
|
1512
|
+
{
|
|
1513
|
+
$lookup: {
|
|
1514
|
+
from: 'taggings',
|
|
1515
|
+
let: { tagName: '$tagName' },
|
|
1516
|
+
pipeline: [
|
|
1517
|
+
{
|
|
1518
|
+
$match: {
|
|
1519
|
+
$expr: {
|
|
1520
|
+
$and: [
|
|
1521
|
+
{ $eq: [ '$tagName', '$$tagName' ] },
|
|
1522
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
1523
|
+
],
|
|
1524
|
+
},
|
|
1525
|
+
},
|
|
1526
|
+
},
|
|
1527
|
+
],
|
|
1528
|
+
as: 'tagsCount',
|
|
1529
|
+
},
|
|
1530
|
+
},
|
|
1531
|
+
|
|
1532
|
+
{
|
|
1533
|
+
$project: {
|
|
1534
|
+
clientId: 1,
|
|
1535
|
+
tagName: 1,
|
|
1536
|
+
groupName: 1,
|
|
1537
|
+
storesTaggedCount: { $size: '$tagsCount' },
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
|
|
1541
|
+
{
|
|
1542
|
+
$facet: {
|
|
1543
|
+
totalCount: [
|
|
1544
|
+
...( searchStage ? [ { $match: searchStage } ] : [] ),
|
|
1545
|
+
{ $count: 'count' },
|
|
1546
|
+
],
|
|
1547
|
+
|
|
1548
|
+
filteredData: [
|
|
1549
|
+
...( searchStage ? [ { $match: searchStage } ] : [] ),
|
|
1550
|
+
...( sortColumName && sortBy ?
|
|
1551
|
+
[ { $sort: { [sortColumName]: sortBy } } ] :
|
|
1552
|
+
[] ),
|
|
1553
|
+
...( isExport ?
|
|
1554
|
+
[] :
|
|
1555
|
+
[
|
|
1556
|
+
{ $skip: ( offset - 1 ) * limit },
|
|
1557
|
+
{ $limit: Number( limit ) },
|
|
1558
|
+
] ),
|
|
1559
|
+
],
|
|
1560
|
+
},
|
|
1561
|
+
},
|
|
1562
|
+
];
|
|
1563
|
+
|
|
1564
|
+
const [ result ] = await customZoneTagService.aggregate( pipeline );
|
|
1565
|
+
|
|
1566
|
+
const totalCount = result.totalCount[0]?.count || 0;
|
|
1567
|
+
const zoneList = result.filteredData;
|
|
1568
|
+
|
|
1569
|
+
// Get total count of groups for tabs (actual count, not filtered by search)
|
|
1570
|
+
const totalGroupCount = await customzonegrouping.count( { clientId } );
|
|
1571
|
+
|
|
1572
|
+
if ( isExport && zoneList.length ) {
|
|
1573
|
+
const exportdata = zoneList.map( ( z ) => ( {
|
|
1574
|
+
'Zone Name': z.tagName || '--',
|
|
1575
|
+
'Zone Groups': z.groupName || '--',
|
|
1576
|
+
'Stores tagged Count': z.storesTaggedCount || 0,
|
|
1577
|
+
} ) );
|
|
1578
|
+
await download( exportdata, res );
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
return res.sendSuccess( {
|
|
1583
|
+
result: zoneList,
|
|
1584
|
+
totalCount,
|
|
1585
|
+
totalGroupCount,
|
|
1586
|
+
} );
|
|
1587
|
+
} catch ( e ) {
|
|
1588
|
+
logger.error( { error: e, function: 'getZoneTaggingDetails' } );
|
|
1589
|
+
return res.sendError( e, 500 );
|
|
1590
|
+
}
|
|
1591
|
+
// try {
|
|
1592
|
+
// const inputData = req.body;
|
|
1593
|
+
// let Query = [
|
|
1594
|
+
// {
|
|
1595
|
+
// $match: {
|
|
1596
|
+
// clientId: inputData.clientId,
|
|
1597
|
+
// },
|
|
1598
|
+
// },
|
|
1599
|
+
// {
|
|
1600
|
+
// $lookup: {
|
|
1601
|
+
// from: 'taggings',
|
|
1602
|
+
// let: { tagName: '$tagName' },
|
|
1603
|
+
// pipeline: [
|
|
1604
|
+
// {
|
|
1605
|
+
// $match: {
|
|
1606
|
+
// $expr: {
|
|
1607
|
+
// $and: [
|
|
1608
|
+
// {
|
|
1609
|
+
// $eq: [ '$tagName', '$$tagName' ],
|
|
1610
|
+
// },
|
|
1611
|
+
// {
|
|
1612
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
1613
|
+
// },
|
|
1614
|
+
// ],
|
|
1615
|
+
// },
|
|
1616
|
+
// },
|
|
1617
|
+
// },
|
|
1618
|
+
// ], as: 'tagsCount',
|
|
1619
|
+
// },
|
|
1620
|
+
// },
|
|
1621
|
+
// {
|
|
1622
|
+
// $project: {
|
|
1623
|
+
// clientId: 1,
|
|
1624
|
+
// tagName: 1,
|
|
1625
|
+
// groupName: 1,
|
|
1626
|
+
// storesTaggedCount: { $size: '$tagsCount' },
|
|
1627
|
+
// },
|
|
1628
|
+
// },
|
|
1629
|
+
// ];
|
|
1630
|
+
// if ( req.body.searchValue && req.body.searchValue !== '' ) {
|
|
1631
|
+
// Query.push( {
|
|
1632
|
+
// $match: {
|
|
1633
|
+
// $or: [
|
|
1634
|
+
// { tagName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
1635
|
+
// { groupName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
1636
|
+
// ],
|
|
1637
|
+
// },
|
|
1638
|
+
// } );
|
|
1639
|
+
// }
|
|
1640
|
+
|
|
1641
|
+
// if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
1642
|
+
// Query.push( {
|
|
1643
|
+
// $sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
1644
|
+
// } );
|
|
1645
|
+
// }
|
|
1646
|
+
|
|
1647
|
+
// const totalCount = await customZoneTagService.aggregate( Query );
|
|
1648
|
+
// if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
1649
|
+
// Query.push(
|
|
1650
|
+
// { $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
1651
|
+
// { $limit: Number( req.body.limit ) },
|
|
1652
|
+
// );
|
|
1653
|
+
// }
|
|
1654
|
+
// const zoneList = await customZoneTagService.aggregate( Query );
|
|
1655
|
+
|
|
1656
|
+
// if ( req.body.export && zoneList.length > 0 ) {
|
|
1657
|
+
// const exportdata = [];
|
|
1658
|
+
// zoneList.forEach( ( element ) => {
|
|
1659
|
+
// const data = {
|
|
1660
|
+
// 'Zone Name': element.tagName || '--',
|
|
1661
|
+
// 'Zone Groups': element.groupName || '--',
|
|
1662
|
+
// 'Stores tagged Count': element.storesTaggedCount || 0,
|
|
1663
|
+
// };
|
|
1664
|
+
// exportdata.push( data );
|
|
1665
|
+
// } );
|
|
1666
|
+
// await download( exportdata, res );
|
|
1667
|
+
// return;
|
|
1668
|
+
// }
|
|
1669
|
+
|
|
1670
|
+
// return res.sendSuccess( {
|
|
1671
|
+
// result: zoneList,
|
|
1672
|
+
// count: totalCount.length,
|
|
1673
|
+
// } );
|
|
1674
|
+
// } catch ( e ) {
|
|
1675
|
+
// logger.error( { error: e, function: 'getZoneTaggingDetails' } );
|
|
1676
|
+
// console.error( 'getZoneTaggingDetails error:', e );
|
|
1677
|
+
// return res.sendError( e, 500 );
|
|
1678
|
+
// }
|
|
1679
|
+
};
|
|
1680
|
+
|
|
1681
|
+
export const addZoneCustomTag = async ( req, res ) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const {
|
|
1684
|
+
clientId,
|
|
1685
|
+
tagName,
|
|
1686
|
+
groupName,
|
|
1687
|
+
productName,
|
|
1688
|
+
isExistingGroup,
|
|
1689
|
+
rgbColor,
|
|
1690
|
+
rgbBorderColor,
|
|
1691
|
+
_id,
|
|
1692
|
+
} = req.body;
|
|
1693
|
+
|
|
1694
|
+
if ( !clientId || !tagName ) {
|
|
1695
|
+
return res.sendError( 'clientId and tagName are required', 400 );
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
const existingTag = await customZoneTagService.findOne(
|
|
1699
|
+
{ clientId, tagName, _id: { $ne: _id } },
|
|
1700
|
+
);
|
|
1701
|
+
|
|
1702
|
+
if ( existingTag ) {
|
|
1703
|
+
return res.sendError( `tagName "${tagName}" already exists for this client`, 409 );
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
let groupDoc = null;
|
|
1707
|
+
if ( groupName ) {
|
|
1708
|
+
groupDoc = await customzonegrouping.findOne( { clientId, groupName } );
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
if ( !isExistingGroup && groupDoc ) {
|
|
1712
|
+
return res.sendError( `groupName "${groupName}" already exists for this client`, 409 );
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
/** Create Zone Tag */
|
|
1716
|
+
await customZoneTagService.create(
|
|
1717
|
+
[ {
|
|
1718
|
+
clientId,
|
|
1719
|
+
tagName,
|
|
1720
|
+
rgbColor,
|
|
1721
|
+
rgbBorderColor,
|
|
1722
|
+
groupName: groupName || null,
|
|
1723
|
+
productName,
|
|
1724
|
+
} ],
|
|
1725
|
+
);
|
|
1726
|
+
|
|
1727
|
+
/** Create or Update Zone Group */
|
|
1728
|
+
if ( groupName ) {
|
|
1729
|
+
if ( !groupDoc && !isExistingGroup ) {
|
|
1730
|
+
await customzonegrouping.create(
|
|
1731
|
+
[ {
|
|
1732
|
+
clientId,
|
|
1733
|
+
groupName,
|
|
1734
|
+
productName,
|
|
1735
|
+
zonesTagged: [ tagName ],
|
|
1736
|
+
} ],
|
|
1737
|
+
);
|
|
1738
|
+
} else if ( groupDoc && isExistingGroup ) {
|
|
1739
|
+
await customzonegrouping.updateOne(
|
|
1740
|
+
{ _id: groupDoc._id },
|
|
1741
|
+
{ $addToSet: { zonesTagged: tagName } },
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
await taggingService.updateMany( { clientId, tagName }, { $set: { groupName } } );
|
|
1747
|
+
|
|
1748
|
+
logger.info( 'Zone Tag Created Successfully' );
|
|
1749
|
+
const logObj = {
|
|
1750
|
+
clientId: clientId,
|
|
1751
|
+
userName: req.user?.userName,
|
|
1752
|
+
email: req.user?.email,
|
|
1753
|
+
date: new Date(),
|
|
1754
|
+
logType: 'zone',
|
|
1755
|
+
logSubType: 'addZoneCustomTag',
|
|
1756
|
+
changes: [ `${tagName} custom zone tag` ],
|
|
1757
|
+
eventType: 'create',
|
|
1758
|
+
showTo: [ 'client', 'tango' ],
|
|
1759
|
+
};
|
|
1760
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
1761
|
+
|
|
1762
|
+
return res.sendSuccess( 'Zone Tag Created Successfully' );
|
|
1763
|
+
} catch ( e ) {
|
|
1764
|
+
logger.error( { error: e, function: 'addZoneCustomTag' } );
|
|
1765
|
+
return res.sendError( e.message || e, 500 );
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
export const uploadBulkZoneTag = async ( req, res ) => {
|
|
1770
|
+
try {
|
|
1771
|
+
const zonesArray = req.body;
|
|
1772
|
+
const clientId = req.bulkZoneClientId;
|
|
1773
|
+
|
|
1774
|
+
if ( !zonesArray || !Array.isArray( zonesArray ) || zonesArray.length === 0 ) {
|
|
1775
|
+
return res.sendError( 'zone-config file must not be empty', 400 );
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
if ( !clientId ) {
|
|
1779
|
+
return res.sendError( 'clientId is required', 400 );
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
const zoneTagDataList = [];
|
|
1783
|
+
const zoneGroupDataMap = new Map();
|
|
1784
|
+
const createdTagNames = [];
|
|
1785
|
+
|
|
1786
|
+
// Process each zone in the array
|
|
1787
|
+
for ( const zone of zonesArray ) {
|
|
1788
|
+
const zoneTagData = {
|
|
1789
|
+
clientId: clientId,
|
|
1790
|
+
tagName: zone.tagName,
|
|
1791
|
+
rgbColor: zone.rgbColor,
|
|
1792
|
+
rgbBorderColor: zone.rgbBorderColor,
|
|
1793
|
+
groupName: zone.groupName && zone.groupName !== '' ? zone.groupName : null,
|
|
1794
|
+
productName: zone.productName,
|
|
1795
|
+
};
|
|
1796
|
+
zoneTagDataList.push( zoneTagData );
|
|
1797
|
+
createdTagNames.push( zone.tagName );
|
|
1798
|
+
|
|
1799
|
+
// Prepare zone group data if groupName is provided
|
|
1800
|
+
if ( zone.groupName && zone.groupName !== '' && zone.groupName !== null ) {
|
|
1801
|
+
if ( !zoneGroupDataMap.has( zone.groupName ) ) {
|
|
1802
|
+
zoneGroupDataMap.set( zone.groupName, {
|
|
1803
|
+
clientId: clientId,
|
|
1804
|
+
groupName: zone.groupName,
|
|
1805
|
+
productName: zone.productName,
|
|
1806
|
+
} );
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// Create all zone tags
|
|
1812
|
+
await customZoneTagService.create( zoneTagDataList );
|
|
1813
|
+
|
|
1814
|
+
// Create zone groups (convert Map values to array)
|
|
1815
|
+
const zoneGroupDataList = Array.from( zoneGroupDataMap.values() );
|
|
1816
|
+
if ( zoneGroupDataList.length > 0 ) {
|
|
1817
|
+
for ( const groupData of zoneGroupDataList ) {
|
|
1818
|
+
await customzonegrouping.create( groupData );
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
logger.info( `Bulk Zone Tags Created Successfully: ${createdTagNames.length} tags` );
|
|
1823
|
+
const logObj = {
|
|
1824
|
+
clientId: clientId,
|
|
1825
|
+
userName: req.user?.userName,
|
|
1826
|
+
email: req.user?.email,
|
|
1827
|
+
date: new Date(),
|
|
1828
|
+
logType: 'zone',
|
|
1829
|
+
logSubType: 'addBulkZoneCustomTag',
|
|
1830
|
+
changes: createdTagNames.map( ( tagName ) => `${tagName} custom tag` ),
|
|
1831
|
+
eventType: 'create',
|
|
1832
|
+
showTo: [ 'client', 'tango' ],
|
|
1833
|
+
};
|
|
1834
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
1835
|
+
return res.sendSuccess( {
|
|
1836
|
+
message: `Zone Tags Created Successfully`,
|
|
1837
|
+
count: createdTagNames.length,
|
|
1838
|
+
} );
|
|
1839
|
+
} catch ( e ) {
|
|
1840
|
+
logger.error( { error: e, function: 'addBulkZoneCustomTag' } );
|
|
1841
|
+
return res.sendError( 'Failed to upload zone-config file', 500 );
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1845
|
+
export const updateZoneCustomTag = async ( req, res ) => {
|
|
1846
|
+
try {
|
|
1847
|
+
const { clientId, tagName, oldTag, isExistingGroup, oldGroupName, groupName } = req.body;
|
|
1848
|
+
|
|
1849
|
+
if ( !clientId || !tagName ) {
|
|
1850
|
+
return res.sendError( 'clientId, tagName are required', 400 );
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
let findQuery = {};
|
|
1854
|
+
if ( oldTag !== undefined && oldTag !== null && tagName !== oldTag ) {
|
|
1855
|
+
findQuery = {
|
|
1856
|
+
clientId,
|
|
1857
|
+
tagName: oldTag,
|
|
1858
|
+
};
|
|
1859
|
+
} else {
|
|
1860
|
+
findQuery = {
|
|
1861
|
+
clientId,
|
|
1862
|
+
tagName,
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
let customZoneTagDetails = await customZoneTagService.findOne( findQuery );
|
|
1867
|
+
|
|
1868
|
+
if ( !customZoneTagDetails ) {
|
|
1869
|
+
return res.sendError( 'No data found', 400 );
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// Get the actual oldGroupName from the database record
|
|
1873
|
+
const actualOldGroupName = customZoneTagDetails.groupName || null;
|
|
1874
|
+
// Get the actual tag name currently in the database
|
|
1875
|
+
const actualTagNameInDb = customZoneTagDetails.tagName;
|
|
1876
|
+
|
|
1877
|
+
// Validate that the new tagName does not already exist for this client
|
|
1878
|
+
// Check if tagName is being changed (either via oldTag or if it's different from DB value)
|
|
1879
|
+
const isTagNameChanging = ( oldTag && tagName !== oldTag ) || ( actualTagNameInDb && tagName !== actualTagNameInDb );
|
|
1880
|
+
|
|
1881
|
+
if ( isTagNameChanging ) {
|
|
1882
|
+
const existingTagWithNewName = await customZoneTagService.findOne( {
|
|
1883
|
+
clientId,
|
|
1884
|
+
tagName,
|
|
1885
|
+
} );
|
|
1886
|
+
|
|
1887
|
+
// If a tag with the new name exists and it's not the same record we're updating, return error
|
|
1888
|
+
if ( existingTagWithNewName && existingTagWithNewName._id.toString() !== customZoneTagDetails._id.toString() ) {
|
|
1889
|
+
return res.sendError( `tagName "${tagName}" already exists for this client`, 409 );
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
let query = {
|
|
1894
|
+
clientId,
|
|
1895
|
+
};
|
|
1896
|
+
let payload = { tagName };
|
|
1897
|
+
if ( oldTag && tagName !== oldTag ) {
|
|
1898
|
+
query.tagName = oldTag;
|
|
1899
|
+
} else {
|
|
1900
|
+
query.tagName = tagName;
|
|
1901
|
+
}
|
|
1902
|
+
if ( groupName ) {
|
|
1903
|
+
payload.groupName = groupName || null;
|
|
1904
|
+
} else {
|
|
1905
|
+
payload.groupName = null;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
// Determine the tag name to use for group operations
|
|
1909
|
+
// Use the actual tag name from database for removing from old group
|
|
1910
|
+
const tagNameForRemoval = actualTagNameInDb;
|
|
1911
|
+
const newTagNameForGroup = tagName;
|
|
1912
|
+
|
|
1913
|
+
// Handle group changes: remove from old group and add to new group
|
|
1914
|
+
// Compare actual old group name from DB with new group name
|
|
1915
|
+
const oldGroupNameStr = actualOldGroupName ? String( actualOldGroupName ).trim() : null;
|
|
1916
|
+
const newGroupNameStr = groupName ? String( groupName ).trim() : null;
|
|
1917
|
+
|
|
1918
|
+
if ( oldGroupNameStr !== newGroupNameStr ) {
|
|
1919
|
+
// Remove tag from old group if oldGroupName exists
|
|
1920
|
+
if ( actualOldGroupName ) {
|
|
1921
|
+
const oldGroupDoc = await customzonegrouping.findOne(
|
|
1922
|
+
{ clientId, groupName: actualOldGroupName },
|
|
1923
|
+
);
|
|
1924
|
+
if ( oldGroupDoc ) {
|
|
1925
|
+
await customzonegrouping.updateOne(
|
|
1926
|
+
{ _id: oldGroupDoc._id },
|
|
1927
|
+
{ $pull: { zonesTagged: tagNameForRemoval } },
|
|
1928
|
+
);
|
|
1929
|
+
} else {
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// Add tag to new group if groupName exists
|
|
1934
|
+
if ( groupName ) {
|
|
1935
|
+
let newGroupDoc = await customzonegrouping.findOne(
|
|
1936
|
+
{ clientId, groupName },
|
|
1937
|
+
);
|
|
1938
|
+
|
|
1939
|
+
if ( !newGroupDoc && !isExistingGroup ) {
|
|
1940
|
+
await customzonegrouping.create(
|
|
1941
|
+
[ {
|
|
1942
|
+
clientId,
|
|
1943
|
+
groupName,
|
|
1944
|
+
productName: req.body.productName,
|
|
1945
|
+
zonesTagged: [ newTagNameForGroup ],
|
|
1946
|
+
} ],
|
|
1947
|
+
);
|
|
1948
|
+
} else if ( newGroupDoc ) {
|
|
1949
|
+
// Add to existing group
|
|
1950
|
+
await customzonegrouping.updateOne(
|
|
1951
|
+
{ _id: newGroupDoc._id },
|
|
1952
|
+
{ $addToSet: { zonesTagged: newTagNameForGroup } },
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
} else if ( groupName && oldGroupNameStr === newGroupNameStr && oldTag && tagName !== oldTag ) {
|
|
1957
|
+
// If groupName is same (and not null) but tagName changed, update the tagName in the group's zonesTagged
|
|
1958
|
+
const groupDoc = await customzonegrouping.findOne( { clientId, groupName } );
|
|
1959
|
+
if ( groupDoc ) {
|
|
1960
|
+
await customzonegrouping.updateOne(
|
|
1961
|
+
{ _id: groupDoc._id },
|
|
1962
|
+
[
|
|
1963
|
+
{
|
|
1964
|
+
$set: {
|
|
1965
|
+
zonesTagged: {
|
|
1966
|
+
$setUnion: [
|
|
1967
|
+
{
|
|
1968
|
+
$filter: {
|
|
1969
|
+
input: '$zonesTagged',
|
|
1970
|
+
cond: { $ne: [ '$$this', actualTagNameInDb ] },
|
|
1971
|
+
},
|
|
1972
|
+
},
|
|
1973
|
+
[ tagName ],
|
|
1974
|
+
],
|
|
1975
|
+
},
|
|
1976
|
+
},
|
|
1977
|
+
},
|
|
1978
|
+
],
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
// Update tagging collection if groupName changed or tagName changed (similar to addZoneCustomTag)
|
|
1983
|
+
if ( oldTag && tagName !== oldTag ) {
|
|
1984
|
+
// If tagName changed, update tagging collection records with oldTag to have new tagName and groupName
|
|
1985
|
+
await taggingService.updateMany(
|
|
1986
|
+
{ clientId, tagName: oldTag },
|
|
1987
|
+
{ $set: { tagName, groupName: groupName || null } },
|
|
1988
|
+
);
|
|
1989
|
+
} else if ( groupName !== oldGroupName ) {
|
|
1990
|
+
// If only groupName changed, update tagging collection with new groupName
|
|
1991
|
+
await taggingService.updateMany(
|
|
1992
|
+
{ clientId, tagName },
|
|
1993
|
+
{ $set: { groupName: groupName || null } },
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
let zonetag = await customZoneTagService.updateOne( query, { $set: payload } );
|
|
1998
|
+
const logObj = {
|
|
1999
|
+
clientId: req.body.clientId,
|
|
2000
|
+
userName: req.user?.userName,
|
|
2001
|
+
email: req.user?.email,
|
|
2002
|
+
date: new Date(),
|
|
2003
|
+
logType: 'zone',
|
|
2004
|
+
logSubType: 'updateZoneCustomTag',
|
|
2005
|
+
changes: [ `tagName changed from ${oldTag} to ${tagName}` ],
|
|
2006
|
+
eventType: 'update',
|
|
2007
|
+
previous: {
|
|
2008
|
+
tagName: oldTag,
|
|
2009
|
+
},
|
|
2010
|
+
current: {
|
|
2011
|
+
tagName: tagName,
|
|
2012
|
+
},
|
|
2013
|
+
oldData: {
|
|
2014
|
+
TagName: oldTag,
|
|
2015
|
+
},
|
|
2016
|
+
newData: {
|
|
2017
|
+
TagName: tagName,
|
|
2018
|
+
},
|
|
2019
|
+
showTo: [ 'tango', 'client' ],
|
|
2020
|
+
};
|
|
2021
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2022
|
+
if ( zonetag.modifiedCount || zonetag.matchedCount ) {
|
|
2023
|
+
logger.info( 'Zone Tag Updated Successfully' );
|
|
2024
|
+
res.sendSuccess( 'Zone Tag Updated Successfully' );
|
|
2025
|
+
} else {
|
|
2026
|
+
logger.error( { error: 'something went wrong', function: 'updateZoneCustomTag' } );
|
|
2027
|
+
return res.sendError( 'something went wrong', 500 );
|
|
2028
|
+
}
|
|
2029
|
+
} catch ( error ) {
|
|
2030
|
+
logger.error( { error: error, function: 'updateZoneCustomTag' } );
|
|
2031
|
+
return res.sendError( error, 500 );
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
|
|
2035
|
+
export const deleteZoneCustomTag = async ( req, res ) => {
|
|
2036
|
+
try {
|
|
2037
|
+
const { clientId, tagName } = req.body;
|
|
2038
|
+
if ( !clientId || !tagName ) {
|
|
2039
|
+
return res.sendError( 'clientId and tagName are required', 400 );
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
let zoneTagDetails = await customZoneTagService.findOne( { clientId, tagName } );
|
|
2043
|
+
if ( !zoneTagDetails || zoneTagDetails?.length == 0 ) {
|
|
2044
|
+
return res.sendError( 'No data found', 400 );
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
await customZoneTagService.deleteOne( { clientId, tagName } );
|
|
2048
|
+
const logObj = {
|
|
2049
|
+
clientId: req.body?.clientId,
|
|
2050
|
+
userName: req.user?.userName,
|
|
2051
|
+
email: req.user?.email,
|
|
2052
|
+
date: new Date(),
|
|
2053
|
+
logType: 'zone',
|
|
2054
|
+
logSubType: 'deleteZoneCustomTag',
|
|
2055
|
+
changes: [ `${tagName} zone tag` ],
|
|
2056
|
+
eventType: 'delete',
|
|
2057
|
+
showTo: [ 'client', 'tango' ],
|
|
2058
|
+
};
|
|
2059
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2060
|
+
return res.sendSuccess( 'Zone Tag Deleted Successfully' );
|
|
2061
|
+
} catch ( e ) {
|
|
2062
|
+
logger.error( { error: e, function: 'deleteZoneCustomTag' } );
|
|
2063
|
+
return res.sendError( e, 500 );
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
|
|
2068
|
+
// setting config - get zone grouping details
|
|
2069
|
+
export const getZoneGroupDetails = async ( req, res ) => {
|
|
2070
|
+
try {
|
|
2071
|
+
const {
|
|
2072
|
+
clientId,
|
|
2073
|
+
searchValue,
|
|
2074
|
+
sortColumName,
|
|
2075
|
+
sortBy,
|
|
2076
|
+
limit = 10,
|
|
2077
|
+
offset = 1,
|
|
2078
|
+
export: isExport,
|
|
2079
|
+
} = req.body;
|
|
2080
|
+
|
|
2081
|
+
if ( !clientId ) {
|
|
2082
|
+
return res.sendError( 'clientId is required', 400 );
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
const searchMatch = searchValue ?
|
|
2086
|
+
{ groupName: { $regex: searchValue, $options: 'i' } } :
|
|
2087
|
+
null;
|
|
2088
|
+
|
|
2089
|
+
const pipeline = [
|
|
2090
|
+
{ $match: { clientId } },
|
|
2091
|
+
|
|
2092
|
+
{
|
|
2093
|
+
$lookup: {
|
|
2094
|
+
from: 'taggings',
|
|
2095
|
+
let: { groupName: '$groupName' },
|
|
2096
|
+
pipeline: [
|
|
2097
|
+
{
|
|
2098
|
+
$match: {
|
|
2099
|
+
$expr: {
|
|
2100
|
+
$and: [
|
|
2101
|
+
{ $eq: [ '$groupName', '$$groupName' ] },
|
|
2102
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
2103
|
+
],
|
|
2104
|
+
},
|
|
2105
|
+
},
|
|
2106
|
+
},
|
|
2107
|
+
],
|
|
2108
|
+
as: 'tagsCount',
|
|
2109
|
+
},
|
|
2110
|
+
},
|
|
2111
|
+
|
|
2112
|
+
{
|
|
2113
|
+
$lookup: {
|
|
2114
|
+
from: 'customzonetags',
|
|
2115
|
+
let: { groupName: '$groupName' },
|
|
2116
|
+
pipeline: [
|
|
2117
|
+
{
|
|
2118
|
+
$match: {
|
|
2119
|
+
$expr: {
|
|
2120
|
+
$and: [
|
|
2121
|
+
{ $eq: [ '$groupName', '$$groupName' ] },
|
|
2122
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
2123
|
+
],
|
|
2124
|
+
},
|
|
2125
|
+
},
|
|
2126
|
+
},
|
|
2127
|
+
],
|
|
2128
|
+
as: 'zonesTagged',
|
|
2129
|
+
},
|
|
2130
|
+
},
|
|
2131
|
+
|
|
2132
|
+
{
|
|
2133
|
+
$project: {
|
|
2134
|
+
clientId: 1,
|
|
2135
|
+
groupName: 1,
|
|
2136
|
+
zonesTagged: 1,
|
|
2137
|
+
zonesCount: { $size: '$tagsCount' },
|
|
2138
|
+
createdAt: 1,
|
|
2139
|
+
},
|
|
2140
|
+
},
|
|
2141
|
+
|
|
2142
|
+
{
|
|
2143
|
+
$facet: {
|
|
2144
|
+
totalCount: [
|
|
2145
|
+
...( searchMatch ? [ { $match: searchMatch } ] : [] ),
|
|
2146
|
+
{ $count: 'count' },
|
|
2147
|
+
],
|
|
2148
|
+
|
|
2149
|
+
data: [
|
|
2150
|
+
...( searchMatch ? [ { $match: searchMatch } ] : [] ),
|
|
2151
|
+
|
|
2152
|
+
...( sortColumName && sortBy ?
|
|
2153
|
+
[ { $sort: { [sortColumName]: sortBy } } ] :
|
|
2154
|
+
[ { $sort: { createdAt: -1 } } ] ),
|
|
2155
|
+
|
|
2156
|
+
...( isExport ?
|
|
2157
|
+
[] :
|
|
2158
|
+
[
|
|
2159
|
+
{ $skip: ( offset - 1 ) * limit },
|
|
2160
|
+
{ $limit: Number( limit ) },
|
|
2161
|
+
] ),
|
|
2162
|
+
],
|
|
2163
|
+
},
|
|
2164
|
+
},
|
|
2165
|
+
];
|
|
2166
|
+
|
|
2167
|
+
const [ result ] = await customzonegrouping.aggregate( pipeline );
|
|
2168
|
+
|
|
2169
|
+
const zoneGroupList = result.data || [];
|
|
2170
|
+
|
|
2171
|
+
const totalCount = result.totalCount[0]?.count || 0;
|
|
2172
|
+
|
|
2173
|
+
// Get total count of zones for tabs (actual count, not filtered by search)
|
|
2174
|
+
const totalZoneCount = await customZoneTagService.count( { clientId } );
|
|
2175
|
+
|
|
2176
|
+
if ( isExport && zoneGroupList.length ) {
|
|
2177
|
+
const exportdata = zoneGroupList.map( ( element ) => ( {
|
|
2178
|
+
'Zone Group Name': element.groupName || '--',
|
|
2179
|
+
'Zones tagged': Array.isArray( element.zonesTagged ) ?
|
|
2180
|
+
element.zonesTagged.map( ( zone ) => zone.tagName ).filter( Boolean ).
|
|
2181
|
+
join( ', ' ) : '--',
|
|
2182
|
+
'Zones tagged Count': element.zonesCount || 0,
|
|
2183
|
+
} ) );
|
|
2184
|
+
await download( exportdata, res );
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
return res.sendSuccess( {
|
|
2189
|
+
result: zoneGroupList,
|
|
2190
|
+
totalCount,
|
|
2191
|
+
totalZoneCount,
|
|
2192
|
+
} );
|
|
2193
|
+
} catch ( e ) {
|
|
2194
|
+
logger.error( { error: e, function: 'getZoneGroupDetails' } );
|
|
2195
|
+
return res.sendError( e, 500 );
|
|
2196
|
+
}
|
|
2197
|
+
// try {
|
|
2198
|
+
// const inputData = req.body;
|
|
2199
|
+
// if ( !inputData.clientId ) {
|
|
2200
|
+
// return res.sendError( 'clientId is required', 400 );
|
|
2201
|
+
// }
|
|
2202
|
+
|
|
2203
|
+
// let Query = [
|
|
2204
|
+
// {
|
|
2205
|
+
// $match: {
|
|
2206
|
+
// clientId: inputData.clientId,
|
|
2207
|
+
// },
|
|
2208
|
+
// },
|
|
2209
|
+
// {
|
|
2210
|
+
// $lookup: {
|
|
2211
|
+
// from: 'taggings',
|
|
2212
|
+
// let: { groupName: '$groupName' },
|
|
2213
|
+
// pipeline: [
|
|
2214
|
+
// {
|
|
2215
|
+
// $match: {
|
|
2216
|
+
// $expr: {
|
|
2217
|
+
// $and: [
|
|
2218
|
+
// {
|
|
2219
|
+
// $eq: [ '$groupName', '$$groupName' ],
|
|
2220
|
+
// },
|
|
2221
|
+
// {
|
|
2222
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
2223
|
+
// },
|
|
2224
|
+
// ],
|
|
2225
|
+
// },
|
|
2226
|
+
// },
|
|
2227
|
+
// },
|
|
2228
|
+
// ], as: 'tagsCount',
|
|
2229
|
+
// },
|
|
2230
|
+
// },
|
|
2231
|
+
// {
|
|
2232
|
+
// $lookup: {
|
|
2233
|
+
// from: 'customzonetags',
|
|
2234
|
+
// let: { groupName: '$groupName' },
|
|
2235
|
+
// pipeline: [
|
|
2236
|
+
// {
|
|
2237
|
+
// $match: {
|
|
2238
|
+
// $expr: {
|
|
2239
|
+
// $and: [
|
|
2240
|
+
// {
|
|
2241
|
+
// $eq: [ '$groupName', '$$groupName' ],
|
|
2242
|
+
// },
|
|
2243
|
+
// {
|
|
2244
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
2245
|
+
// },
|
|
2246
|
+
// ],
|
|
2247
|
+
// },
|
|
2248
|
+
// },
|
|
2249
|
+
// },
|
|
2250
|
+
// ], as: 'zonesTagged',
|
|
2251
|
+
// },
|
|
2252
|
+
// },
|
|
2253
|
+
// {
|
|
2254
|
+
// $project: {
|
|
2255
|
+
// clientId: 1,
|
|
2256
|
+
// groupName: 1,
|
|
2257
|
+
// zonesTagged: 1,
|
|
2258
|
+
// zonesCount: { $size: '$tagsCount' },
|
|
2259
|
+
// },
|
|
2260
|
+
// },
|
|
2261
|
+
// ];
|
|
2262
|
+
|
|
2263
|
+
// if ( req.body.searchValue && req.body.searchValue !== '' ) {
|
|
2264
|
+
// Query.push( {
|
|
2265
|
+
// $match: {
|
|
2266
|
+
// $or: [
|
|
2267
|
+
// { groupName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
2268
|
+
// ],
|
|
2269
|
+
// },
|
|
2270
|
+
// } );
|
|
2271
|
+
// }
|
|
2272
|
+
|
|
2273
|
+
// if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
2274
|
+
// Query.push( {
|
|
2275
|
+
// $sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
2276
|
+
// } );
|
|
2277
|
+
// } else {
|
|
2278
|
+
// // Default sort by createdAt descending if no sort specified
|
|
2279
|
+
// Query.push( {
|
|
2280
|
+
// $sort: { createdAt: -1 },
|
|
2281
|
+
// } );
|
|
2282
|
+
// }
|
|
2283
|
+
|
|
2284
|
+
// // Get total count before pagination
|
|
2285
|
+
// const countQuery = [ ...Query ];
|
|
2286
|
+
// const totalCountResult = await customzonegrouping.aggregate( [
|
|
2287
|
+
// ...countQuery,
|
|
2288
|
+
// { $count: 'total' },
|
|
2289
|
+
// ] );
|
|
2290
|
+
// console.log( '🚀 ~ getZoneGroupDetails ~ totalCountResult:', totalCountResult );
|
|
2291
|
+
// const totalCount = totalCountResult.length > 0 ? totalCountResult[0].total : 0;
|
|
2292
|
+
|
|
2293
|
+
// if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
2294
|
+
// Query.push(
|
|
2295
|
+
// { $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
2296
|
+
// { $limit: Number( req.body.limit ) },
|
|
2297
|
+
// );
|
|
2298
|
+
// }
|
|
2299
|
+
|
|
2300
|
+
// const zoneGroupList = await customzonegrouping.aggregate( Query );
|
|
2301
|
+
|
|
2302
|
+
// if ( req.body.export && zoneGroupList.length > 0 ) {
|
|
2303
|
+
// const exportdata = [];
|
|
2304
|
+
// zoneGroupList.forEach( ( element ) => {
|
|
2305
|
+
// const data = {
|
|
2306
|
+
// 'Zone Group Name': element.groupName || '--',
|
|
2307
|
+
// 'Zones tagged': Array.isArray( element.zonesTagged ) ? element.zonesTagged.join( ', ' ) : '--',
|
|
2308
|
+
// 'Zones tagged Count': element.zonesCount || 0,
|
|
2309
|
+
// // 'Stores Tagged Count': element.storesTaggedCount || 0,
|
|
2310
|
+
// };
|
|
2311
|
+
// exportdata.push( data );
|
|
2312
|
+
// } );
|
|
2313
|
+
// await download( exportdata, res );
|
|
2314
|
+
// return;
|
|
2315
|
+
// }
|
|
2316
|
+
|
|
2317
|
+
// return res.sendSuccess( {
|
|
2318
|
+
// result: zoneGroupList,
|
|
2319
|
+
// count: totalCount,
|
|
2320
|
+
// } );
|
|
2321
|
+
// } catch ( e ) {
|
|
2322
|
+
// logger.error( { error: e, function: 'getZoneGroupDetails' } );
|
|
2323
|
+
// console.error( 'getZoneGroupDetails error:', e );
|
|
2324
|
+
// return res.sendError( e, 500 );
|
|
2325
|
+
// }
|
|
2326
|
+
};
|
|
2327
|
+
|
|
2328
|
+
export const addZoneGroup = async ( req, res ) => {
|
|
2329
|
+
try {
|
|
2330
|
+
const { clientId, groupName, zonesTagged = [], productName = '' } = req.body;
|
|
2331
|
+
|
|
2332
|
+
if ( !clientId || !groupName ) {
|
|
2333
|
+
return res.sendError( 'clientId and groupName are required', 400 );
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
const payload = {
|
|
2337
|
+
clientId,
|
|
2338
|
+
groupName,
|
|
2339
|
+
zonesTagged: Array.isArray( zonesTagged ) ? zonesTagged : [],
|
|
2340
|
+
productName,
|
|
2341
|
+
};
|
|
2342
|
+
|
|
2343
|
+
await customzonegrouping.create( payload );
|
|
2344
|
+
|
|
2345
|
+
// Update groupName for all zone tags in zonesTagged array
|
|
2346
|
+
if ( Array.isArray( zonesTagged ) && zonesTagged.length > 0 ) {
|
|
2347
|
+
// Update all tags that are in the zonesTagged array to have this groupName
|
|
2348
|
+
await customZoneTagService.updateMany(
|
|
2349
|
+
{
|
|
2350
|
+
clientId,
|
|
2351
|
+
tagName: { $in: zonesTagged },
|
|
2352
|
+
},
|
|
2353
|
+
{ $set: { groupName } },
|
|
2354
|
+
);
|
|
2355
|
+
await taggingService.updateMany(
|
|
2356
|
+
{
|
|
2357
|
+
clientId,
|
|
2358
|
+
tagName: { $in: zonesTagged },
|
|
2359
|
+
},
|
|
2360
|
+
{ $set: { groupName } },
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
logger.info( 'Zone Group Created Successfully' );
|
|
2365
|
+
|
|
2366
|
+
const logObj = {
|
|
2367
|
+
clientId,
|
|
2368
|
+
userName: req.user?.userName,
|
|
2369
|
+
email: req.user?.email,
|
|
2370
|
+
date: new Date(),
|
|
2371
|
+
logType: 'zone',
|
|
2372
|
+
logSubType: 'addZoneGroup',
|
|
2373
|
+
changes: [ `${groupName} zone group` ],
|
|
2374
|
+
eventType: 'create',
|
|
2375
|
+
showTo: [ 'client', 'tango' ],
|
|
2376
|
+
};
|
|
2377
|
+
|
|
2378
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2379
|
+
return res.sendSuccess( 'Zone Group Created Successfully' );
|
|
2380
|
+
} catch ( e ) {
|
|
2381
|
+
// Duplicate key error from unique index
|
|
2382
|
+
if ( e.code === 11000 ) {
|
|
2383
|
+
return res.sendError( `groupName "${req.body.groupName}" already exists for this client`, 409 );
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
logger.error( { error: e, function: 'addZoneGroup' } );
|
|
2387
|
+
return res.sendError( e.message || 'Internal Server Error', 500 );
|
|
2388
|
+
}
|
|
2389
|
+
};
|
|
2390
|
+
|
|
2391
|
+
export const updateZoneGroup = async ( req, res ) => {
|
|
2392
|
+
try {
|
|
2393
|
+
const { _id, clientId, groupName, productName, zonesTagged = [] } = req.body;
|
|
2394
|
+
|
|
2395
|
+
if ( !clientId || !groupName ) {
|
|
2396
|
+
return res.sendError( 'clientId and groupName are required', 400 );
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
let findGroup = await customzonegrouping.findOne( { _id: _id } );
|
|
2400
|
+
const existingGroup = await customzonegrouping.findOne( {
|
|
2401
|
+
clientId: clientId,
|
|
2402
|
+
groupName: groupName,
|
|
2403
|
+
_id: { $ne: _id },
|
|
2404
|
+
} );
|
|
2405
|
+
if ( existingGroup && existingGroup.groupName ) {
|
|
2406
|
+
return res.sendError( `groupName "${existingGroup.groupName}" already exists for this client`, 409 );
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
if ( findGroup ) {
|
|
2410
|
+
let removedTags = findGroup.zonesTagged.filter( ( zone ) => !zonesTagged.includes( zone ) ); ;
|
|
2411
|
+
if ( removedTags && removedTags.length > 0 ) {
|
|
2412
|
+
let updateQuery = { clientId, tagName: { $in: removedTags } };
|
|
2413
|
+
await customZoneTagService.updateMany( updateQuery, { $set: { groupName: null } } );
|
|
2414
|
+
await taggingService.updateMany( updateQuery, { $set: { groupName: null } } );
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
|
|
2419
|
+
await customzonegrouping.updateOne( { _id: _id }, { groupName: groupName, zonesTagged: zonesTagged } );
|
|
2420
|
+
let updateQuery = { clientId, tagName: { $in: zonesTagged } };
|
|
2421
|
+
await customZoneTagService.updateMany( updateQuery, { $set: { groupName } } );
|
|
2422
|
+
await taggingService.updateMany( updateQuery, { $set: { groupName } } );
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
const logObj = {
|
|
2426
|
+
clientId: clientId,
|
|
2427
|
+
userName: req.user?.userName,
|
|
2428
|
+
email: req.user?.email,
|
|
2429
|
+
date: new Date(),
|
|
2430
|
+
logType: 'zone',
|
|
2431
|
+
logSubType: 'updateZoneGroup',
|
|
2432
|
+
changes: [ `zone group updated from "${findGroup.groupName || groupName}" to "${groupName}"` ],
|
|
2433
|
+
eventType: 'update',
|
|
2434
|
+
previous: {
|
|
2435
|
+
groupName: findGroup.groupName || groupName,
|
|
2436
|
+
productName: findGroup.productName,
|
|
2437
|
+
zonesTagged: findGroup.zonesTagged,
|
|
2438
|
+
},
|
|
2439
|
+
current: {
|
|
2440
|
+
groupName: groupName,
|
|
2441
|
+
productName: productName,
|
|
2442
|
+
zonesTagged: zonesTagged,
|
|
2443
|
+
},
|
|
2444
|
+
oldData: {
|
|
2445
|
+
GroupName: findGroup.groupName || groupName,
|
|
2446
|
+
},
|
|
2447
|
+
newData: {
|
|
2448
|
+
GroupName: groupName,
|
|
2449
|
+
},
|
|
2450
|
+
showTo: [ 'client', 'tango' ],
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2454
|
+
|
|
2455
|
+
return res.sendSuccess( 'Zone Group Updated Successfully' );
|
|
2456
|
+
} catch ( e ) {
|
|
2457
|
+
// Duplicate key error from unique index
|
|
2458
|
+
if ( e.code === 11000 ) {
|
|
2459
|
+
return res.sendError( `groupName "${req.body.groupName}" already exists for this client`, 409 );
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
logger.error( { error: e, function: 'updateZoneGroup' } );
|
|
2463
|
+
return res.sendError( e.message || 'Internal Server Error', 500 );
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
|
|
2467
|
+
export const uploadBulkZoneGroup = async ( req, res ) => {
|
|
2468
|
+
try {
|
|
2469
|
+
const groupsArray = req.body;
|
|
2470
|
+
const clientId = req.bulkZoneClientId;
|
|
2471
|
+
|
|
2472
|
+
if ( !groupsArray || !Array.isArray( groupsArray ) || groupsArray.length === 0 ) {
|
|
2473
|
+
return res.sendError( 'group-config file must not be empty', 400 );
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if ( !clientId ) {
|
|
2477
|
+
return res.sendError( 'clientId is required', 400 );
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
const zoneGroupDataList = [];
|
|
2481
|
+
const createdGroupNames = [];
|
|
2482
|
+
|
|
2483
|
+
// Process each group in the array
|
|
2484
|
+
for ( const group of groupsArray ) {
|
|
2485
|
+
if ( !group.groupName || group.groupName === '' ) {
|
|
2486
|
+
continue; // Skip groups without groupName
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
const zoneGroupData = {
|
|
2490
|
+
clientId: clientId,
|
|
2491
|
+
groupName: group.groupName,
|
|
2492
|
+
productName: group.productName,
|
|
2493
|
+
};
|
|
2494
|
+
zoneGroupDataList.push( zoneGroupData );
|
|
2495
|
+
createdGroupNames.push( group.groupName );
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// Create all zone groups
|
|
2499
|
+
if ( zoneGroupDataList.length > 0 ) {
|
|
2500
|
+
await customzonegrouping.create( zoneGroupDataList );
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
logger.info( `Bulk Zone Groups Created Successfully: ${createdGroupNames.length} groups` );
|
|
2504
|
+
const logObj = {
|
|
2505
|
+
clientId: clientId,
|
|
2506
|
+
userName: req.user?.userName,
|
|
2507
|
+
email: req.user?.email,
|
|
2508
|
+
date: new Date(),
|
|
2509
|
+
logType: 'zone',
|
|
2510
|
+
logSubType: 'addBulkZoneGroup',
|
|
2511
|
+
changes: createdGroupNames.map( ( groupName ) => `${groupName} zone group` ),
|
|
2512
|
+
eventType: 'create',
|
|
2513
|
+
showTo: [ 'client', 'tango' ],
|
|
2514
|
+
};
|
|
2515
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2516
|
+
return res.sendSuccess( {
|
|
2517
|
+
message: `Zone Groups Created Successfully`,
|
|
2518
|
+
count: createdGroupNames.length,
|
|
2519
|
+
} );
|
|
2520
|
+
} catch ( e ) {
|
|
2521
|
+
logger.error( { error: e, function: 'uploadBulkZoneGroup' } );
|
|
2522
|
+
return res.sendError( 'Failed to upload group-config file', 500 );
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2526
|
+
export const deleteZoneGroup = async ( req, res ) => {
|
|
2527
|
+
try {
|
|
2528
|
+
let zoneGroupDetails = await customzonegrouping.findOne( { _id: req.body._id, clientId: req.body.clientId, groupName: req.body.groupName } );
|
|
2529
|
+
if ( !zoneGroupDetails || zoneGroupDetails?.length == 0 ) {
|
|
2530
|
+
return res.sendError( 'no data found', 204 );
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
// Remove this group mapping from all custom zone tags (zones) that are linked to it
|
|
2534
|
+
await customZoneTagService.updateMany(
|
|
2535
|
+
{
|
|
2536
|
+
clientId: req.body.clientId,
|
|
2537
|
+
groupName: req.body.groupName,
|
|
2538
|
+
},
|
|
2539
|
+
{ $set: { groupName: null } },
|
|
2540
|
+
);
|
|
2541
|
+
await taggingService.updateMany(
|
|
2542
|
+
{
|
|
2543
|
+
clientId: req.body.clientId,
|
|
2544
|
+
groupName: req.body.groupName,
|
|
2545
|
+
},
|
|
2546
|
+
{ $set: { groupName: null } },
|
|
2547
|
+
);
|
|
2548
|
+
await customzonegrouping.deleteOne( { _id: req.body._id, clientId: req.body.clientId, groupName: req.body.groupName } );
|
|
2549
|
+
|
|
2550
|
+
const logObj = {
|
|
2551
|
+
clientId: req.body?.clientId,
|
|
2552
|
+
userName: req.user?.userName,
|
|
2553
|
+
email: req.user?.email,
|
|
2554
|
+
date: new Date(),
|
|
2555
|
+
logType: 'zone',
|
|
2556
|
+
logSubType: 'deleteZoneGroup',
|
|
2557
|
+
changes: [ `${req.body.groupName} zone group` ],
|
|
2558
|
+
eventType: 'delete',
|
|
2559
|
+
showTo: [ 'client', 'tango' ],
|
|
2560
|
+
};
|
|
2561
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2562
|
+
// await updatezoneTagging( req, res );
|
|
2563
|
+
return res.sendSuccess( 'Zone Group Deleted Successfully' );
|
|
2564
|
+
} catch ( e ) {
|
|
2565
|
+
logger.error( { error: e, function: 'deleteZoneGroup' } );
|
|
2566
|
+
return res.sendError( e, 500 );
|
|
2567
|
+
}
|
|
2568
|
+
};
|
|
2569
|
+
|
|
2570
|
+
export async function oldTagsMigration() {
|
|
2571
|
+
let uniqueTags = await taggingService.find( { productName: 'tangoZone' } );
|
|
2572
|
+
const result = _.uniqBy( uniqueTags, 'tagName' );
|
|
2573
|
+
|
|
2574
|
+
|
|
2575
|
+
for ( let zone of result ) {
|
|
2576
|
+
let obj = {
|
|
2577
|
+
clientId: zone.clientId,
|
|
2578
|
+
tagName: zone.tagName,
|
|
2579
|
+
productName: zone.productName,
|
|
2580
|
+
rgbColor: zone.rgbColor,
|
|
2581
|
+
rgbBorderColor: zone.rgbBorderColor,
|
|
2582
|
+
groupName: null,
|
|
2583
|
+
};
|
|
2584
|
+
await customZoneTagService.create( [ obj ] );
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|