tango-app-api-analysis-traffic 3.8.7-vms.3 → 3.8.7-vms.31
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 +2 -2
- package/src/controllers/revop.controller.js +787 -38
- package/src/controllers/tangoTrafficV3.controllers.js +27 -26
- package/src/dtos/revop.dtos.js +13 -2
- package/src/dtos/validation.dtos.js +11 -0
- package/src/routes/revop.routes.js +6 -5
- package/src/routes/traffic.routes.js +3 -1
- package/src/validations/revop.validation.js +229 -111
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger, insertOpenSearchData, getOpenSearchData, updateOpenSearchData } from 'tango-app-api-middleware';
|
|
2
2
|
import { findOnerevopConfig } from '../services/revopConfig.service.js';
|
|
3
3
|
import * as clientService from '../services/clients.services.js';
|
|
4
|
-
import { bulkUpdate, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
4
|
+
import { bulkUpdate, insertWithId, searchOpenSearchData, scrollResponse, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
5
5
|
import { findOneVmsStoreRequest } from '../services/vmsStoreRequest.service.js';
|
|
6
6
|
// import dayjs from 'dayjs';
|
|
7
7
|
// Lamda Service Call //
|
|
@@ -137,6 +137,217 @@ export async function getrevoptagging( req, res ) {
|
|
|
137
137
|
return res.sendError( { error: error }, 500 );
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
export async function migrateRevopIndex( req, res ) {
|
|
142
|
+
try {
|
|
143
|
+
const { storeId, dateString, size = 500 } = req.body;
|
|
144
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
145
|
+
|
|
146
|
+
const query = {
|
|
147
|
+
size: size,
|
|
148
|
+
query: {
|
|
149
|
+
bool: {
|
|
150
|
+
must: [],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if ( storeId ) {
|
|
156
|
+
query.query.bool.must.push( {
|
|
157
|
+
term: {
|
|
158
|
+
'storeId.keyword': storeId,
|
|
159
|
+
},
|
|
160
|
+
} );
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if ( dateString ) {
|
|
164
|
+
query.query.bool.must.push( {
|
|
165
|
+
terms: {
|
|
166
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
167
|
+
},
|
|
168
|
+
} );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const response = await getOpenSearchData( openSearch.revop, query );
|
|
172
|
+
const hits = response?.body?.hits?.hits || [];
|
|
173
|
+
|
|
174
|
+
if ( hits.length === 0 ) {
|
|
175
|
+
return res.sendSuccess( { message: 'No records found for migration', updated: 0 } );
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const bulkBody = [];
|
|
179
|
+
|
|
180
|
+
for ( const hit of hits ) {
|
|
181
|
+
const src = hit._source || {};
|
|
182
|
+
const statusValue = ( src.status || '' ).toLowerCase();
|
|
183
|
+
const parentValue = src.parent;
|
|
184
|
+
const idValue = `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${src.tempId || ''}`;
|
|
185
|
+
|
|
186
|
+
let actions = [
|
|
187
|
+
{
|
|
188
|
+
actionType: 'tagging',
|
|
189
|
+
action: 'submitted',
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
if ( statusValue === 'approved' ) {
|
|
194
|
+
actions = [
|
|
195
|
+
{
|
|
196
|
+
actionType: 'tagging',
|
|
197
|
+
action: 'submitted',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
actionType: 'review',
|
|
201
|
+
action: 'approved',
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
} else if ( statusValue === 'rejected' ) {
|
|
205
|
+
actions = [
|
|
206
|
+
{
|
|
207
|
+
actionType: 'tagging',
|
|
208
|
+
action: 'submitted',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
actionType: 'review',
|
|
212
|
+
action: 'rejected',
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const doc = {
|
|
218
|
+
id: idValue,
|
|
219
|
+
isParent: parentValue === null || parentValue === undefined ? false : true,
|
|
220
|
+
actions,
|
|
221
|
+
ticketStatus: src.status,
|
|
222
|
+
// updatedAt: new Date(),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
bulkBody.push(
|
|
226
|
+
{ update: { _index: openSearch.newRevop, _id: hit._id } },
|
|
227
|
+
{ doc: doc, doc_as_upsert: true },
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
232
|
+
if ( bulkRes?.errors ) {
|
|
233
|
+
logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
234
|
+
return res.sendError( 'Failed to migrate some records', 500 );
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return res.sendSuccess( { message: 'Migration completed', updated: hits.length } );
|
|
238
|
+
} catch ( error ) {
|
|
239
|
+
logger.error( { error: error, message: req.body, function: 'migrateRevopIndex' } );
|
|
240
|
+
return res.sendError( { error: error }, 500 );
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function expireReviewStatus( req, res ) {
|
|
245
|
+
try {
|
|
246
|
+
const {
|
|
247
|
+
thresholdDate = '2025-12-20',
|
|
248
|
+
batchSize = 500,
|
|
249
|
+
storeId,
|
|
250
|
+
dateString,
|
|
251
|
+
} = req.body;
|
|
252
|
+
logger.info( { inputData: req.body, msg: '........1' } );
|
|
253
|
+
const cutoffDate = new Date( thresholdDate );
|
|
254
|
+
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
255
|
+
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
260
|
+
const query = {
|
|
261
|
+
size: batchSize,
|
|
262
|
+
query: {
|
|
263
|
+
bool: {
|
|
264
|
+
must: [
|
|
265
|
+
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
266
|
+
{ term: { 'type.keyword': 'store' } },
|
|
267
|
+
],
|
|
268
|
+
must_not: [
|
|
269
|
+
{ terms: { 'status.keyword': [ 'Closed' ] } },
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if ( storeId ) {
|
|
277
|
+
query.query.bool.must.push( { term: { 'storeId.keyword': storeId } } );
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if ( dateString ) {
|
|
282
|
+
query.query.bool.must.push( {
|
|
283
|
+
terms: {
|
|
284
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
285
|
+
},
|
|
286
|
+
} );
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
let totalUpdated = 0;
|
|
291
|
+
let scrollId = null;
|
|
292
|
+
|
|
293
|
+
let firstResponse = await searchOpenSearchData( openSearch.footfallDirectory, query );
|
|
294
|
+
let hitsBatch = firstResponse?.body?.hits?.hits || [];
|
|
295
|
+
logger.info( { hitsBatch } );
|
|
296
|
+
scrollId = firstResponse?.body?._scroll_id;
|
|
297
|
+
|
|
298
|
+
while ( hitsBatch.length > 0 ) {
|
|
299
|
+
const bulkBody = [];
|
|
300
|
+
|
|
301
|
+
for ( const hit of hitsBatch ) {
|
|
302
|
+
const src = hit._source || {};
|
|
303
|
+
const mappingInfo = Array.isArray( src.mappingInfo ) ? src.mappingInfo : [];
|
|
304
|
+
let changed = false;
|
|
305
|
+
const updatedMapping = mappingInfo.map( ( item ) => {
|
|
306
|
+
if ( item?.type === 'review'&& item?.type !== 'Closed' && item?.dueDate ) {
|
|
307
|
+
const due = new Date( item.dueDate );
|
|
308
|
+
logger.info( { due, msg: '..........1', cutoffDate } );
|
|
309
|
+
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
310
|
+
changed = true;
|
|
311
|
+
return { ...item, status: 'Expired' };
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return item;
|
|
315
|
+
} );
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
if ( changed ) {
|
|
319
|
+
const doc = {
|
|
320
|
+
mappingInfo: updatedMapping,
|
|
321
|
+
status: 'Expired',
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
bulkBody.push(
|
|
325
|
+
{ update: { _index: openSearch.footfallDirectory, _id: hit._id } },
|
|
326
|
+
{ doc: doc, doc_as_upsert: true },
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if ( bulkBody.length > 0 ) {
|
|
332
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
333
|
+
if ( bulkRes?.errors ) {
|
|
334
|
+
logger.error( { message: 'Bulk expire errors', items: bulkRes.items } );
|
|
335
|
+
}
|
|
336
|
+
totalUpdated += bulkBody.length / 2;
|
|
337
|
+
}
|
|
338
|
+
logger.info( { totalUpdated, msg: '........9' } );
|
|
339
|
+
if ( !scrollId ) break;
|
|
340
|
+
const nextRes = await scrollResponse( scrollId );
|
|
341
|
+
hitsBatch = nextRes?.body?.hits?.hits || [];
|
|
342
|
+
scrollId = nextRes?.body?._scroll_id;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return res.sendSuccess( { message: 'Expired review status updated', updated: totalUpdated } );
|
|
346
|
+
} catch ( error ) {
|
|
347
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
348
|
+
return res.sendError( { error: error }, 500 );
|
|
349
|
+
}
|
|
350
|
+
}
|
|
140
351
|
export async function revoptaggingcount( req, res ) {
|
|
141
352
|
try {
|
|
142
353
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
@@ -221,6 +432,9 @@ export async function storeProcessedData( req, res ) {
|
|
|
221
432
|
dayjs.extend( isSameOrBefore );
|
|
222
433
|
|
|
223
434
|
let start = dayjs( fromDate );
|
|
435
|
+
// get start value from the before day one
|
|
436
|
+
// Move start one day back so we can access "day before" when looping
|
|
437
|
+
start = start.subtract( 1, 'day' );
|
|
224
438
|
let end = dayjs( toDate );
|
|
225
439
|
|
|
226
440
|
if ( !start.isValid() || !end.isValid() ) {
|
|
@@ -267,10 +481,12 @@ export async function storeProcessedData( req, res ) {
|
|
|
267
481
|
|
|
268
482
|
const responseArray = [];
|
|
269
483
|
|
|
270
|
-
for ( let i =
|
|
484
|
+
for ( let i = 1; i < orderedDates.length; i++ ) {
|
|
271
485
|
const currentDate = orderedDates[i];
|
|
272
486
|
const currentId = `${storeId}_${currentDate}`;
|
|
487
|
+
logger.info( { currentId, currentDate } );
|
|
273
488
|
const processedData = hitsMap.get( currentId );
|
|
489
|
+
logger.info( { processedData } );
|
|
274
490
|
if ( !processedData ) {
|
|
275
491
|
responseArray.push( {
|
|
276
492
|
date: currentDate,
|
|
@@ -283,13 +499,17 @@ export async function storeProcessedData( req, res ) {
|
|
|
283
499
|
|
|
284
500
|
const prevDate = dayjs( currentDate ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
|
|
285
501
|
const prevId = `${storeId}_${prevDate}`;
|
|
502
|
+
logger.info( { prevId, prevDate } );
|
|
286
503
|
const previousData = hitsMap.get( prevId );
|
|
287
504
|
|
|
288
505
|
let footfallCountTrend = 0;
|
|
506
|
+
logger.info( { previousData, previoucubr1: previousData?.footfall_count } );
|
|
289
507
|
if ( previousData && previousData.footfall_count ) {
|
|
508
|
+
logger.info( { previousData, previoucubr: previousData?.footfall_count } );
|
|
290
509
|
footfallCountTrend = Math.round(
|
|
291
|
-
( ( processedData.footfall_count - previousData
|
|
510
|
+
( ( processedData.footfall_count - previousData?.footfall_count ) / previousData.footfall_count ) * 100,
|
|
292
511
|
);
|
|
512
|
+
logger.info( { footfallCountTrend } );
|
|
293
513
|
}
|
|
294
514
|
// Add ticket status from openSearch.footfallDirectory (_source.status)
|
|
295
515
|
let ticketStatus = null;
|
|
@@ -302,6 +522,11 @@ export async function storeProcessedData( req, res ) {
|
|
|
302
522
|
{ term: { 'storeId.keyword': storeId } },
|
|
303
523
|
{ term: { 'dateString': currentDate } },
|
|
304
524
|
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
525
|
+
{
|
|
526
|
+
'term': {
|
|
527
|
+
'type.keyword': 'store',
|
|
528
|
+
},
|
|
529
|
+
},
|
|
305
530
|
],
|
|
306
531
|
},
|
|
307
532
|
},
|
|
@@ -403,43 +628,177 @@ export async function footFallImages( req, res ) {
|
|
|
403
628
|
'ticketName.keyword': 'footfall-directory',
|
|
404
629
|
},
|
|
405
630
|
},
|
|
631
|
+
{
|
|
632
|
+
'term': {
|
|
633
|
+
'type.keyword': 'store',
|
|
634
|
+
},
|
|
635
|
+
},
|
|
406
636
|
],
|
|
407
637
|
},
|
|
408
638
|
},
|
|
409
|
-
|
|
639
|
+
'_source': [ 'dateString', 'storeId', 'mappingInfo', 'revicedFootfall', 'revicedPerc', 'reviced', 'createdAt', 'updatedAt', 'footfallCount' ],
|
|
410
640
|
|
|
411
641
|
};
|
|
412
642
|
|
|
413
643
|
const getData = await getOpenSearchData( opensearch.footfallDirectory, query );
|
|
414
644
|
const ticketDetails = getData?.body?.hits?.hits[0];
|
|
415
645
|
let temp = [];
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
646
|
+
const footfallValue = ticketDetails?._source?.footfallCount ?? 0;
|
|
647
|
+
const mappingInfoArray = ticketDetails?._source?.mappingInfo ?? [];
|
|
648
|
+
// Helper to get mappingInfo for an actionType
|
|
649
|
+
function getMappingForType( type ) {
|
|
650
|
+
return mappingInfoArray.find( ( m ) => m.type === type ) || {};
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// List of actionTypes to process in sequence
|
|
654
|
+
|
|
655
|
+
if ( req.user.userType !== 'tango' && req.user.role !== 'superadmin' ) {
|
|
656
|
+
switch ( req.user.role ) {
|
|
657
|
+
case 'user':
|
|
658
|
+
const actionTypesUser = [ 'tagging', 'finalRevision' ];
|
|
659
|
+
|
|
660
|
+
temp = [];
|
|
661
|
+
actionTypesUser.forEach( ( type ) => {
|
|
662
|
+
const mapping = getMappingForType( type );
|
|
663
|
+
if ( type === 'tagging' ) {
|
|
664
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
665
|
+
const revisedPerc =
|
|
666
|
+
footfallValue > 0 ?
|
|
667
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
668
|
+
'0';
|
|
669
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
670
|
+
temp.push( {
|
|
671
|
+
actionType: type,
|
|
672
|
+
footfall: footfallValue,
|
|
673
|
+
revicedFootfall: revisedFootfall,
|
|
674
|
+
revicedPerc: revisedPerc,
|
|
675
|
+
count: countObj,
|
|
676
|
+
createdAt: mapping.createdAt ?? '',
|
|
677
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
678
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
679
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
680
|
+
isUp: false,
|
|
681
|
+
} );
|
|
682
|
+
} else if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
683
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
684
|
+
const revisedPerc =
|
|
685
|
+
footfallValue > 0 ?
|
|
686
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
687
|
+
'0';
|
|
688
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
689
|
+
temp.push( {
|
|
690
|
+
actionType: type,
|
|
691
|
+
footfall: footfallValue,
|
|
692
|
+
revicedFootfall: revisedFootfall,
|
|
693
|
+
revicedPerc: revisedPerc,
|
|
694
|
+
count: countObj,
|
|
695
|
+
createdAt: mapping.createdAt ?? '',
|
|
696
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
697
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
698
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
699
|
+
} );
|
|
700
|
+
}
|
|
701
|
+
} );
|
|
702
|
+
break;
|
|
703
|
+
case 'admin':
|
|
704
|
+
const actionTypesAdmin = [ 'tagging', 'review', 'finalRevision' ];
|
|
705
|
+
temp = [];
|
|
706
|
+
actionTypesAdmin.forEach( ( type ) => {
|
|
707
|
+
const mapping = getMappingForType( type );
|
|
708
|
+
if ( type === 'tagging' ) {
|
|
709
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
710
|
+
const revisedPerc =
|
|
711
|
+
footfallValue > 0 ?
|
|
712
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
713
|
+
'0';
|
|
714
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
715
|
+
temp.push( {
|
|
716
|
+
actionType: type,
|
|
717
|
+
footfall: footfallValue,
|
|
718
|
+
revicedFootfall: revisedFootfall,
|
|
719
|
+
revicedPerc: revisedPerc,
|
|
720
|
+
count: countObj,
|
|
721
|
+
createdAt: mapping.createdAt ?? '',
|
|
722
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
723
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
724
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
725
|
+
isUp: false,
|
|
726
|
+
} );
|
|
727
|
+
} else if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
728
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
729
|
+
const revisedPerc =
|
|
730
|
+
footfallValue > 0 ?
|
|
731
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
732
|
+
'0';
|
|
733
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
734
|
+
temp.push( {
|
|
735
|
+
actionType: type,
|
|
736
|
+
footfall: footfallValue,
|
|
737
|
+
revicedFootfall: revisedFootfall,
|
|
738
|
+
revicedPerc: revisedPerc,
|
|
739
|
+
count: countObj,
|
|
740
|
+
createdAt: mapping.createdAt ?? '',
|
|
741
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
742
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
743
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
744
|
+
} );
|
|
745
|
+
}
|
|
746
|
+
} );
|
|
747
|
+
}
|
|
748
|
+
} else {
|
|
749
|
+
const actionTypes = [ 'tagging', 'review', 'approve', 'tangoreview', 'finalRevision' ];
|
|
750
|
+
|
|
751
|
+
// Dynamically add to temp only if actionType matches and status is 'closed',
|
|
752
|
+
// except for 'tagging' where status must be 'raised'
|
|
753
|
+
temp = [];
|
|
754
|
+
actionTypes.forEach( ( type ) => {
|
|
755
|
+
const mapping = getMappingForType( type );
|
|
756
|
+
if ( type === 'tagging' ) {
|
|
757
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
758
|
+
temp.push( {
|
|
759
|
+
actionType: type,
|
|
760
|
+
footfall: footfallValue,
|
|
761
|
+
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
762
|
+
revicedPerc: mapping.revicedPerc ?? '--',
|
|
763
|
+
count: countObj,
|
|
764
|
+
createdAt: mapping.createdAt ?? '',
|
|
765
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
766
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
767
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
768
|
+
isUp: false,
|
|
769
|
+
} );
|
|
770
|
+
}
|
|
771
|
+
if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
772
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
773
|
+
temp.push( {
|
|
774
|
+
actionType: type,
|
|
775
|
+
footfall: footfallValue,
|
|
776
|
+
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
777
|
+
revicedPerc: mapping.revicedPerc ?? '--',
|
|
778
|
+
count: countObj,
|
|
779
|
+
createdAt: mapping.createdAt ?? '',
|
|
780
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
781
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
782
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
783
|
+
} );
|
|
784
|
+
}
|
|
785
|
+
} );
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
const LamdaURL = revop.getImages;
|
|
790
|
+
let resultData = await LamdaServiceCall( LamdaURL, inputData );
|
|
791
|
+
if ( resultData ) {
|
|
792
|
+
// temp.length? temp[0].status = 'open': null;
|
|
793
|
+
|
|
794
|
+
if ( resultData.status_code == '200' ) {
|
|
795
|
+
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
796
|
+
} else {
|
|
797
|
+
return res.sendError( 'No Content', 204 );
|
|
798
|
+
}
|
|
799
|
+
} else {
|
|
800
|
+
return res.sendError( 'No Content', 204 );
|
|
801
|
+
}
|
|
443
802
|
} catch ( error ) {
|
|
444
803
|
logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
|
|
445
804
|
const err = error.message || 'Internal Server Error';
|
|
@@ -464,9 +823,21 @@ export async function tagTempId( req, res ) {
|
|
|
464
823
|
exitTime: inputData.exitTime,
|
|
465
824
|
filePath: inputData.filePath,
|
|
466
825
|
status: inputData?.revopsType == 'non-tagging' ?'':'submitted',
|
|
467
|
-
description: '',
|
|
468
|
-
isChecked:
|
|
469
|
-
|
|
826
|
+
description: inputData.comments || '',
|
|
827
|
+
isChecked: null,
|
|
828
|
+
// Add id to each object in duplicateImage if it exists and is an array
|
|
829
|
+
duplicateImage: Array.isArray( inputData?.duplicateImage ) ?
|
|
830
|
+
inputData.duplicateImage.map( ( img ) => ( {
|
|
831
|
+
...img,
|
|
832
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${img?.tempId}`,
|
|
833
|
+
actions: [
|
|
834
|
+
{
|
|
835
|
+
actionType: 'tagging',
|
|
836
|
+
action: 'submitted',
|
|
837
|
+
},
|
|
838
|
+
],
|
|
839
|
+
} ) ) :
|
|
840
|
+
[],
|
|
470
841
|
type: 'tagging-reflect',
|
|
471
842
|
ticketStatus: 'submitted',
|
|
472
843
|
isParent: inputData?.revopsType === 'duplicate'? true : false,
|
|
@@ -476,6 +847,7 @@ export async function tagTempId( req, res ) {
|
|
|
476
847
|
action: 'submitted',
|
|
477
848
|
},
|
|
478
849
|
],
|
|
850
|
+
comments: inputData.comments || '',
|
|
479
851
|
createdAt: new Date(),
|
|
480
852
|
updatedAt: new Date(),
|
|
481
853
|
|
|
@@ -491,8 +863,10 @@ export async function tagTempId( req, res ) {
|
|
|
491
863
|
ctx._source.description = params.description;
|
|
492
864
|
ctx._source.isChecked = params.isChecked;
|
|
493
865
|
ctx._source.duplicateImage = params.duplicateImage;
|
|
866
|
+
ctx._source.isParent = params.isParent;
|
|
494
867
|
ctx._source.updatedAt = params.updatedAt;
|
|
495
868
|
ctx._source.timeRange = params.timeRange;
|
|
869
|
+
ctx._source.comments = params.comments;
|
|
496
870
|
if (ctx._source.createdAt == null) {
|
|
497
871
|
ctx._source.createdAt = params.createdAt;
|
|
498
872
|
ctx._source.clientId = params.clientId;
|
|
@@ -507,6 +881,32 @@ export async function tagTempId( req, res ) {
|
|
|
507
881
|
};
|
|
508
882
|
const id = `${inputData.storeId}_${inputData.dateString}_${inputData.timeRange}_${inputData.tempId}`;
|
|
509
883
|
await upsertWithScript( openSearch.revop, id, { script, upsert: upsertRecord } );
|
|
884
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
885
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
886
|
+
const logs = {
|
|
887
|
+
type: 'tagging',
|
|
888
|
+
parent: inputData?.revopsType == 'duplicate'? inputData.tempId : null,
|
|
889
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
890
|
+
tempId: inputData.tempId,
|
|
891
|
+
timeRange: inputData.timeRange,
|
|
892
|
+
storeId: inputData.storeId,
|
|
893
|
+
dateString: inputData.dateString,
|
|
894
|
+
processType: inputData.processType,
|
|
895
|
+
category: inputData.revopsType,
|
|
896
|
+
entryTime: inputData.entryTime,
|
|
897
|
+
exitTime: inputData.exitTime,
|
|
898
|
+
filePath: inputData.filePath,
|
|
899
|
+
status: inputData?.revopsType == 'non-tagging' ?'':'submitted',
|
|
900
|
+
description: inputData.comments || '',
|
|
901
|
+
isChecked: null,
|
|
902
|
+
createdByEmail: req?.user?.email,
|
|
903
|
+
createdByUserName: req?.user?.userName,
|
|
904
|
+
createdByRole: req?.user?.role,
|
|
905
|
+
message: inputData.comments || '',
|
|
906
|
+
createdAt: new Date(),
|
|
907
|
+
};
|
|
908
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
909
|
+
}
|
|
510
910
|
if ( inputData?.duplicateImage?.length> 0 ) {
|
|
511
911
|
let bulkBody = [];
|
|
512
912
|
for ( let item of inputData?.duplicateImage ) {
|
|
@@ -514,10 +914,10 @@ export async function tagTempId( req, res ) {
|
|
|
514
914
|
clientId: inputData.storeId.split( '-' )[0],
|
|
515
915
|
storeId: inputData.storeId,
|
|
516
916
|
tempId: item.tempId,
|
|
517
|
-
id: `${inputData?.storeId}_${inputData?.dateString}_${
|
|
917
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${item?.tempId}`,
|
|
518
918
|
dateString: inputData.dateString,
|
|
519
919
|
timeRange: item.timeRange,
|
|
520
|
-
isChecked:
|
|
920
|
+
isChecked: null,
|
|
521
921
|
processType: inputData.processType,
|
|
522
922
|
revopsType: item.revopsType,
|
|
523
923
|
entryTime: item.entryTime,
|
|
@@ -529,6 +929,7 @@ export async function tagTempId( req, res ) {
|
|
|
529
929
|
isParent: false,
|
|
530
930
|
type: 'tagging-reflect',
|
|
531
931
|
ticketStatus: 'submitted',
|
|
932
|
+
comments: inputData.comments || '',
|
|
532
933
|
actions: [
|
|
533
934
|
{
|
|
534
935
|
actionType: 'tagging',
|
|
@@ -556,12 +957,20 @@ export async function tagTempId( req, res ) {
|
|
|
556
957
|
return { success: false, errors: res1.items };
|
|
557
958
|
} else {
|
|
558
959
|
logger.info( { msg: 'res1' } );
|
|
559
|
-
return res.sendSuccess( `ID tagged as
|
|
960
|
+
return res.sendSuccess( `ID tagged as Duplicates` );
|
|
560
961
|
// return { success: true };
|
|
561
962
|
}
|
|
562
963
|
}
|
|
563
964
|
} else {
|
|
564
|
-
|
|
965
|
+
// Convert camelCase revopsType to space-separated and capitalize first letter of each word
|
|
966
|
+
function camelCaseToTitle( str ) {
|
|
967
|
+
if ( !str ) return '';
|
|
968
|
+
return str.replace( /([A-Z])/g, ' $1' ).replace( /^./, ( s ) => s.toUpperCase() );
|
|
969
|
+
}
|
|
970
|
+
const titleRevopsType = camelCaseToTitle( inputData?.revopsType );
|
|
971
|
+
const message = inputData?.revopsType == 'non-tagging' ?
|
|
972
|
+
'ID removed from tagging' :
|
|
973
|
+
`ID tagged as ${titleRevopsType}`;
|
|
565
974
|
return res.sendSuccess( message );
|
|
566
975
|
}
|
|
567
976
|
} catch ( error ) {
|
|
@@ -588,3 +997,343 @@ export async function getCategorizedImages( req, res ) {
|
|
|
588
997
|
return res.sendError( err, 500 );
|
|
589
998
|
}
|
|
590
999
|
}
|
|
1000
|
+
|
|
1001
|
+
export async function vmsDataMigration( req, res ) {
|
|
1002
|
+
try {
|
|
1003
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1004
|
+
const inputData = req.body;
|
|
1005
|
+
const { storeId, dateString, limit = 100 } = inputData;
|
|
1006
|
+
|
|
1007
|
+
// Build query to fetch old structure documents
|
|
1008
|
+
const query = {
|
|
1009
|
+
query: {
|
|
1010
|
+
bool: {
|
|
1011
|
+
must: [
|
|
1012
|
+
{
|
|
1013
|
+
term: {
|
|
1014
|
+
'ticketName.keyword': 'footfall-directory',
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
],
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
size: parseInt( limit ),
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
// Add storeId filter if provided
|
|
1024
|
+
if ( storeId ) {
|
|
1025
|
+
query.query.bool.must.push( {
|
|
1026
|
+
term: {
|
|
1027
|
+
'storeId.keyword': storeId,
|
|
1028
|
+
},
|
|
1029
|
+
} );
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// Add dateString filter if provided
|
|
1033
|
+
if ( dateString ) {
|
|
1034
|
+
query.query.bool.must.push( {
|
|
1035
|
+
terms: {
|
|
1036
|
+
dateString: dateString?.split( ',' ),
|
|
1037
|
+
},
|
|
1038
|
+
} );
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Exclude documents that already have the new structure (have mappingInfo or type: 'store')
|
|
1042
|
+
query.query.bool.must_not = [
|
|
1043
|
+
{
|
|
1044
|
+
exists: {
|
|
1045
|
+
field: 'mappingInfo',
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
term: {
|
|
1050
|
+
'type.keyword': 'store',
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
];
|
|
1054
|
+
|
|
1055
|
+
const getData = await getOpenSearchData( openSearch.oldFootfallDirectory, query );
|
|
1056
|
+
logger.info( { getData } );
|
|
1057
|
+
const hits = getData?.body?.hits?.hits || [];
|
|
1058
|
+
|
|
1059
|
+
if ( hits.length === 0 ) {
|
|
1060
|
+
return res.sendSuccess( { message: 'No documents found to migrate', migrated: 0 } );
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
let migratedCount = 0;
|
|
1064
|
+
const errors = [];
|
|
1065
|
+
|
|
1066
|
+
for ( const hit of hits ) {
|
|
1067
|
+
try {
|
|
1068
|
+
const oldSource = hit._source;
|
|
1069
|
+
const documentId = hit._id;
|
|
1070
|
+
|
|
1071
|
+
// Calculate revicedFootfall (sum of AC counts)
|
|
1072
|
+
const revicedFootfall = ( oldSource.duplicateACCount || 0 ) +
|
|
1073
|
+
( oldSource.employeeACCount || 0 ) +
|
|
1074
|
+
( oldSource.houseKeepingACCount || 0 ) +
|
|
1075
|
+
( oldSource.junkACCount || 0 );
|
|
1076
|
+
|
|
1077
|
+
// Calculate revicedPerc
|
|
1078
|
+
const footfallCount = oldSource.footfallCount || 0;
|
|
1079
|
+
const revicedPerc = footfallCount > 0 ?
|
|
1080
|
+
Math.round( ( revicedFootfall / footfallCount ) * 100 ) :
|
|
1081
|
+
0;
|
|
1082
|
+
|
|
1083
|
+
// Calculate reviced
|
|
1084
|
+
const reviced = parseInt( revicedPerc );
|
|
1085
|
+
|
|
1086
|
+
// Transform arrays to revisedDetail format
|
|
1087
|
+
const revisedDetail = [];
|
|
1088
|
+
|
|
1089
|
+
// Transform duplicateImages
|
|
1090
|
+
if ( Array.isArray( oldSource.duplicateImages ) ) {
|
|
1091
|
+
for ( const duplicate of oldSource.duplicateImages ) {
|
|
1092
|
+
const parentId = `${oldSource.storeId}_${oldSource.dateString}_${duplicate.tempId}`;
|
|
1093
|
+
const parentDetail = {
|
|
1094
|
+
id: parentId,
|
|
1095
|
+
clientId: oldSource.clientId,
|
|
1096
|
+
storeId: oldSource.storeId,
|
|
1097
|
+
tempId: duplicate.tempId,
|
|
1098
|
+
dateString: oldSource.dateString,
|
|
1099
|
+
timeRange: duplicate.timeRange,
|
|
1100
|
+
processType: 'footfall',
|
|
1101
|
+
revopsType: 'duplicate',
|
|
1102
|
+
entryTime: duplicate.entryTime,
|
|
1103
|
+
exitTime: duplicate.exitTime,
|
|
1104
|
+
filePath: duplicate.filePath,
|
|
1105
|
+
status: oldSource.duplicateStatus || 'submitted',
|
|
1106
|
+
description: '',
|
|
1107
|
+
isChecked: duplicate.isChecked !== undefined ? duplicate.isChecked : false,
|
|
1108
|
+
type: 'tagging-reflect',
|
|
1109
|
+
parent: null,
|
|
1110
|
+
isParent: true,
|
|
1111
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1112
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1113
|
+
duplicateImage: [],
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
// Add child duplicate images
|
|
1117
|
+
if ( Array.isArray( duplicate.data ) ) {
|
|
1118
|
+
parentDetail.duplicateImage = duplicate.data.map( ( child ) => ( {
|
|
1119
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${child.tempId}`,
|
|
1120
|
+
tempId: child.tempId,
|
|
1121
|
+
timeRange: child.timeRange,
|
|
1122
|
+
entryTime: child.entryTime,
|
|
1123
|
+
exitTime: child.exitTime,
|
|
1124
|
+
filePath: child.filePath,
|
|
1125
|
+
isChecked: child.isChecked !== undefined ? child.isChecked : true,
|
|
1126
|
+
} ) );
|
|
1127
|
+
|
|
1128
|
+
// Add child details to revisedDetail
|
|
1129
|
+
for ( const child of duplicate.data ) {
|
|
1130
|
+
revisedDetail.push( {
|
|
1131
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${child.tempId}`,
|
|
1132
|
+
clientId: oldSource.clientId,
|
|
1133
|
+
storeId: oldSource.storeId,
|
|
1134
|
+
tempId: child.tempId,
|
|
1135
|
+
dateString: oldSource.dateString,
|
|
1136
|
+
timeRange: child.timeRange,
|
|
1137
|
+
processType: 'footfall',
|
|
1138
|
+
revopsType: 'duplicate',
|
|
1139
|
+
entryTime: child.entryTime,
|
|
1140
|
+
exitTime: child.exitTime,
|
|
1141
|
+
filePath: child.filePath,
|
|
1142
|
+
status: oldSource.duplicateStatus || 'submitted',
|
|
1143
|
+
description: '',
|
|
1144
|
+
isChecked: child.isChecked !== undefined ? child.isChecked : false,
|
|
1145
|
+
type: 'tagging-reflect',
|
|
1146
|
+
parent: duplicate.tempId,
|
|
1147
|
+
isParent: false,
|
|
1148
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1149
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1150
|
+
duplicateImage: [],
|
|
1151
|
+
} );
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
revisedDetail.push( parentDetail );
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Transform houseKeeping
|
|
1160
|
+
if ( Array.isArray( oldSource.houseKeeping ) ) {
|
|
1161
|
+
for ( const item of oldSource.houseKeeping ) {
|
|
1162
|
+
revisedDetail.push( {
|
|
1163
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1164
|
+
clientId: oldSource.clientId,
|
|
1165
|
+
storeId: oldSource.storeId,
|
|
1166
|
+
tempId: item.tempId,
|
|
1167
|
+
dateString: oldSource.dateString,
|
|
1168
|
+
timeRange: item.timeRange,
|
|
1169
|
+
processType: 'footfall',
|
|
1170
|
+
revopsType: 'houseKeeping',
|
|
1171
|
+
entryTime: item.entryTime,
|
|
1172
|
+
exitTime: item.exitTime,
|
|
1173
|
+
filePath: item.filePath,
|
|
1174
|
+
status: oldSource.houseKeepingStatus || 'submitted',
|
|
1175
|
+
description: '',
|
|
1176
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1177
|
+
type: 'tagging-reflect',
|
|
1178
|
+
parent: null,
|
|
1179
|
+
isParent: false,
|
|
1180
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1181
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1182
|
+
duplicateImage: [],
|
|
1183
|
+
} );
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// Transform employee
|
|
1188
|
+
if ( Array.isArray( oldSource.employee ) ) {
|
|
1189
|
+
for ( const item of oldSource.employee ) {
|
|
1190
|
+
revisedDetail.push( {
|
|
1191
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1192
|
+
clientId: oldSource.clientId,
|
|
1193
|
+
storeId: oldSource.storeId,
|
|
1194
|
+
tempId: item.tempId,
|
|
1195
|
+
dateString: oldSource.dateString,
|
|
1196
|
+
timeRange: item.timeRange,
|
|
1197
|
+
processType: 'footfall',
|
|
1198
|
+
revopsType: 'employee',
|
|
1199
|
+
entryTime: item.entryTime,
|
|
1200
|
+
exitTime: item.exitTime,
|
|
1201
|
+
filePath: item.filePath,
|
|
1202
|
+
status: oldSource.employeeStatus || 'submitted',
|
|
1203
|
+
description: '',
|
|
1204
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1205
|
+
type: 'tagging-reflect',
|
|
1206
|
+
parent: null,
|
|
1207
|
+
isParent: false,
|
|
1208
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1209
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1210
|
+
duplicateImage: [],
|
|
1211
|
+
} );
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Transform junk
|
|
1216
|
+
if ( Array.isArray( oldSource.junk ) ) {
|
|
1217
|
+
for ( const item of oldSource.junk ) {
|
|
1218
|
+
revisedDetail.push( {
|
|
1219
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1220
|
+
clientId: oldSource.clientId,
|
|
1221
|
+
storeId: oldSource.storeId,
|
|
1222
|
+
tempId: item.tempId,
|
|
1223
|
+
dateString: oldSource.dateString,
|
|
1224
|
+
timeRange: item.timeRange,
|
|
1225
|
+
processType: 'footfall',
|
|
1226
|
+
revopsType: 'junk',
|
|
1227
|
+
entryTime: item.entryTime,
|
|
1228
|
+
exitTime: item.exitTime,
|
|
1229
|
+
filePath: item.filePath,
|
|
1230
|
+
status: 'submitted',
|
|
1231
|
+
description: '',
|
|
1232
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1233
|
+
type: 'tagging-reflect',
|
|
1234
|
+
parent: null,
|
|
1235
|
+
isParent: false,
|
|
1236
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1237
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1238
|
+
duplicateImage: [],
|
|
1239
|
+
} );
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Create count array
|
|
1244
|
+
const count = [
|
|
1245
|
+
{
|
|
1246
|
+
name: 'Duplicate',
|
|
1247
|
+
value: oldSource.duplicateCount || 0,
|
|
1248
|
+
key: 'duplicateCount',
|
|
1249
|
+
type: 'duplicate',
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
name: 'Employee',
|
|
1253
|
+
value: oldSource.employeeCount || 0,
|
|
1254
|
+
key: 'employeeCount',
|
|
1255
|
+
type: 'employee',
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
name: 'House keeping',
|
|
1259
|
+
value: oldSource.houseKeepingCount || 0,
|
|
1260
|
+
key: 'houseKeepingCount',
|
|
1261
|
+
type: 'houseKeeping',
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
name: 'Junk',
|
|
1265
|
+
value: oldSource.junkCount || 0,
|
|
1266
|
+
key: 'junkCount',
|
|
1267
|
+
type: 'junk',
|
|
1268
|
+
},
|
|
1269
|
+
];
|
|
1270
|
+
|
|
1271
|
+
// Create mappingInfo array
|
|
1272
|
+
const mappingInfo = [
|
|
1273
|
+
{
|
|
1274
|
+
type: 'tagging',
|
|
1275
|
+
mode: 'mobile',
|
|
1276
|
+
revicedFootfall,
|
|
1277
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1278
|
+
reviced,
|
|
1279
|
+
count,
|
|
1280
|
+
revisedDetail,
|
|
1281
|
+
status: oldSource.status === 'open' ? 'Raised' : oldSource.status || 'Raised',
|
|
1282
|
+
createdByEmail: oldSource.email || '',
|
|
1283
|
+
createdByUserName: oldSource.userName || '',
|
|
1284
|
+
createdByRole: oldSource.role || 'user',
|
|
1285
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
type: 'review',
|
|
1289
|
+
count,
|
|
1290
|
+
revisedDetail,
|
|
1291
|
+
status: oldSource.status === 'open' ? 'Open' : oldSource.status || 'Open',
|
|
1292
|
+
dueDate: oldSource.updatedAt ? new Date( new Date( oldSource.updatedAt ).getTime() + 3 * 24 * 60 * 60 * 1000 ) : new Date( Date.now() + 3 * 24 * 60 * 60 * 1000 ),
|
|
1293
|
+
},
|
|
1294
|
+
];
|
|
1295
|
+
|
|
1296
|
+
// Create new structure
|
|
1297
|
+
const newSource = {
|
|
1298
|
+
storeId: oldSource.storeId,
|
|
1299
|
+
type: 'store',
|
|
1300
|
+
dateString: oldSource.dateString,
|
|
1301
|
+
storeName: oldSource.storeName,
|
|
1302
|
+
ticketName: oldSource.ticketName,
|
|
1303
|
+
footfallCount: oldSource.footfallCount,
|
|
1304
|
+
clientId: oldSource.clientId,
|
|
1305
|
+
ticketId: oldSource.ticketId,
|
|
1306
|
+
createdAt: oldSource.createdAt,
|
|
1307
|
+
updatedAt: oldSource.updatedAt,
|
|
1308
|
+
status: oldSource.status === 'open' ? 'Raised' : oldSource.status || 'Raised',
|
|
1309
|
+
comments: oldSource.comments || '',
|
|
1310
|
+
revicedFootfall,
|
|
1311
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1312
|
+
reviced,
|
|
1313
|
+
mappingInfo,
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
// Update document in OpenSearch
|
|
1317
|
+
// await updateOpenSearchData( openSearch.footfallDirectory, documentId, { doc: newSource } );
|
|
1318
|
+
// migratedCount++;
|
|
1319
|
+
|
|
1320
|
+
logger.info( { message: 'Document migrated successfully', newSource, documentId, storeId: oldSource.storeId, dateString: oldSource.dateString } );
|
|
1321
|
+
} catch ( error ) {
|
|
1322
|
+
const errorMsg = `Error migrating document ${hit._id}: ${error.message}`;
|
|
1323
|
+
errors.push( errorMsg );
|
|
1324
|
+
logger.error( { error: error, documentId: hit._id, function: 'vmsDataMigration' } );
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
return res.sendSuccess( {
|
|
1329
|
+
message: `Migration completed. ${migratedCount} document(s) migrated.`,
|
|
1330
|
+
migrated: migratedCount,
|
|
1331
|
+
total: hits.length,
|
|
1332
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
1333
|
+
} );
|
|
1334
|
+
} catch ( error ) {
|
|
1335
|
+
logger.error( { error: error, message: req.query, function: 'vmsDataMigration' } );
|
|
1336
|
+
const err = error.message || 'Internal Server Error';
|
|
1337
|
+
return res.sendError( err, 500 );
|
|
1338
|
+
}
|
|
1339
|
+
}
|