tango-app-api-analysis-traffic 3.8.7-vms.30 → 3.8.7-vms.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,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, insertWithId, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
4
|
+
import { bulkUpdate, insertWithId, scrollResponse, searchOpenSearchData, 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 //
|
|
@@ -70,7 +70,7 @@ export async function revoptagging( req, res ) {
|
|
|
70
70
|
|
|
71
71
|
let respo= await getOpenSearchData( openSearch.revops, searchQuery );
|
|
72
72
|
const revopData = respo?.body?.hits?.hits;
|
|
73
|
-
if ( revopData&& revopData.length>0 ) {
|
|
73
|
+
if ( revopData && revopData.length>0 ) {
|
|
74
74
|
await updateOpenSearchData( openSearch.revops, revopData[0]._id, { doc: item } );
|
|
75
75
|
} else {
|
|
76
76
|
item.createdAt = new Date();
|
|
@@ -140,14 +140,29 @@ export async function getrevoptagging( req, res ) {
|
|
|
140
140
|
|
|
141
141
|
export async function migrateRevopIndex( req, res ) {
|
|
142
142
|
try {
|
|
143
|
-
const { storeId, dateString, size =
|
|
143
|
+
const { storeId, dateString, size = 100 } = req.body;
|
|
144
144
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
145
145
|
|
|
146
146
|
const query = {
|
|
147
147
|
size: size,
|
|
148
148
|
query: {
|
|
149
149
|
bool: {
|
|
150
|
-
must: [
|
|
150
|
+
must: [
|
|
151
|
+
// {
|
|
152
|
+
// range: {
|
|
153
|
+
// createdAt: {
|
|
154
|
+
// gte: '2025-10-01T00:00:00.000Z',
|
|
155
|
+
// lte: '2025-10-31T23:59:59.000Z',
|
|
156
|
+
// },
|
|
157
|
+
// },
|
|
158
|
+
// },
|
|
159
|
+
{
|
|
160
|
+
term: {
|
|
161
|
+
'type.keyword': 'tagging-reflect',
|
|
162
|
+
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
151
166
|
},
|
|
152
167
|
},
|
|
153
168
|
};
|
|
@@ -168,21 +183,80 @@ export async function migrateRevopIndex( req, res ) {
|
|
|
168
183
|
} );
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
const response = await getOpenSearchData( openSearch.revop, query );
|
|
172
|
-
const hits = response?.body?.hits?.hits || [];
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
// const response = await getOpenSearchData( openSearch.revop, query );
|
|
187
|
+
// const hits = response?.body?.hits?.hits || [];
|
|
188
|
+
|
|
189
|
+
// Use OpenSearch scroll API to retrieve up to 60000 records efficiently
|
|
190
|
+
let allHits = [];
|
|
191
|
+
let scrollId = null;
|
|
192
|
+
let totalFetched = 0;
|
|
193
|
+
let firstResponse = await searchOpenSearchData( openSearch.revop, query );
|
|
194
|
+
// Collect first batch
|
|
195
|
+
let hitsBatch = firstResponse?.body?.hits?.hits || [];
|
|
196
|
+
if ( hitsBatch.length > 0 ) {
|
|
197
|
+
allHits.push( ...hitsBatch );
|
|
198
|
+
totalFetched += hitsBatch.length;
|
|
199
|
+
scrollId = firstResponse.body._scroll_id;
|
|
176
200
|
}
|
|
177
201
|
|
|
178
202
|
const bulkBody = [];
|
|
179
|
-
|
|
180
|
-
for ( const hit of hits ) {
|
|
203
|
+
for ( const hit of hitsBatch ) {
|
|
181
204
|
const src = hit._source || {};
|
|
182
205
|
const statusValue = ( src.status || '' ).toLowerCase();
|
|
183
|
-
const parentValue = src.parent;
|
|
184
|
-
|
|
206
|
+
// const parentValue = src.parent;
|
|
207
|
+
|
|
208
|
+
// Get ticket sattasu from the footfalldirectory index matching src.storeId and src.dateString
|
|
209
|
+
let ticketStatus = null;
|
|
185
210
|
|
|
211
|
+
const footfallQuery = {
|
|
212
|
+
size: 1,
|
|
213
|
+
query: {
|
|
214
|
+
bool: {
|
|
215
|
+
must: [
|
|
216
|
+
{ term: { 'storeId.keyword': src.storeId } },
|
|
217
|
+
{ term: { 'dateString': src.dateString } },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
const footfallResp = await getOpenSearchData( openSearch.oldFootfallDirectory, footfallQuery );
|
|
223
|
+
ticketStatus = footfallResp?.body?.hits?.hits?.[0]?._source?.status || null;
|
|
224
|
+
if ( src?.duplicateImage?.length > 0 ) {
|
|
225
|
+
src.duplicateImage = src.duplicateImage.map( ( item ) => ( {
|
|
226
|
+
...item,
|
|
227
|
+
id: `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${item.tempId || ''}`,
|
|
228
|
+
actions: ( ticketStatus === 'closed' && item.isChecked === true ) ? [
|
|
229
|
+
{
|
|
230
|
+
actionType: 'tagging',
|
|
231
|
+
action: 'submitted',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
actionType: 'review',
|
|
235
|
+
action: 'approved',
|
|
236
|
+
},
|
|
237
|
+
]: ( ticketStatus === 'closed' && item.isChecked === false )?
|
|
238
|
+
[
|
|
239
|
+
{
|
|
240
|
+
actionType: 'tagging',
|
|
241
|
+
action: 'submitted',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
actionType: 'review',
|
|
245
|
+
action: 'rejected',
|
|
246
|
+
},
|
|
247
|
+
]:
|
|
248
|
+
[
|
|
249
|
+
{
|
|
250
|
+
actionType: 'tagging',
|
|
251
|
+
action: 'submitted',
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
// Include relevant action, assuming 'actions' will be determined below
|
|
255
|
+
} ) );
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
const idValue = `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${src.tempId || ''}`;
|
|
186
260
|
let actions = [
|
|
187
261
|
{
|
|
188
262
|
actionType: 'tagging',
|
|
@@ -214,12 +288,15 @@ export async function migrateRevopIndex( req, res ) {
|
|
|
214
288
|
];
|
|
215
289
|
}
|
|
216
290
|
|
|
291
|
+
|
|
217
292
|
const doc = {
|
|
293
|
+
...src,
|
|
218
294
|
id: idValue,
|
|
219
|
-
|
|
295
|
+
revopsType: src?.revopsType === 'house-keeping'? 'houseKeeping' : src?.revopsType,
|
|
296
|
+
isParent: src?.duplicateImage?.length > 0? true : false,
|
|
220
297
|
actions,
|
|
221
298
|
ticketStatus: src.status,
|
|
222
|
-
|
|
299
|
+
// updatedAt: new Date(),
|
|
223
300
|
};
|
|
224
301
|
|
|
225
302
|
bulkBody.push(
|
|
@@ -229,17 +306,388 @@ export async function migrateRevopIndex( req, res ) {
|
|
|
229
306
|
}
|
|
230
307
|
|
|
231
308
|
const bulkRes = await bulkUpdate( bulkBody );
|
|
309
|
+
|
|
232
310
|
if ( bulkRes?.errors ) {
|
|
233
311
|
logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
234
312
|
return res.sendError( 'Failed to migrate some records', 500 );
|
|
235
313
|
}
|
|
236
314
|
|
|
315
|
+
while ( hitsBatch.length > 0 && scrollId ) {
|
|
316
|
+
// Fetch next batch using scroll_id
|
|
317
|
+
const nextScrollRes = await scrollResponse( scrollId );
|
|
318
|
+
|
|
319
|
+
hitsBatch = nextScrollRes?.body?.hits?.hits || [];
|
|
320
|
+
if ( hitsBatch.length === 0 ) break;
|
|
321
|
+
logger.info( { hitsBatch: hitsBatch?.length } );
|
|
322
|
+
const bulkBody = [];
|
|
323
|
+
for ( const hit of hitsBatch ) {
|
|
324
|
+
const src = hit._source || {};
|
|
325
|
+
const statusValue = ( src.status || '' ).toLowerCase();
|
|
326
|
+
// const parentValue = src.parent;
|
|
327
|
+
|
|
328
|
+
// Get ticket sattasu from the footfalldirectory index matching src.storeId and src.dateString
|
|
329
|
+
let ticketStatus = null;
|
|
330
|
+
|
|
331
|
+
const footfallQuery = {
|
|
332
|
+
size: 1,
|
|
333
|
+
query: {
|
|
334
|
+
bool: {
|
|
335
|
+
must: [
|
|
336
|
+
{ term: { 'storeId.keyword': src.storeId } },
|
|
337
|
+
{ term: { 'dateString': src.dateString } },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
const footfallResp = await getOpenSearchData( openSearch.oldFootfallDirectory, footfallQuery );
|
|
343
|
+
ticketStatus = footfallResp?.body?.hits?.hits?.[0]?._source?.status || null;
|
|
344
|
+
if ( src?.duplicateImage?.length > 0 ) {
|
|
345
|
+
src.duplicateImage = src.duplicateImage.map( ( item ) => ( {
|
|
346
|
+
...item,
|
|
347
|
+
id: `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${item.tempId || ''}`,
|
|
348
|
+
actions: ( ticketStatus === 'closed' && item.isChecked === true ) ? [
|
|
349
|
+
{
|
|
350
|
+
actionType: 'tagging',
|
|
351
|
+
action: 'submitted',
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
actionType: 'review',
|
|
355
|
+
action: 'approved',
|
|
356
|
+
},
|
|
357
|
+
]: ( ticketStatus === 'closed' && item.isChecked === false )?
|
|
358
|
+
[
|
|
359
|
+
{
|
|
360
|
+
actionType: 'tagging',
|
|
361
|
+
action: 'submitted',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
actionType: 'review',
|
|
365
|
+
action: 'rejected',
|
|
366
|
+
},
|
|
367
|
+
]:
|
|
368
|
+
[
|
|
369
|
+
{
|
|
370
|
+
actionType: 'tagging',
|
|
371
|
+
action: 'submitted',
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
// Include relevant action, assuming 'actions' will be determined below
|
|
375
|
+
} ) );
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
const idValue = `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${src.tempId || ''}`;
|
|
380
|
+
logger.info( { idValue } );
|
|
381
|
+
let actions = [
|
|
382
|
+
{
|
|
383
|
+
actionType: 'tagging',
|
|
384
|
+
action: 'submitted',
|
|
385
|
+
},
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
if ( statusValue === 'approved' ) {
|
|
389
|
+
actions = [
|
|
390
|
+
{
|
|
391
|
+
actionType: 'tagging',
|
|
392
|
+
action: 'submitted',
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
actionType: 'review',
|
|
396
|
+
action: 'approved',
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
} else if ( statusValue === 'rejected' ) {
|
|
400
|
+
actions = [
|
|
401
|
+
{
|
|
402
|
+
actionType: 'tagging',
|
|
403
|
+
action: 'submitted',
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
actionType: 'review',
|
|
407
|
+
action: 'rejected',
|
|
408
|
+
},
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
const doc = {
|
|
414
|
+
...src,
|
|
415
|
+
id: idValue,
|
|
416
|
+
revopsType: src?.revopsType === 'house-keeping'? 'houseKeeping' : src?.revopsType,
|
|
417
|
+
isParent: src?.duplicateImage?.length > 0? true : false,
|
|
418
|
+
actions,
|
|
419
|
+
ticketStatus: src.status,
|
|
420
|
+
// updatedAt: new Date(),
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
bulkBody.push(
|
|
424
|
+
{ update: { _index: openSearch.newRevop, _id: hit._id } },
|
|
425
|
+
{ doc: doc, doc_as_upsert: true },
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
430
|
+
|
|
431
|
+
if ( bulkRes?.errors ) {
|
|
432
|
+
logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
433
|
+
return res.sendError( 'Failed to migrate some records', 500 );
|
|
434
|
+
}
|
|
435
|
+
allHits.push( ...hitsBatch );
|
|
436
|
+
totalFetched += hitsBatch.length;
|
|
437
|
+
logger.info( { totalFetched } );
|
|
438
|
+
// Protect against exceeding limit
|
|
439
|
+
|
|
440
|
+
scrollId = nextScrollRes.body._scroll_id;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// For downstream logic, use allHits instead of hits
|
|
444
|
+
const hits = allHits;
|
|
445
|
+
|
|
446
|
+
if ( hits.length === 0 ) {
|
|
447
|
+
return res.sendSuccess( { message: 'No records found for migration', updated: 0 } );
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
// for ( const hit of hits ) {
|
|
452
|
+
// const src = hit._source || {};
|
|
453
|
+
// const statusValue = ( src.status || '' ).toLowerCase();
|
|
454
|
+
// // const parentValue = src.parent;
|
|
455
|
+
|
|
456
|
+
// // Get ticket sattasu from the footfalldirectory index matching src.storeId and src.dateString
|
|
457
|
+
// let ticketStatus = null;
|
|
458
|
+
|
|
459
|
+
// const footfallQuery = {
|
|
460
|
+
// size: 1,
|
|
461
|
+
// query: {
|
|
462
|
+
// bool: {
|
|
463
|
+
// must: [
|
|
464
|
+
// { term: { 'storeId.keyword': src.storeId } },
|
|
465
|
+
// { term: { 'dateString': src.dateString } },
|
|
466
|
+
// ],
|
|
467
|
+
// },
|
|
468
|
+
// },
|
|
469
|
+
// };
|
|
470
|
+
// const footfallResp = await getOpenSearchData( openSearch.oldFootfallDirectory, footfallQuery );
|
|
471
|
+
// ticketStatus = footfallResp?.body?.hits?.hits?.[0]?._source?.status || null;
|
|
472
|
+
// if ( src?.duplicateImage?.length > 0 ) {
|
|
473
|
+
// src.duplicateImage = src.duplicateImage.map( ( item ) => ( {
|
|
474
|
+
// ...item,
|
|
475
|
+
// id: `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${item.tempId || ''}`,
|
|
476
|
+
// actions: ( ticketStatus === 'closed' && item.isChecked === true ) ? [
|
|
477
|
+
// {
|
|
478
|
+
// actionType: 'tagging',
|
|
479
|
+
// action: 'submitted',
|
|
480
|
+
// },
|
|
481
|
+
// {
|
|
482
|
+
// actionType: 'review',
|
|
483
|
+
// action: 'approved',
|
|
484
|
+
// },
|
|
485
|
+
// ]: ( ticketStatus === 'closed' && item.isChecked === false )?
|
|
486
|
+
// [
|
|
487
|
+
// {
|
|
488
|
+
// actionType: 'tagging',
|
|
489
|
+
// action: 'submitted',
|
|
490
|
+
// },
|
|
491
|
+
// {
|
|
492
|
+
// actionType: 'review',
|
|
493
|
+
// action: 'rejected',
|
|
494
|
+
// },
|
|
495
|
+
// ]:
|
|
496
|
+
// [
|
|
497
|
+
// {
|
|
498
|
+
// actionType: 'tagging',
|
|
499
|
+
// action: 'submitted',
|
|
500
|
+
// },
|
|
501
|
+
// ],
|
|
502
|
+
// // Include relevant action, assuming 'actions' will be determined below
|
|
503
|
+
// } ) );
|
|
504
|
+
// }
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
// const idValue = `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${src.tempId || ''}`;
|
|
508
|
+
|
|
509
|
+
// let actions = [
|
|
510
|
+
// {
|
|
511
|
+
// actionType: 'tagging',
|
|
512
|
+
// action: 'submitted',
|
|
513
|
+
// },
|
|
514
|
+
// ];
|
|
515
|
+
|
|
516
|
+
// if ( statusValue === 'approved' ) {
|
|
517
|
+
// actions = [
|
|
518
|
+
// {
|
|
519
|
+
// actionType: 'tagging',
|
|
520
|
+
// action: 'submitted',
|
|
521
|
+
// },
|
|
522
|
+
// {
|
|
523
|
+
// actionType: 'review',
|
|
524
|
+
// action: 'approved',
|
|
525
|
+
// },
|
|
526
|
+
// ];
|
|
527
|
+
// } else if ( statusValue === 'rejected' ) {
|
|
528
|
+
// actions = [
|
|
529
|
+
// {
|
|
530
|
+
// actionType: 'tagging',
|
|
531
|
+
// action: 'submitted',
|
|
532
|
+
// },
|
|
533
|
+
// {
|
|
534
|
+
// actionType: 'review',
|
|
535
|
+
// action: 'rejected',
|
|
536
|
+
// },
|
|
537
|
+
// ];
|
|
538
|
+
// }
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
// const doc = {
|
|
542
|
+
// ...src,
|
|
543
|
+
// id: idValue,
|
|
544
|
+
// revopsType: src?.revopsType === 'house-keeping'? 'houseKeeping' : src?.revopsType,
|
|
545
|
+
// isParent: src?.duplicateImage?.length > 0? true : false,
|
|
546
|
+
// actions,
|
|
547
|
+
// ticketStatus: src.status,
|
|
548
|
+
// // updatedAt: new Date(),
|
|
549
|
+
// };
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
// bulkBody.push(
|
|
553
|
+
// { update: { _index: openSearch.newRevop, _id: hit._id } },
|
|
554
|
+
// { doc: doc, doc_as_upsert: true },
|
|
555
|
+
// );
|
|
556
|
+
// }
|
|
557
|
+
// Implement batch by batch update
|
|
558
|
+
// const BATCH_SIZE = 10000; // You can adjust the batch size as needed
|
|
559
|
+
|
|
560
|
+
// for ( let i = 0; i < bulkBody.length; i += BATCH_SIZE ) {
|
|
561
|
+
// const batch = bulkBody.slice( i, i + BATCH_SIZE );
|
|
562
|
+
// const bulkRes = await bulkUpdate( batch );
|
|
563
|
+
|
|
564
|
+
// if ( bulkRes?.errors ) {
|
|
565
|
+
// logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
566
|
+
// return res.sendError( 'Failed to migrate some records', 500 );
|
|
567
|
+
// }
|
|
568
|
+
// }
|
|
569
|
+
|
|
570
|
+
// const bulkRes = await bulkUpdate( bulkBody );
|
|
571
|
+
|
|
572
|
+
// if ( bulkRes?.errors ) {
|
|
573
|
+
// logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
574
|
+
// return res.sendError( 'Failed to migrate some records', 500 );
|
|
575
|
+
// }
|
|
576
|
+
|
|
237
577
|
return res.sendSuccess( { message: 'Migration completed', updated: hits.length } );
|
|
238
578
|
} catch ( error ) {
|
|
239
579
|
logger.error( { error: error, message: req.body, function: 'migrateRevopIndex' } );
|
|
240
580
|
return res.sendError( { error: error }, 500 );
|
|
241
581
|
}
|
|
242
582
|
}
|
|
583
|
+
|
|
584
|
+
export async function expireReviewStatus( req, res ) {
|
|
585
|
+
try {
|
|
586
|
+
const {
|
|
587
|
+
thresholdDate = '2025-12-20',
|
|
588
|
+
batchSize = 500,
|
|
589
|
+
storeId,
|
|
590
|
+
dateString,
|
|
591
|
+
} = req.body;
|
|
592
|
+
logger.info( { inputData: req.body, msg: '........1' } );
|
|
593
|
+
const cutoffDate = new Date( thresholdDate );
|
|
594
|
+
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
595
|
+
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
600
|
+
const query = {
|
|
601
|
+
size: batchSize,
|
|
602
|
+
query: {
|
|
603
|
+
bool: {
|
|
604
|
+
must: [
|
|
605
|
+
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
606
|
+
{ term: { 'type.keyword': 'store' } },
|
|
607
|
+
],
|
|
608
|
+
must_not: [
|
|
609
|
+
{ terms: { 'status.keyword': [ 'Closed' ] } },
|
|
610
|
+
],
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
if ( storeId ) {
|
|
617
|
+
query.query.bool.must.push( { term: { 'storeId.keyword': storeId } } );
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
if ( dateString ) {
|
|
622
|
+
query.query.bool.must.push( {
|
|
623
|
+
terms: {
|
|
624
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
625
|
+
},
|
|
626
|
+
} );
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
let totalUpdated = 0;
|
|
631
|
+
let scrollId = null;
|
|
632
|
+
|
|
633
|
+
let firstResponse = await searchOpenSearchData( openSearch.footfallDirectory, query );
|
|
634
|
+
let hitsBatch = firstResponse?.body?.hits?.hits || [];
|
|
635
|
+
logger.info( { hitsBatch } );
|
|
636
|
+
scrollId = firstResponse?.body?._scroll_id;
|
|
637
|
+
|
|
638
|
+
while ( hitsBatch.length > 0 ) {
|
|
639
|
+
const bulkBody = [];
|
|
640
|
+
|
|
641
|
+
for ( const hit of hitsBatch ) {
|
|
642
|
+
const src = hit._source || {};
|
|
643
|
+
const mappingInfo = Array.isArray( src.mappingInfo ) ? src.mappingInfo : [];
|
|
644
|
+
let changed = false;
|
|
645
|
+
const updatedMapping = mappingInfo.map( ( item ) => {
|
|
646
|
+
if ( item?.type === 'review'&& item?.type !== 'Closed' && item?.dueDate ) {
|
|
647
|
+
const due = new Date( item.dueDate );
|
|
648
|
+
logger.info( { due, msg: '..........1', cutoffDate } );
|
|
649
|
+
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
650
|
+
changed = true;
|
|
651
|
+
return { ...item, status: 'Expired' };
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return item;
|
|
655
|
+
} );
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
if ( changed ) {
|
|
659
|
+
const doc = {
|
|
660
|
+
mappingInfo: updatedMapping,
|
|
661
|
+
status: 'Expired',
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
bulkBody.push(
|
|
665
|
+
{ update: { _index: openSearch.footfallDirectory, _id: hit._id } },
|
|
666
|
+
{ doc: doc, doc_as_upsert: true },
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if ( bulkBody.length > 0 ) {
|
|
672
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
673
|
+
if ( bulkRes?.errors ) {
|
|
674
|
+
logger.error( { message: 'Bulk expire errors', items: bulkRes.items } );
|
|
675
|
+
}
|
|
676
|
+
totalUpdated += bulkBody.length / 2;
|
|
677
|
+
}
|
|
678
|
+
logger.info( { totalUpdated, msg: '........9' } );
|
|
679
|
+
if ( !scrollId ) break;
|
|
680
|
+
const nextRes = await scrollResponse( scrollId );
|
|
681
|
+
hitsBatch = nextRes?.body?.hits?.hits || [];
|
|
682
|
+
scrollId = nextRes?.body?._scroll_id;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return res.sendSuccess( { message: 'Expired review status updated', updated: totalUpdated } );
|
|
686
|
+
} catch ( error ) {
|
|
687
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
688
|
+
return res.sendError( { error: error }, 500 );
|
|
689
|
+
}
|
|
690
|
+
}
|
|
243
691
|
export async function revoptaggingcount( req, res ) {
|
|
244
692
|
try {
|
|
245
693
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
@@ -651,7 +1099,7 @@ export async function footFallImages( req, res ) {
|
|
|
651
1099
|
actionType: type,
|
|
652
1100
|
footfall: footfallValue,
|
|
653
1101
|
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
654
|
-
revicedPerc: mapping.
|
|
1102
|
+
revicedPerc: mapping.revicedPerc ?? '--',
|
|
655
1103
|
count: countObj,
|
|
656
1104
|
createdAt: mapping.createdAt ?? '',
|
|
657
1105
|
createdByEmail: mapping.createdByEmail ?? '',
|
|
@@ -666,7 +1114,7 @@ export async function footFallImages( req, res ) {
|
|
|
666
1114
|
actionType: type,
|
|
667
1115
|
footfall: footfallValue,
|
|
668
1116
|
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
669
|
-
revicedPerc: mapping.
|
|
1117
|
+
revicedPerc: mapping.revicedPerc ?? '--',
|
|
670
1118
|
count: countObj,
|
|
671
1119
|
createdAt: mapping.createdAt ?? '',
|
|
672
1120
|
createdByEmail: mapping.createdByEmail ?? '',
|
|
@@ -894,7 +1342,7 @@ export async function vmsDataMigration( req, res ) {
|
|
|
894
1342
|
try {
|
|
895
1343
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
896
1344
|
const inputData = req.body;
|
|
897
|
-
const { storeId, dateString, limit =
|
|
1345
|
+
const { storeId, dateString, limit = 10000 } = inputData;
|
|
898
1346
|
|
|
899
1347
|
// Build query to fetch old structure documents
|
|
900
1348
|
const query = {
|
|
@@ -906,6 +1354,14 @@ export async function vmsDataMigration( req, res ) {
|
|
|
906
1354
|
'ticketName.keyword': 'footfall-directory',
|
|
907
1355
|
},
|
|
908
1356
|
},
|
|
1357
|
+
// {
|
|
1358
|
+
// range: {
|
|
1359
|
+
// createdAt: {
|
|
1360
|
+
// gte: '2025-12-01T00:00:00.000Z',
|
|
1361
|
+
// lte: '2025-12-03T00:00:00.000Z',
|
|
1362
|
+
// },
|
|
1363
|
+
// },
|
|
1364
|
+
// },
|
|
909
1365
|
],
|
|
910
1366
|
},
|
|
911
1367
|
},
|
|
@@ -961,17 +1417,22 @@ export async function vmsDataMigration( req, res ) {
|
|
|
961
1417
|
const documentId = hit._id;
|
|
962
1418
|
|
|
963
1419
|
// Calculate revicedFootfall (sum of AC counts)
|
|
964
|
-
const
|
|
1420
|
+
const tempFootfall =oldSource?.status === 'open' ?
|
|
1421
|
+
( oldSource.duplicateCount || 0 ) +
|
|
1422
|
+
( oldSource.employeeCount || 0 ) +
|
|
1423
|
+
( oldSource.houseKeepingCount || 0 ) +
|
|
1424
|
+
( oldSource.junkCount || 0 ):
|
|
1425
|
+
( oldSource.duplicateACCount || 0 ) +
|
|
965
1426
|
( oldSource.employeeACCount || 0 ) +
|
|
966
1427
|
( oldSource.houseKeepingACCount || 0 ) +
|
|
967
1428
|
( oldSource.junkACCount || 0 );
|
|
968
1429
|
|
|
969
1430
|
// Calculate revicedPerc
|
|
970
1431
|
const footfallCount = oldSource.footfallCount || 0;
|
|
1432
|
+
const revicedFootfall = footfallCount - tempFootfall;
|
|
971
1433
|
const revicedPerc = footfallCount > 0 ?
|
|
972
1434
|
Math.round( ( revicedFootfall / footfallCount ) * 100 ) :
|
|
973
1435
|
0;
|
|
974
|
-
|
|
975
1436
|
// Calculate reviced
|
|
976
1437
|
const reviced = parseInt( revicedPerc );
|
|
977
1438
|
|
|
@@ -1161,7 +1622,8 @@ export async function vmsDataMigration( req, res ) {
|
|
|
1161
1622
|
];
|
|
1162
1623
|
|
|
1163
1624
|
// Create mappingInfo array
|
|
1164
|
-
const mappingInfo =
|
|
1625
|
+
const mappingInfo = oldSource.status === 'open' ?
|
|
1626
|
+
[
|
|
1165
1627
|
{
|
|
1166
1628
|
type: 'tagging',
|
|
1167
1629
|
mode: 'mobile',
|
|
@@ -1182,9 +1644,41 @@ export async function vmsDataMigration( req, res ) {
|
|
|
1182
1644
|
revisedDetail,
|
|
1183
1645
|
status: oldSource.status === 'open' ? 'Open' : oldSource.status || 'Open',
|
|
1184
1646
|
dueDate: oldSource.updatedAt ? new Date( new Date( oldSource.updatedAt ).getTime() + 3 * 24 * 60 * 60 * 1000 ) : new Date( Date.now() + 3 * 24 * 60 * 60 * 1000 ),
|
|
1647
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1185
1648
|
},
|
|
1186
|
-
]
|
|
1187
|
-
|
|
1649
|
+
]:
|
|
1650
|
+
|
|
1651
|
+
[
|
|
1652
|
+
{
|
|
1653
|
+
type: 'tagging',
|
|
1654
|
+
mode: 'web',
|
|
1655
|
+
revicedFootfall,
|
|
1656
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1657
|
+
reviced,
|
|
1658
|
+
count,
|
|
1659
|
+
revisedDetail,
|
|
1660
|
+
status: oldSource.status === 'closed' ? 'Closed' : oldSource.status || 'Closed',
|
|
1661
|
+
createdByEmail: oldSource.email || '',
|
|
1662
|
+
createdByUserName: oldSource.userName || '',
|
|
1663
|
+
createdByRole: oldSource.role || 'user',
|
|
1664
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
type: 'review',
|
|
1668
|
+
mode: 'web',
|
|
1669
|
+
revicedFootfall,
|
|
1670
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1671
|
+
reviced,
|
|
1672
|
+
count,
|
|
1673
|
+
revisedDetail,
|
|
1674
|
+
status: oldSource.status === 'closed' ? 'Closed' : oldSource.status || 'Closed',
|
|
1675
|
+
dueDate: oldSource.createdAt ? new Date( new Date( oldSource.createdAt ).getTime() + 3 * 24 * 60 * 60 * 1000 ) : new Date( Date.now() + 3 * 24 * 60 * 60 * 1000 ),
|
|
1676
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1677
|
+
createdByEmail: oldSource.approverEmail || '',
|
|
1678
|
+
createdByUserName: oldSource.approverUserName || '',
|
|
1679
|
+
createdByRole: oldSource.approverRole || 'user',
|
|
1680
|
+
},
|
|
1681
|
+
];
|
|
1188
1682
|
// Create new structure
|
|
1189
1683
|
const newSource = {
|
|
1190
1684
|
storeId: oldSource.storeId,
|
|
@@ -1197,7 +1691,7 @@ export async function vmsDataMigration( req, res ) {
|
|
|
1197
1691
|
ticketId: oldSource.ticketId,
|
|
1198
1692
|
createdAt: oldSource.createdAt,
|
|
1199
1693
|
updatedAt: oldSource.updatedAt,
|
|
1200
|
-
status: oldSource.status === 'open' ? 'Raised' :
|
|
1694
|
+
status: oldSource.status === 'open' ? 'Raised' : 'Closed' || 'Raised',
|
|
1201
1695
|
comments: oldSource.comments || '',
|
|
1202
1696
|
revicedFootfall,
|
|
1203
1697
|
revicedPerc: `${revicedPerc}%`,
|
|
@@ -1206,8 +1700,9 @@ export async function vmsDataMigration( req, res ) {
|
|
|
1206
1700
|
};
|
|
1207
1701
|
|
|
1208
1702
|
// Update document in OpenSearch
|
|
1209
|
-
|
|
1210
|
-
|
|
1703
|
+
const updatedData = await updateOpenSearchData( openSearch.footfallDirectory, documentId, { doc: newSource, doc_as_upsert: true } );
|
|
1704
|
+
logger.info( { updatedData } );
|
|
1705
|
+
migratedCount++;
|
|
1211
1706
|
|
|
1212
1707
|
logger.info( { message: 'Document migrated successfully', newSource, documentId, storeId: oldSource.storeId, dateString: oldSource.dateString } );
|
|
1213
1708
|
} catch ( error ) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex } from '../controllers/revop.controller.js';
|
|
3
|
+
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus } from '../controllers/revop.controller.js';
|
|
4
4
|
import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
5
5
|
import { footfallImagesValid, getCategorizedImagesValid, storeProcessedDataValid, tagTempIdValid, vmsDataMigrationValid } from '../dtos/revop.dtos.js';
|
|
6
6
|
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig } from '../validations/revop.validation.js';
|
|
@@ -12,6 +12,7 @@ revopRouter
|
|
|
12
12
|
.post( '/tagging', isAllowedSessionHandler, revoptagging )
|
|
13
13
|
.post( '/getrevoptagging', isAllowedSessionHandler, getrevoptagging )
|
|
14
14
|
.post( '/migrate-revop', migrateRevopIndex )
|
|
15
|
+
.post( '/expire-review-status', expireReviewStatus )
|
|
15
16
|
.post( '/revoptaggingcount', isAllowedSessionHandler, revoptaggingcount )
|
|
16
17
|
|
|
17
18
|
// new enhnacemnet (for footfall directory)
|