tango-app-api-store-builder 1.0.3-alpha → 1.0.4

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.
Files changed (62) hide show
  1. package/data/Bitz_Final_Store_List_28.08.25.xlsx +0 -0
  2. package/data/Coastline 3.0 Tango.xlsx +0 -0
  3. package/data/Fixture capacity.xlsx +0 -0
  4. package/data/JJ_OD New Launch_Tango.xlsx +0 -0
  5. package/data/LKST 98 - Inventory Analysis.xlsx +0 -0
  6. package/data/OE_Vs_NON_OE_UPDATED.xlsx +0 -0
  7. package/data/Sale, Non sale stores.xlsx +0 -0
  8. package/data/Updated IVM New Fixture Flow-v8.xlsx +0 -0
  9. package/data/VM_logic.xlsx +0 -0
  10. package/data/euro_center_stores_tentpole.xls +0 -0
  11. package/data/ivmLogic.json +6058 -0
  12. package/data/logs.json +3 -0
  13. package/data/missing_stores.json +3 -0
  14. package/data/response.json +2119 -0
  15. package/index.js +7 -1
  16. package/package.json +15 -4
  17. package/src/controllers/fixtureTemplate.controller.js +1767 -0
  18. package/src/controllers/managePlano.controller.js +1986 -0
  19. package/src/controllers/planoLibrary.controller.js +1487 -0
  20. package/src/controllers/script.controller.js +14680 -0
  21. package/src/controllers/storeBuilder.controller.js +7353 -15
  22. package/src/controllers/task.controller.js +1149 -0
  23. package/src/dtos/validation.dtos.js +277 -1
  24. package/src/routes/fixtureTemplate.routes.js +30 -0
  25. package/src/routes/managePlano.routes.js +29 -0
  26. package/src/routes/planoLibrary.routes.js +42 -0
  27. package/src/routes/script.routes.js +44 -0
  28. package/src/routes/storeBuilder.routes.js +55 -5
  29. package/src/routes/task.routes.js +20 -0
  30. package/src/service/assignService.service.js +11 -0
  31. package/src/service/checklist.service.js +7 -0
  32. package/src/service/fixtureConfig.service.js +52 -0
  33. package/src/service/fixtureConfigDuplicate.service.js +52 -0
  34. package/src/service/fixtureShelf.service.js +53 -0
  35. package/src/service/fixtureShelfDuplicate.service.js +53 -0
  36. package/src/service/planoCompliance.service.js +33 -0
  37. package/src/service/planoDuplicateModel.service.js +41 -0
  38. package/src/service/planoGlobalComment.service.js +25 -0
  39. package/src/service/planoLibrary.service.js +45 -0
  40. package/src/service/planoLibraryDuplicate.service.js +45 -0
  41. package/src/service/planoMapping.service.js +44 -0
  42. package/src/service/planoMappingDuplicate.service.js +44 -0
  43. package/src/service/planoProduct.service.js +42 -0
  44. package/src/service/planoProductDuplicate.service.js +42 -0
  45. package/src/service/planoQrConversionRequest.service.js +32 -0
  46. package/src/service/planoRevision.service.js +15 -0
  47. package/src/service/planoStaticData.service.js +11 -0
  48. package/src/service/planoTask.service.js +39 -0
  49. package/src/service/planoVm.service.js +49 -0
  50. package/src/service/planoVmDuplicate.service.js +49 -0
  51. package/src/service/planogram.service.js +8 -0
  52. package/src/service/planoproductCategory.service.js +47 -0
  53. package/src/service/processedTaskservice.js +29 -0
  54. package/src/service/processedchecklist.service.js +17 -0
  55. package/src/service/storeBuilder.service.js +20 -0
  56. package/src/service/storeBuilderDuplicate.service.js +53 -0
  57. package/src/service/storeFixture.service.js +83 -0
  58. package/src/service/storeFixtureDuplicate.service.js +69 -0
  59. package/src/service/task.service.js +6 -0
  60. package/src/service/templateLog.service.js +10 -0
  61. package/src/service/user.service.js +14 -0
  62. package/src/service/vmType.service.js +33 -0
@@ -0,0 +1,1986 @@
1
+ import * as floorService from '../service/storeBuilder.service.js';
2
+ import { logger, insertOpenSearchData, getOpenSearchData } from 'tango-app-api-middleware';
3
+ // import * as storeService from '../service/store.service.js';
4
+ import * as planoService from '../service/planogram.service.js';
5
+ import * as storeFixtureService from '../service/storeFixture.service.js';
6
+ import * as fixtureShelfService from '../service/fixtureShelf.service.js';
7
+ // import * as planoProductService from '../service/planoProduct.service.js';
8
+ import * as planoVmService from '../service/planoVm.service.js';
9
+ // import * as planoMappingService from '../service/planoMapping.service.js';
10
+ // import * as planoTaskService from '../service/planoTask.service.js';
11
+ import * as processedTaskService from '../service/processedTaskservice.js';
12
+ import * as planoproductCategoryService from '../service/planoproductCategory.service.js';
13
+ import * as fixtureConfigService from '../service/fixtureConfig.service.js';
14
+ import * as fixtureLibraryService from '../service/planoLibrary.service.js';
15
+ import * as planoTaskService from '../service/planoTask.service.js';
16
+ import * as planoGlobalCommentService from '../service/planoGlobalComment.service.js';
17
+ import mongoose from 'mongoose';
18
+ import * as planoRevisionService from '../service/planoRevision.service.js';
19
+ import * as planoVmDuplicateService from '../service/planoVmDuplicate.service.js';
20
+ import * as vmTypeService from '../service/vmType.service.js';
21
+ export async function getplanoFeedback( req, res ) {
22
+ try {
23
+ const taskTypes = req.body.filterByTask && req.body.filterByTask.length > 0 ? req.body.filterByTask : [ 'layout', 'fixture', 'vm' ];
24
+ const filterByStatus = req.body.filterByStatus || [];
25
+ const filterByApprovalStatus = req.body.filterByApprovalStatus || '';
26
+ const resultMap = {};
27
+ const commentMap = {};
28
+ await Promise.all(
29
+ taskTypes.map( async ( type, index ) => {
30
+ const pipeline = buildPipelineByType( type, req.body.planoId, req.body.floorId, filterByStatus, filterByApprovalStatus, req.body.showtask );
31
+
32
+ let data = await planoTaskService.aggregate( pipeline );
33
+ if ( filterByApprovalStatus && filterByApprovalStatus?.length ) {
34
+ if ( type == 'fixture' ) {
35
+ let pendingData = [];
36
+ let agreeData = [];
37
+ let disAgreeData = [];
38
+ if ( filterByApprovalStatus.includes( 'pending' ) ) {
39
+ pendingData = data.filter( ( element ) => {
40
+ return element?.approvalStatus == 'pending';
41
+ } );
42
+ } if ( filterByApprovalStatus.includes( 'agree' ) ) {
43
+ agreeData = data.filter( ( element ) => {
44
+ return element?.answers?.[0]?.status == 'agree';
45
+ } );
46
+ } if ( filterByApprovalStatus.includes( 'disagree' ) ) {
47
+ disAgreeData = data.filter( ( element ) => {
48
+ return element?.answers?.[0]?.status == 'disagree';
49
+ } );
50
+ }
51
+ data = [ ...pendingData, ...agreeData, ...disAgreeData ];
52
+ } else {
53
+ data.forEach( ( element ) => {
54
+ element.answers?.forEach( ( ans ) => {
55
+ ans.issues = ans.issues?.filter(
56
+ ( issue ) => issue.Details && issue.Details.length > 0,
57
+ );
58
+ } );
59
+ element.answers = element.answers?.filter(
60
+ ( ans ) => ans.issues && ans.issues.length > 0,
61
+ );
62
+ } );
63
+ }
64
+ data = data.filter(
65
+ ( element ) => element.answers && element.answers.length > 0,
66
+ );
67
+ }
68
+
69
+
70
+ resultMap[type] = data;
71
+
72
+ const comments = await planoGlobalCommentService.find( {
73
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
74
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
75
+ taskType: type,
76
+ } );
77
+ commentMap[type] = comments;
78
+ } ),
79
+ );
80
+
81
+ const response = {
82
+ layoutData: resultMap['layout'] || [],
83
+ fixtureData: resultMap['fixture'] || [],
84
+ VmData: resultMap['vm'] || [],
85
+ layoutComment: commentMap['layout'] || [],
86
+ fixtureComment: commentMap['fixture'] || [],
87
+ vmComment: commentMap['vm'] || [],
88
+ };
89
+
90
+ response.fixtureData = await Promise.all( response.fixtureData.map( async ( ele ) => {
91
+ if ( ele?.FixtureData && ele?.FixtureData?._id ) {
92
+ // if ( !ele?.FixtureData._id ) {
93
+ // return res.sendError( 'Fixture Id is required', 400 );
94
+ // }
95
+ let query = {
96
+ query: {
97
+ bool: {
98
+ must: [
99
+ {
100
+ term: {
101
+ fixtureId: ele?.FixtureData._id,
102
+ },
103
+ },
104
+ ],
105
+ },
106
+ },
107
+ sort: [
108
+ { storeDate: { order: 'desc' } },
109
+ ],
110
+ };
111
+
112
+
113
+ let aiDetails = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).planoAIValidation, query );
114
+ if ( aiDetails.statusCode == 200 ) {
115
+ let data = {};
116
+ if ( aiDetails?.body?.hits?.hits.length ) {
117
+ data = aiDetails?.body?.hits?.hits?.[0]?._source;
118
+ delete data.isEmpty;
119
+ delete data.fixtureFound;
120
+ }
121
+ ele.fixtureAIData = data;
122
+ }
123
+ }
124
+ return ele;
125
+ } ) );
126
+
127
+ res.sendSuccess( response );
128
+ } catch ( e ) {
129
+ logger.error( { functionName: 'getplanoFeedback', error: e, message: req.body } );
130
+ return res.sendError( e, 500 );
131
+ }
132
+ }
133
+ export async function getplanoFeedbackv1( req, res ) {
134
+ try {
135
+ const taskTypes = req.body.filterByTask && req.body.filterByTask.length > 0 ? req.body.filterByTask : [ 'layout', 'fixture', 'vm' ];
136
+ const filterByStatus = req.body.filterByStatus || [];
137
+ const filterByApprovalStatus = req.body.filterByApprovalStatus || '';
138
+ const resultMap = {};
139
+ const commentMap = {};
140
+ await Promise.all(
141
+ taskTypes.map( async ( type, index ) => {
142
+ const pipeline = buildPipelineByType1( type, req.body.planoId, req.body.floorId, filterByStatus, filterByApprovalStatus, req.body.showtask );
143
+
144
+ let data = await planoTaskService.aggregate( pipeline );
145
+ if ( filterByApprovalStatus && filterByApprovalStatus !== '' ) {
146
+ data.forEach( ( element ) => {
147
+ element.answers?.forEach( ( ans ) => {
148
+ ans.issues = ans.issues?.filter(
149
+ ( issue ) => issue.Details && issue.Details.length > 0,
150
+ );
151
+ } );
152
+ element.answers = element.answers?.filter(
153
+ ( ans ) => ans.issues && ans.issues.length > 0,
154
+ );
155
+ } );
156
+ data = data.filter(
157
+ ( element ) => element.answers && element.answers.length > 0,
158
+ );
159
+ }
160
+
161
+
162
+ resultMap[type] = data;
163
+
164
+ const comments = await planoGlobalCommentService.find( {
165
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
166
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
167
+ taskType: type,
168
+ } );
169
+ commentMap[type] = comments;
170
+ } ),
171
+ );
172
+
173
+ const response = {
174
+ layoutData: resultMap['layout'] || [],
175
+ fixtureData: resultMap['fixture'] || [],
176
+ VmData: resultMap['vm'] || [],
177
+ layoutComment: commentMap['layout'] || [],
178
+ fixtureComment: commentMap['fixture'] || [],
179
+ vmComment: commentMap['vm'] || [],
180
+ };
181
+
182
+ res.sendSuccess( response );
183
+ } catch ( e ) {
184
+ logger.error( { functionName: 'getplanoFeedback', error: e, message: req.body } );
185
+ return res.sendError( e, 500 );
186
+ }
187
+ }
188
+
189
+ function buildPipelineByType( type, planoId, floorId, filterByStatus, filterByApprovalStatus, showtask, taskId = 0 ) {
190
+ const matchStage = {
191
+ $match: {
192
+ planoId: new mongoose.Types.ObjectId( planoId ),
193
+ floorId: new mongoose.Types.ObjectId( floorId ),
194
+ type: type,
195
+ ...( taskId && { taskId: new mongoose.Types.ObjectId( taskId ) } ),
196
+ ...( filterByStatus?.length ? { status: { $in: filterByStatus } } : {} ),
197
+ },
198
+ };
199
+
200
+
201
+ const conditionalMatchExpr = showtask ?
202
+ {
203
+ $eq: [ '$_id', '$$taskId' ],
204
+ } :
205
+ {
206
+ $and: [
207
+ {
208
+ $eq: [ '$_id', '$$taskId' ],
209
+ },
210
+ {
211
+ $or: [
212
+ { $eq: [ '$redoStatus', true ] },
213
+ {
214
+ $and: [
215
+ { $eq: [ '$redoStatus', false ] },
216
+ { $eq: [ '$checklistStatus', 'submit' ] },
217
+ ],
218
+ },
219
+ ],
220
+ },
221
+ ],
222
+ };
223
+
224
+ const taskLookup = {
225
+ $lookup: {
226
+ from: 'processedtasks',
227
+ let: { taskId: '$taskId' },
228
+ pipeline: [
229
+ {
230
+ $match: {
231
+ $expr: conditionalMatchExpr,
232
+ },
233
+ },
234
+ {
235
+ $project: {
236
+ userName: 1,
237
+ createdAt: 1,
238
+ createdByName: 1,
239
+ submitTime_string: 1,
240
+ },
241
+ },
242
+ ],
243
+ as: 'taskData',
244
+ },
245
+ };
246
+
247
+
248
+ const unwindTask = { $unwind: { path: '$taskData', preserveNullAndEmptyArrays: false } };
249
+
250
+ const commonLookups = type === 'layout' ? [] : [
251
+ {
252
+ $lookup: {
253
+ from: 'storefixtures',
254
+ let: { fixtureId: '$fixtureId' },
255
+ pipeline: [
256
+ {
257
+ $match: { $expr: { $eq: [ '$_id', '$$fixtureId' ] } },
258
+ },
259
+ ],
260
+ as: 'FixtureData',
261
+ },
262
+ },
263
+ {
264
+ $unwind: { path: '$FixtureData', preserveNullAndEmptyArrays: true },
265
+ },
266
+ {
267
+ $lookup: {
268
+ from: 'fixtureshelves',
269
+ let: { fixtureId: '$FixtureData._id' },
270
+ pipeline: [
271
+ {
272
+ $match: { $expr: { $eq: [ '$fixtureId', '$$fixtureId' ] } },
273
+ },
274
+ ],
275
+ as: 'Fixtureshelves',
276
+ },
277
+ },
278
+
279
+ {
280
+ $set: {
281
+ 'FixtureData.shelfConfig': '$Fixtureshelves',
282
+ },
283
+ },
284
+ ];
285
+
286
+ const vmStages = [ 'vm', 'vmRollout' ].includes( type ) ? [
287
+ {
288
+ $unwind: { path: '$FixtureData.vmConfig', preserveNullAndEmptyArrays: true },
289
+ },
290
+ {
291
+ $lookup: {
292
+ from: 'planovmdetails',
293
+ let: { vmId: '$FixtureData.vmConfig.vmId' },
294
+ pipeline: [
295
+ {
296
+ $match: { $expr: { $eq: [ '$_id', '$$vmId' ] } },
297
+ },
298
+ {
299
+ $project: { vmName: 1, vmType: 1 },
300
+ },
301
+ ],
302
+ as: 'vmDetails',
303
+ },
304
+ },
305
+ {
306
+ $unwind: { path: '$vmDetails', preserveNullAndEmptyArrays: true },
307
+ },
308
+ {
309
+ $set: {
310
+ 'FixtureData.vmConfig.vmName': '$vmDetails.vmName',
311
+ 'FixtureData.vmConfig.vmType': '$vmDetails.vmType',
312
+ },
313
+ },
314
+ {
315
+ $group: {
316
+ _id: '$_id',
317
+ answers: { $first: '$answers' },
318
+ createdAt: { $first: '$createdAt' },
319
+ date_iso: { $first: '$date_iso' },
320
+ date_string: { $first: '$date_string' },
321
+ fixtureId: { $first: '$fixtureId' },
322
+ floorId: { $first: '$floorId' },
323
+ planoId: { $first: '$planoId' },
324
+ status: { $first: '$status' },
325
+ taskType: { $first: '$taskType' },
326
+ type: { $first: '$type' },
327
+ taskId: { $first: '$taskId' },
328
+ taskData: { $first: '$taskData' },
329
+ baseFixtureData: { $first: '$FixtureData' },
330
+ collectedVmConfigs: {
331
+ $push: {
332
+ $cond: [
333
+ { $ne: [ '$FixtureData.vmConfig', {} ] },
334
+ '$FixtureData.vmConfig',
335
+ '$$REMOVE',
336
+ ],
337
+ },
338
+ },
339
+ },
340
+ },
341
+ {
342
+ $set: {
343
+ FixtureData: {
344
+ $mergeObjects: [
345
+ '$baseFixtureData',
346
+ {
347
+ vmConfig: {
348
+ $reduce: {
349
+ input: '$collectedVmConfigs',
350
+ initialValue: [],
351
+ in: {
352
+ $cond: [
353
+ { $isArray: '$$this' },
354
+ { $concatArrays: [ '$$value', '$$this' ] },
355
+ { $concatArrays: [ '$$value', [ '$$this' ] ] },
356
+ ],
357
+ },
358
+ },
359
+ },
360
+ },
361
+ ],
362
+ },
363
+ },
364
+ },
365
+ ] : [];
366
+
367
+
368
+ let pipeline = [
369
+ matchStage,
370
+ taskLookup,
371
+ unwindTask,
372
+ ...commonLookups,
373
+ ...vmStages,
374
+ { $sort: { _id: -1 } },
375
+ ];
376
+ if ( filterByApprovalStatus && filterByApprovalStatus != '' && type !== 'fixture' ) {
377
+ pipeline = [];
378
+ pipeline.push( matchStage );
379
+ pipeline.push(
380
+ {
381
+ $addFields: {
382
+ answers: {
383
+ $map: {
384
+ input: '$answers',
385
+ as: 'ans',
386
+ in: {
387
+ $mergeObjects: [
388
+ '$$ans',
389
+ {
390
+ issues: {
391
+ $map: {
392
+ input: '$$ans.issues',
393
+ as: 'issue',
394
+ in: {
395
+ $mergeObjects: [
396
+ '$$issue',
397
+ {
398
+ Details: {
399
+ $filter: {
400
+ input: '$$issue.Details',
401
+ as: 'detail',
402
+ cond: filterByApprovalStatus === 'pending' ?
403
+ { $eq: [ '$$detail.status', 'pending' ] } :
404
+ { $ne: [ '$$detail.status', 'pending' ] },
405
+ },
406
+ },
407
+ },
408
+ ],
409
+ },
410
+ },
411
+ },
412
+ },
413
+ ],
414
+ },
415
+ },
416
+ },
417
+ },
418
+ },
419
+ );
420
+ pipeline.push( taskLookup );
421
+ pipeline.push( unwindTask );
422
+ pipeline.push( ...commonLookups );
423
+ pipeline.push( ...vmStages );
424
+ pipeline.push( { $sort: { _id: -1 } } );
425
+ }
426
+
427
+
428
+ return pipeline;
429
+ }
430
+
431
+ function buildPipelineByType1( type, planoId, floorId, filterByStatus, filterByApprovalStatus, showtask, taskId = 0 ) {
432
+ const matchStage = {
433
+ $match: {
434
+ planoId: new mongoose.Types.ObjectId( planoId ),
435
+ floorId: new mongoose.Types.ObjectId( floorId ),
436
+ type: type,
437
+ ...( taskId && { taskId: new mongoose.Types.ObjectId( taskId ) } ),
438
+ ...( filterByStatus?.length ? { status: { $in: filterByStatus } } : {} ),
439
+ },
440
+ };
441
+
442
+
443
+ const conditionalMatchExpr = showtask ?
444
+ {
445
+ $eq: [ '$_id', '$$taskId' ],
446
+ } :
447
+ {
448
+ $and: [
449
+ {
450
+ $eq: [ '$_id', '$$taskId' ],
451
+ },
452
+ {
453
+ $or: [
454
+ { $eq: [ '$redoStatus', true ] },
455
+ {
456
+ $and: [
457
+ { $eq: [ '$redoStatus', false ] },
458
+ { $eq: [ '$checklistStatus', 'submit' ] },
459
+ ],
460
+ },
461
+ ],
462
+ },
463
+ ],
464
+ };
465
+
466
+ const taskLookup = {
467
+ $lookup: {
468
+ from: 'processedtasks',
469
+ let: { taskId: '$taskId' },
470
+ pipeline: [
471
+ {
472
+ $match: {
473
+ $expr: conditionalMatchExpr,
474
+ },
475
+ },
476
+ {
477
+ $project: {
478
+ userName: 1,
479
+ createdAt: 1,
480
+ createdByName: 1,
481
+ submitTime_string: 1,
482
+ },
483
+ },
484
+ ],
485
+ as: 'taskData',
486
+ },
487
+ };
488
+
489
+
490
+ const unwindTask = { $unwind: { path: '$taskData', preserveNullAndEmptyArrays: false } };
491
+
492
+ const commonLookups = type === 'layout' ? [] : [
493
+ {
494
+ $lookup: {
495
+ from: 'storefixtureduplicates',
496
+ let: { fixtureId: '$fixtureId' },
497
+ pipeline: [
498
+ {
499
+ $match: { $expr: { $eq: [ '$_id', '$$fixtureId' ] } },
500
+ },
501
+ ],
502
+ as: 'FixtureData',
503
+ },
504
+ },
505
+ {
506
+ $unwind: { path: '$FixtureData', preserveNullAndEmptyArrays: true },
507
+ },
508
+ {
509
+ $lookup: {
510
+ from: 'fixtureshelfduplicates',
511
+ let: { fixtureId: '$FixtureData._id' },
512
+ pipeline: [
513
+ {
514
+ $match: { $expr: { $eq: [ '$fixtureId', '$$fixtureId' ] } },
515
+ },
516
+ ],
517
+ as: 'Fixtureshelves',
518
+ },
519
+ },
520
+
521
+ {
522
+ $set: {
523
+ 'FixtureData.shelfConfig': '$Fixtureshelves',
524
+ },
525
+ },
526
+ ];
527
+
528
+ const vmStages = [ 'vm', 'vmRollout' ].includes( type ) ? [
529
+ {
530
+ $unwind: { path: '$FixtureData.vmConfig', preserveNullAndEmptyArrays: true },
531
+ },
532
+ {
533
+ $lookup: {
534
+ from: 'planovmdetailduplicates',
535
+ let: { vmId: '$FixtureData.vmConfig.vmId' },
536
+ pipeline: [
537
+ {
538
+ $match: { $expr: { $eq: [ '$_id', '$$vmId' ] } },
539
+ },
540
+ {
541
+ $project: { vmName: 1, vmType: 1 },
542
+ },
543
+ ],
544
+ as: 'vmDetails',
545
+ },
546
+ },
547
+ {
548
+ $unwind: { path: '$vmDetails', preserveNullAndEmptyArrays: true },
549
+ },
550
+ {
551
+ $set: {
552
+ 'FixtureData.vmConfig.vmName': '$vmDetails.vmName',
553
+ 'FixtureData.vmConfig.vmType': '$vmDetails.vmType',
554
+ },
555
+ },
556
+ {
557
+ $group: {
558
+ _id: '$_id',
559
+ answers: { $first: '$answers' },
560
+ createdAt: { $first: '$createdAt' },
561
+ date_iso: { $first: '$date_iso' },
562
+ date_string: { $first: '$date_string' },
563
+ fixtureId: { $first: '$fixtureId' },
564
+ floorId: { $first: '$floorId' },
565
+ planoId: { $first: '$planoId' },
566
+ status: { $first: '$status' },
567
+ taskType: { $first: '$taskType' },
568
+ type: { $first: '$type' },
569
+ taskId: { $first: '$taskId' },
570
+ taskData: { $first: '$taskData' },
571
+ baseFixtureData: { $first: '$FixtureData' },
572
+ collectedVmConfigs: {
573
+ $push: {
574
+ $cond: [
575
+ { $ne: [ '$FixtureData.vmConfig', {} ] },
576
+ '$FixtureData.vmConfig',
577
+ '$$REMOVE',
578
+ ],
579
+ },
580
+ },
581
+ },
582
+ },
583
+ {
584
+ $set: {
585
+ FixtureData: {
586
+ $mergeObjects: [
587
+ '$baseFixtureData',
588
+ {
589
+ vmConfig: {
590
+ $reduce: {
591
+ input: '$collectedVmConfigs',
592
+ initialValue: [],
593
+ in: {
594
+ $cond: [
595
+ { $isArray: '$$this' },
596
+ { $concatArrays: [ '$$value', '$$this' ] },
597
+ { $concatArrays: [ '$$value', [ '$$this' ] ] },
598
+ ],
599
+ },
600
+ },
601
+ },
602
+ },
603
+ ],
604
+ },
605
+ },
606
+ },
607
+ ] : [];
608
+
609
+
610
+ let pipeline = [
611
+ matchStage,
612
+ taskLookup,
613
+ unwindTask,
614
+ ...commonLookups,
615
+ ...vmStages,
616
+ { $sort: { _id: -1 } },
617
+ ];
618
+ if ( filterByApprovalStatus && filterByApprovalStatus != '' ) {
619
+ pipeline = [];
620
+ pipeline.push( matchStage );
621
+ pipeline.push(
622
+ {
623
+ $addFields: {
624
+ answers: {
625
+ $map: {
626
+ input: '$answers',
627
+ as: 'ans',
628
+ in: {
629
+ $mergeObjects: [
630
+ '$$ans',
631
+ {
632
+ issues: {
633
+ $map: {
634
+ input: '$$ans.issues',
635
+ as: 'issue',
636
+ in: {
637
+ $mergeObjects: [
638
+ '$$issue',
639
+ {
640
+ Details: {
641
+ $filter: {
642
+ input: '$$issue.Details',
643
+ as: 'detail',
644
+ cond: filterByApprovalStatus === 'pending' ?
645
+ { $eq: [ '$$detail.status', 'pending' ] } :
646
+ { $ne: [ '$$detail.status', 'pending' ] },
647
+ },
648
+ },
649
+ },
650
+ ],
651
+ },
652
+ },
653
+ },
654
+ },
655
+ ],
656
+ },
657
+ },
658
+ },
659
+ },
660
+ },
661
+ );
662
+ pipeline.push( taskLookup );
663
+ pipeline.push( unwindTask );
664
+ pipeline.push( ...commonLookups );
665
+ pipeline.push( ...vmStages );
666
+ pipeline.push( { $sort: { _id: -1 } } );
667
+ }
668
+
669
+
670
+ return pipeline;
671
+ }
672
+
673
+ export async function getStoreFixturesfeedback( req, res ) {
674
+ try {
675
+ let query = [];
676
+
677
+
678
+ query.push( {
679
+ $match: {
680
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
681
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
682
+ type: { $ne: 'layout' },
683
+ },
684
+ },
685
+ {
686
+ $lookup: {
687
+ from: 'processedtasks',
688
+ let: { 'taskId': '$taskId' },
689
+ pipeline: [
690
+ {
691
+ $match: {
692
+ $expr: {
693
+ $and: [
694
+ { $eq: [ '$_id', '$$taskId' ] },
695
+ ],
696
+ },
697
+ },
698
+ },
699
+ {
700
+ $project: {
701
+ 'userName': 1,
702
+ 'createdAt': 1,
703
+ 'createdByName': 1,
704
+ 'submitTime_string': 1,
705
+ },
706
+ },
707
+ ],
708
+ as: 'taskData',
709
+ },
710
+
711
+ }, { $unwind: { path: '$taskData', preserveNullAndEmptyArrays: true } },
712
+ {
713
+ $lookup: {
714
+ from: 'fixtureconfigs',
715
+ let: { 'fixtureId': '$fixtureId' },
716
+ pipeline: [
717
+ {
718
+ $match: {
719
+ $expr: {
720
+ $and: [
721
+ { $eq: [ '$_id', '$$fixtureId' ] },
722
+ ],
723
+ },
724
+ },
725
+ },
726
+ ],
727
+ as: 'FixtureData',
728
+ },
729
+ },
730
+ {
731
+ $unwind: { path: '$FixtureData', preserveNullAndEmptyArrays: true },
732
+ },
733
+ );
734
+
735
+
736
+ let findPlanoCompliance = await planoTaskService.aggregate( query );
737
+ res.sendSuccess( { count: findPlanoCompliance.length, data: findPlanoCompliance } );
738
+ } catch ( e ) {
739
+ logger.error( { functionName: 'getplanoFeedbackFixture', error: e, message: req.body } );
740
+ return res.sendError( e, 500 );
741
+ }
742
+ }
743
+ export async function updateStorePlano( req, res ) {
744
+ try {
745
+ const { floorId, data } = req.body;
746
+
747
+ const floorData = await floorService.findOne( { _id: new mongoose.Types.ObjectId( floorId ) } );
748
+
749
+ const additionalMeta = {
750
+ clientId: '11',
751
+ storeId: floorData.toObject().storeId,
752
+ storeName: floorData.toObject().storeName,
753
+ planoId: floorData.toObject().planoId,
754
+ floorId: floorData.toObject()._id,
755
+ };
756
+
757
+ const layoutPolygon = JSON.parse( JSON.stringify( data.layoutPolygon ) );
758
+
759
+ layoutPolygon.forEach( ( element ) => {
760
+ delete element.fixtures;
761
+ } );
762
+
763
+ await floorService.updateOne( { _id: new mongoose.Types.ObjectId( floorId ) },
764
+ {
765
+ layoutPolygon: layoutPolygon,
766
+ ...( req.body?.editMode === true && { isEdited: true } ),
767
+ } );
768
+
769
+ const currentWallFixtures = data.layoutPolygon.flatMap( ( element ) =>
770
+ ( element.fixtures || [] ).map( ( fixture ) => fixture ),
771
+ );
772
+ const currentFloorFixtures = ( data.centerFixture || [] );
773
+ const currentFixtures = [ ...currentWallFixtures, ...currentFloorFixtures ];
774
+
775
+ const wallOtherElements = data.layoutPolygon.flatMap( ( element ) =>
776
+ ( element.otherElements || [] ).map( ( el ) => el ),
777
+ );
778
+ const floorOtherElements = ( data.otherElements || [] );
779
+ const currentOtherElements = [ ...wallOtherElements, ...floorOtherElements ];
780
+
781
+ const existingFixtures = await storeFixtureService.find( {
782
+ floorId: new mongoose.Types.ObjectId( floorId ),
783
+ fixtureType: { $ne: 'other' },
784
+ } );
785
+
786
+ const existingOtherElements = await storeFixtureService.find( {
787
+ floorId: new mongoose.Types.ObjectId( floorId ),
788
+ fixtureType: 'other',
789
+ } );
790
+
791
+ const currentOtherElementsIds = new Set( currentOtherElements.map( ( f ) => f._id ) );
792
+ const removedOtherElements = existingOtherElements.filter(
793
+ ( f ) => f._id && !currentOtherElementsIds.has( f._id.toString() ),
794
+ );
795
+
796
+ if ( removedOtherElements.length ) {
797
+ const otherElementIds = removedOtherElements.map( ( ele ) => ele.toObject()._id );
798
+ await storeFixtureService.deleteMany( { _id: { $in: otherElementIds } } );
799
+ }
800
+
801
+ const currentFixtureIds = new Set( currentFixtures.map( ( f ) => f._id ) );
802
+ const removedFixtures = existingFixtures.filter(
803
+ ( f ) => f._id && !currentFixtureIds.has( f._id.toString() ),
804
+ );
805
+
806
+ if ( removedFixtures.length ) {
807
+ const fixtureIds = removedFixtures.map( ( fixture ) => fixture.toObject()._id );
808
+ await storeFixtureService.deleteMany( { _id: { $in: fixtureIds } } );
809
+ await fixtureShelfService.deleteMany( { fixtureId: { $in: fixtureIds } } );
810
+ }
811
+
812
+ const newWallFixtures = currentWallFixtures.filter( ( fixture ) => fixture?._id?.startsWith( 'new' ) );
813
+ const newFloorFixtures = currentFloorFixtures.filter( ( fixture ) => fixture?._id?.startsWith( 'new' ) );
814
+
815
+ const newFixtures = [ ...newWallFixtures, ...newFloorFixtures ];
816
+
817
+ if ( newFixtures.length ) {
818
+ newFixtures.forEach( async ( fixture ) => {
819
+ delete fixture._id;
820
+ const fixturePayload = {
821
+ ...additionalMeta,
822
+ ...fixture,
823
+ };
824
+ const createdFixture = await storeFixtureService.create( fixturePayload );
825
+ fixture.shelfConfig.forEach( async ( shelf ) => {
826
+ delete shelf._id;
827
+ const shelfPayload = {
828
+ ...additionalMeta,
829
+ ...shelf,
830
+ fixtureId: createdFixture.toObject()._id,
831
+
832
+ };
833
+ await fixtureShelfService.create( shelfPayload );
834
+ } );
835
+ } );
836
+ }
837
+
838
+
839
+ const newOtherElements = currentOtherElements.filter( ( ele ) => ele?._id?.startsWith( 'new' ) );
840
+
841
+
842
+ currentOtherElements.forEach( async ( ele ) => {
843
+ if ( ele?._id && mongoose.Types.ObjectId.isValid( ele._id ) ) {
844
+ await storeFixtureService.upsertOne( { _id: new mongoose.Types.ObjectId( ele._id ) }, ele );
845
+ }
846
+ } );
847
+
848
+
849
+ if ( newOtherElements.length ) {
850
+ newOtherElements.forEach( async ( ele ) => {
851
+ delete ele._id;
852
+ const payload = {
853
+ ...ele,
854
+ ...additionalMeta,
855
+ };
856
+ await storeFixtureService.create( payload );
857
+ } );
858
+ }
859
+
860
+ currentFixtures.forEach( async ( fixture ) => {
861
+ if ( mongoose.Types.ObjectId.isValid( fixture._id ) ) {
862
+ const updatedFixture = await storeFixtureService.upsertOne( { _id: new mongoose.Types.ObjectId( fixture._id ) }, fixture );
863
+
864
+ await storeFixtureService.removeKeys( { fixtureType: 'floor', _id: fixture._id }, { $unset: { associatedElementType: '', associatedElementNumber: '' } } );
865
+
866
+ await fixtureShelfService.deleteMany( { fixtureId: new mongoose.Types.ObjectId( fixture._id ) } );
867
+
868
+ fixture.shelfConfig.forEach( async ( shelf ) => {
869
+ delete shelf._id;
870
+ const shelfPayload = {
871
+ ...additionalMeta,
872
+ ...shelf,
873
+ fixtureId: updatedFixture.toObject()._id,
874
+ };
875
+ await fixtureShelfService.create( shelfPayload );
876
+ } );
877
+ }
878
+ } );
879
+
880
+ await planoService.updateOne( { _id: floorData.toObject()?.planoId }, { $set: { updatedAt: new Date() } } );
881
+
882
+ res.sendSuccess( 'Updated Successfully' );
883
+ } catch ( e ) {
884
+ logger.error( { functionName: 'updateStorePlano', error: e } );
885
+ return res.sendError( e, 500 );
886
+ }
887
+ }
888
+ export async function fixtureList( req, res ) {
889
+ try {
890
+ let findData = await fixtureLibraryService.find( { clientId: req.query.clientId } );
891
+ if ( findData.length === 0 ) {
892
+ return res.sendError( 'nodata found', 204 );
893
+ }
894
+ res.sendSuccess( findData );
895
+ } catch ( e ) {
896
+ logger.error( { functionName: 'fixtureList', error: e } );
897
+ return res.sendError( e, 500 );
898
+ }
899
+ }
900
+ export async function templateList( req, res ) {
901
+ try {
902
+ let findData = await fixtureConfigService.find( { clientId: req.query.clientId, fixtureLibraryId: new mongoose.Types.ObjectId( req.query.fixtureId ) } );
903
+ if ( findData.length === 0 ) {
904
+ return res.sendError( 'nodata found', 204 );
905
+ }
906
+ res.sendSuccess( findData );
907
+ } catch ( e ) {
908
+ logger.error( { functionName: 'templateList', error: e } );
909
+ return res.sendError( e, 500 );
910
+ }
911
+ }
912
+ export async function fixtureBrandsList( req, res ) {
913
+ try {
914
+ let findData = await planoproductCategoryService.find( { clientId: req.query.clientId } );
915
+ if ( findData.length === 0 ) {
916
+ return res.sendError( 'nodata found', 204 );
917
+ }
918
+ res.sendSuccess( findData );
919
+ } catch ( e ) {
920
+ logger.error( { functionName: 'fixtureBrandsList', error: e } );
921
+ return res.sendError( e, 500 );
922
+ }
923
+ }
924
+
925
+ export async function fixtureVMList( req, res ) {
926
+ try {
927
+ let findData = await planoVmService.find( { clientId: req.query.clientId } );
928
+ if ( findData.length === 0 ) {
929
+ return res.sendError( 'nodata found', 204 );
930
+ }
931
+ res.sendSuccess( findData );
932
+ } catch ( e ) {
933
+ logger.error( { functionName: 'fixtureVMList', error: e } );
934
+ return res.sendError( e, 500 );
935
+ }
936
+ }
937
+
938
+ export async function fixtureVMListv1( req, res ) {
939
+ try {
940
+ let findData = await planoVmDuplicateService.find( { clientId: req.query.clientId } );
941
+ if ( findData.length === 0 ) {
942
+ return res.sendError( 'nodata found', 204 );
943
+ }
944
+ res.sendSuccess( findData );
945
+ } catch ( e ) {
946
+ logger.error( { functionName: 'fixtureVMListv1', error: e } );
947
+ return res.sendError( e, 500 );
948
+ }
949
+ }
950
+ export async function updateFixtureStatus( req, res ) {
951
+ try {
952
+ let comments = {
953
+ userId: req.user._id,
954
+ userName: req.user.userName,
955
+ role: req.user.role,
956
+ responsetype: req.body.type,
957
+ comment: req.body.comments,
958
+ };
959
+
960
+ if ( req.body.taskType.includes( 'fixture' ) ) {
961
+ await planoTaskService.updateOnefilters(
962
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
963
+ {
964
+ $set: { 'answers.$[ans].status': req.body.type },
965
+ },
966
+ [
967
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
968
+
969
+ ],
970
+ );
971
+ await planoTaskService.updateOnefilters(
972
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
973
+ {
974
+ $push: { 'answers.$[ans].comments': comments },
975
+ },
976
+ [
977
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
978
+ ] );
979
+
980
+ await planoTaskService.updateOne( { _id: req.body._id }, { approvalStatus: 'approved', taskType: 'initial' } );
981
+ let fixtureTask = await planoTaskService.find(
982
+ {
983
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
984
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
985
+ type: 'fixture',
986
+ },
987
+ );
988
+ if ( fixtureTask.length > 0 ) {
989
+ let allTaskDone = fixtureTask.filter( ( data ) => !data?.answers?.[0]?.status || data?.answers?.[0]?.status == 'disagree' );
990
+ if ( allTaskDone.length === 0 ) {
991
+ await floorService.updateOne( { _id: new mongoose.Types.ObjectId( req.body.floorId ) }, { planoProgress: 100 } );
992
+ }
993
+ }
994
+ } else {
995
+ let updateResponse = await planoTaskService.updateOnefilters(
996
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
997
+ {
998
+ $set: { 'answers.$[ans].issues.$[iss].Details.$[det].status': req.body.type },
999
+ },
1000
+ [
1001
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1002
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1003
+ { 'det._id': new mongoose.Types.ObjectId( req.body.DetailsId ) },
1004
+
1005
+ ] );
1006
+ if ( updateResponse && updateResponse.answers.length > 0 ) {
1007
+ let findissuse = updateResponse.answers[0].issues.filter( ( data ) => data._id == req.body.issueId );
1008
+ let findDetails = findissuse[0].Details.filter( ( det ) => det.status === 'agree' );
1009
+ if ( findissuse[0].Details.length === findDetails.length ) {
1010
+ await planoTaskService.updateOnefilters(
1011
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1012
+ {
1013
+ $set: { 'answers.$[ans].issues.$[iss].status': 'completed' },
1014
+ },
1015
+ [
1016
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1017
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1018
+ ] );
1019
+ }
1020
+ let findoneplanoData = await planoTaskService.findOne( { _id: new mongoose.Types.ObjectId( req.body._id ) } );
1021
+ let totalApproved = findoneplanoData.answers[0].issues.filter( ( data ) => data.status === 'pending' );
1022
+ if ( totalApproved.length === 0 ) {
1023
+ await planoTaskService.updateOne(
1024
+ {
1025
+ _id: new mongoose.Types.ObjectId( req.body._id ),
1026
+ },
1027
+ {
1028
+ 'status': 'complete',
1029
+ },
1030
+ );
1031
+ if ( req.body.taskType === 'layout' ) {
1032
+ await planoTaskService.updateMany(
1033
+ {
1034
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1035
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1036
+ type: 'layout',
1037
+ },
1038
+ {
1039
+ 'status': 'complete',
1040
+ },
1041
+ );
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ if ( req.body.taskType === 'layout' ) {
1047
+ await planoTaskService.updateOnefilters(
1048
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1049
+ {
1050
+ $push: { 'answers.$[ans].issues.$[iss].Details.$[det].comments': comments },
1051
+ },
1052
+ [
1053
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1054
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1055
+ { 'det._id': new mongoose.Types.ObjectId( req.body.DetailsId ) },
1056
+
1057
+ ] );
1058
+ } else {
1059
+ await planoTaskService.updateOnefilters(
1060
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1061
+ {
1062
+ $push: { 'answers.$[ans].issues.$[iss].comments': comments },
1063
+ },
1064
+ [
1065
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1066
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1067
+ ] );
1068
+ }
1069
+ let vmTask = await planoTaskService.find(
1070
+ {
1071
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1072
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1073
+ type: 'vm',
1074
+ },
1075
+
1076
+ );
1077
+ if ( vmTask.length > 0 ) {
1078
+ let allTaskDone = vmTask.filter( ( data ) => data.status === 'incomplete' );
1079
+ if ( allTaskDone.length === 0 ) {
1080
+ await floorService.updateOne( { _id: new mongoose.Types.ObjectId( req.body.floorId ) }, { planoProgress: 100 } );
1081
+ }
1082
+ }
1083
+ }
1084
+ res.sendSuccess( 'updated successfully' );
1085
+ } catch ( e ) {
1086
+ logger.error( { functionName: 'updateFixtureStatus', error: e } );
1087
+ return res.sendError( e, 500 );
1088
+ }
1089
+ }
1090
+
1091
+ export async function updateApprovalStatus( req, res ) {
1092
+ try {
1093
+ await planoTaskService.updateOnefilters(
1094
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1095
+ { $set: { approvalStatus: 'approved' } } );
1096
+ res.sendSuccess( 'updated successfully' );
1097
+ } catch ( e ) {
1098
+ logger.error( { functionName: 'updateApprovalStatus', error: e } );
1099
+ return res.sendError( e, 500 );
1100
+ }
1101
+ }
1102
+ export async function updateRolloutStatus( req, res ) {
1103
+ try {
1104
+ let comments = {
1105
+ userId: req.user._id,
1106
+ userName: req.user.userName,
1107
+ role: req.user.role,
1108
+ responsetype: req.body.type,
1109
+ comment: req.body.comments,
1110
+ };
1111
+
1112
+ if ( req.body.issueId && req.body.DetailsId ) {
1113
+ let updateResponse = await planoTaskService.updateOnefilters(
1114
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1115
+ {
1116
+ $set: {
1117
+ 'answers.$[ans].issues.$[iss].Details.$[det].status': req.body.type,
1118
+ 'answers.$[ans].issues.$[iss].Details.$[det].adminStatus': req.body.type === 'agree' ? 'approved' : 'Unapproved',
1119
+ },
1120
+ },
1121
+ [
1122
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1123
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1124
+ { 'det._id': new mongoose.Types.ObjectId( req.body.DetailsId ) },
1125
+
1126
+ ],
1127
+ );
1128
+ if ( updateResponse && updateResponse.answers.length > 0 ) {
1129
+ let findissuse = updateResponse.answers[0].issues.filter( ( data ) => data._id == req.body.issueId );
1130
+ let findDetails = findissuse[0].Details.filter( ( det ) => det.status === 'agree' );
1131
+ if ( findissuse[0].Details.length === findDetails.length ) {
1132
+ await planoTaskService.updateOnefilters(
1133
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1134
+ {
1135
+ $set: { 'answers.$[ans].issues.$[iss].status': 'completed' },
1136
+ },
1137
+ [
1138
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1139
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1140
+ ] );
1141
+ }
1142
+ let findoneplanoData = await planoTaskService.findOne( { _id: new mongoose.Types.ObjectId( req.body._id ) } );
1143
+ let totalApproved = findoneplanoData.answers[0].issues.filter( ( data ) => data.status === 'pending' );
1144
+ if ( totalApproved.length === 0 ) {
1145
+ await planoTaskService.updateOne(
1146
+ {
1147
+ _id: new mongoose.Types.ObjectId( req.body._id ),
1148
+ },
1149
+ {
1150
+ 'status': 'complete',
1151
+ 'approvalStatus': 'approved',
1152
+ },
1153
+ );
1154
+ let data = {};
1155
+ if ( req.body.taskType == 'vmRollout' ) {
1156
+ data['isVmEdited'] = false;
1157
+ } else {
1158
+ data['isMerchEdited'] = false;
1159
+ }
1160
+ await storeFixtureService.updateOne( { _id: updateResponse.fixtureId }, data );
1161
+ }
1162
+ }
1163
+ await planoTaskService.updateOnefilters(
1164
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1165
+ {
1166
+ $push: { 'answers.$[ans].issues.$[iss].comments': comments },
1167
+ },
1168
+ [
1169
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1170
+ { 'iss._id': new mongoose.Types.ObjectId( req.body.issueId ) },
1171
+ ],
1172
+ );
1173
+ } else {
1174
+ let updateResponse = await planoTaskService.updateOnefilters(
1175
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1176
+ {
1177
+ $set: {
1178
+ 'answers.$[ans].status': req.body.type,
1179
+ },
1180
+ },
1181
+ [
1182
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1183
+ ],
1184
+ );
1185
+ await planoTaskService.updateOnefilters(
1186
+ { _id: new mongoose.Types.ObjectId( req.body._id ) },
1187
+ {
1188
+ $push: { 'answers.$[ans].comments': comments },
1189
+ },
1190
+ [
1191
+ { 'ans._id': new mongoose.Types.ObjectId( req.body.answerId ) },
1192
+ ],
1193
+ );
1194
+ if ( req.body.type == 'agree' ) {
1195
+ await planoTaskService.updateOne(
1196
+ {
1197
+ _id: new mongoose.Types.ObjectId( req.body._id ),
1198
+ },
1199
+ {
1200
+ 'approvalStatus': 'approved',
1201
+ },
1202
+ );
1203
+ let data = {};
1204
+ if ( req.body.taskType == 'vmRollout' ) {
1205
+ data['isVmEdited'] = false;
1206
+ } else {
1207
+ data['isMerchEdited'] = false;
1208
+ }
1209
+ await storeFixtureService.updateOne( { _id: updateResponse.fixtureId }, data );
1210
+ }
1211
+ }
1212
+
1213
+ res.sendSuccess( 'updated successfully' );
1214
+ } catch ( e ) {
1215
+ logger.error( { functionName: 'updateFixtureStatus', error: e } );
1216
+ return res.sendError( e, 500 );
1217
+ }
1218
+ }
1219
+
1220
+
1221
+ export async function updateStoreFixture( req, res ) {
1222
+ try {
1223
+ const { fixtureId, data } = req.body;
1224
+
1225
+ let configId;
1226
+
1227
+ const currentFixture = await storeFixtureService.findOne( { _id: new mongoose.Types.ObjectId( fixtureId ) } );
1228
+ let currentFixtureDoc = currentFixture.toObject();
1229
+ if ( req?.body?.taskType?.includes( 'fixture' ) || data?.taskType?.includes( 'fixture' ) ) {
1230
+ let shelfDetails = await fixtureShelfService.find( { fixtureId: fixtureId } );
1231
+ data.shelfConfig.forEach( ( ele ) => {
1232
+ let findShelfProduct = shelfDetails.find( ( shelf ) => shelf.shelfNumber == ele.shelfNumber );
1233
+ if ( !ele?.zone ) {
1234
+ ele.zone = findShelfProduct?.zone ?? 'Bottom';
1235
+ }
1236
+ if ( !ele?.productBrandName ) {
1237
+ ele.productBrandName = findShelfProduct?.productBrandName ?? [];
1238
+ }
1239
+ if ( !ele?.productCategory ) {
1240
+ ele.productCategory = findShelfProduct?.productCategory ?? [];
1241
+ }
1242
+ if ( !ele?.productSubCategory ) {
1243
+ ele.productSubCategory = findShelfProduct?.productSubCategory ?? [];
1244
+ }
1245
+ } );
1246
+ let vmConfig = [];
1247
+ for ( let vm of data.vmConfig ) {
1248
+ let checkvmType = await vmTypeService.findOne( { vmType: vm.vmType } );
1249
+ if ( !checkvmType ) {
1250
+ let vmData = {
1251
+ vmType: vm.vmType,
1252
+ imageUrls: [ vm.vmImage ],
1253
+ clientId: '11',
1254
+ };
1255
+ checkvmType = await vmTypeService.create( vmData );
1256
+ }
1257
+ let vmDetails = await planoVmService.findOne( { ...( vm.vmName ) ? { vmName: vm.vmName } : { _id: vm.vmId } } );
1258
+ if ( !vmDetails ) {
1259
+ let planovmData = {
1260
+ vmType: vm.vmType,
1261
+ vmName: vm.vmName,
1262
+ clientId: '11',
1263
+ status: 'complete',
1264
+ vmHeight: {
1265
+ 'value': 100,
1266
+ 'unit': 'mm',
1267
+ },
1268
+ vmWidth: {
1269
+ 'value': 100,
1270
+ 'unit': 'mm',
1271
+ },
1272
+ vmImageUrl: vm.vmImage,
1273
+ };
1274
+ vmDetails = await planoVmService.create( planovmData );
1275
+ }
1276
+ vmConfig.push( { ...vm, vmId: vmDetails._id } );
1277
+ }
1278
+ data.vmConfig = vmConfig;
1279
+ let groupName = {
1280
+ 'Vincent Chase Lenskart air': 'VC Eye / LK Air',
1281
+ 'John Jacobs': 'JJ Eye',
1282
+ 'Vincent Chase': 'VC Eye',
1283
+ 'OwnDays': 'OD Eye',
1284
+ };
1285
+ let header = ( data?.header?.label && data?.header?.label != 'Unknown' ) ? groupName?.[data?.header?.label] ? groupName?.[data?.header?.label] : data?.header?.label : currentFixture.header?.label;
1286
+ let mapKey = `${data.fixtureCategory}${currentFixtureDoc.fixtureWidth.value}${currentFixtureDoc.fixtureWidth.unit}${header}`;
1287
+ mapKey += data.shelfConfig.map( ( ele ) => `${ele.shelfNumber}=${ele.productBrandName.toString()}` ).join( '+' );
1288
+ data?.vmConfig?.forEach( ( ele ) => {
1289
+ mapKey += `${ele.vmName}${ele.startYPosition}${ele.endYPosition}${ele.xZone}${ele.yZone}`;
1290
+ } );
1291
+ let query = [
1292
+ {
1293
+ $addFields: {
1294
+ crestMapKey: { $toLower: '$crestMapKey' },
1295
+ },
1296
+ },
1297
+ {
1298
+ $match: {
1299
+ crestMapKey: mapKey.toLowerCase(),
1300
+ templateType: 'sub',
1301
+ },
1302
+ },
1303
+ ];
1304
+ let masterMapKey = `${data.fixtureCategory}${currentFixtureDoc.fixtureWidth.value}${currentFixtureDoc.fixtureWidth.unit}${header}`;
1305
+ let masterQuery = [
1306
+ {
1307
+ $addFields: {
1308
+ crestMapKey: { $toLower: '$crestMapKey' },
1309
+ },
1310
+ },
1311
+ {
1312
+ $match: {
1313
+ crestMapKey: masterMapKey.toLowerCase(),
1314
+ templateType: 'master',
1315
+ },
1316
+ },
1317
+ ];
1318
+ let [ fixturesubTemplate, fixtureMasterTemplate ] = await Promise.all( [
1319
+ await fixtureConfigService.aggregate( query ),
1320
+ await fixtureConfigService.aggregate( masterQuery ),
1321
+ ] );
1322
+
1323
+ if ( !fixturesubTemplate.length ) {
1324
+ let tempGroupName = ( data?.header?.label && data?.header?.label != 'Unknown' ) ? groupName?.[data?.header?.label] ? groupName?.[data?.header?.label] : data?.header?.label : currentFixture.templateGroupName;
1325
+ let templateIndex = 1;
1326
+ let templateData = {
1327
+ ...currentFixtureDoc,
1328
+ ...data,
1329
+ templateGroupName: tempGroupName,
1330
+ fixtureLibraryId: currentFixture.fixtureLibraryId,
1331
+ status: 'complete',
1332
+ templateIndex: fixtureMasterTemplate.length ? fixtureMasterTemplate.length + 1 : 1,
1333
+ crestMapKey: mapKey,
1334
+ };
1335
+ delete templateData._id;
1336
+ delete templateData.masterTemplateId;
1337
+ if ( !fixtureMasterTemplate.length ) {
1338
+ let masterTemplate = {
1339
+ ...templateData,
1340
+ fixtureName: `${templateData.header.label}-${templateData.fixtureCategory}`,
1341
+ templateType: 'master',
1342
+ crestMapKey: masterMapKey,
1343
+ };
1344
+ fixtureMasterTemplate = await fixtureConfigService.create( masterTemplate );
1345
+ } else {
1346
+ fixtureMasterTemplate = fixtureMasterTemplate[0];
1347
+ let tempQuery = [
1348
+ {
1349
+ $match: {
1350
+ masterTemplateId: new mongoose.Types.ObjectId( fixtureMasterTemplate._id ),
1351
+ },
1352
+ },
1353
+ {
1354
+ $group: {
1355
+ _id: '',
1356
+ tempId: { $max: '$templateIndex' },
1357
+ },
1358
+ },
1359
+ ];
1360
+ let getMaxTemp = await fixtureConfigService.aggregate( tempQuery );
1361
+ templateIndex = getMaxTemp?.[0]?.tempId + 1;
1362
+ }
1363
+ templateData.fixtureName= `${header}-${data.fixtureCategory}-variant-${templateIndex}`,
1364
+ templateData.templateType = 'sub';
1365
+ templateData.masterTemplateId = fixtureMasterTemplate._id;
1366
+ templateData.templateIndex = templateIndex;
1367
+ templateData.createdAt = new Date();
1368
+ templateData.updatedAt = new Date();
1369
+ let subTemplate = await fixtureConfigService.create( templateData );
1370
+ configId = subTemplate._id;
1371
+
1372
+ data.fixtureConfigId = subTemplate._id;
1373
+ data.masterTemplateId = fixtureMasterTemplate._id;
1374
+ } else {
1375
+ data.fixtureConfigId = fixturesubTemplate?.[0]?._id;
1376
+ }
1377
+ }
1378
+
1379
+ const productBrandName = new Set();
1380
+ const productCategory = new Set();
1381
+ const productSubCategory = new Set();
1382
+
1383
+ let fixtureCapacity = 0;
1384
+
1385
+ data.shelfConfig.forEach( ( shelf ) => {
1386
+ const { productBrandName: brand, productCategory: category, productSubCategory: subCategory } = shelf;
1387
+
1388
+ if ( typeof shelf?.productPerShelf === 'number' ) {
1389
+ if ( shelf?.shelfType === 'shelf' ) {
1390
+ fixtureCapacity += shelf.productPerShelf;
1391
+ }
1392
+
1393
+ if ( shelf?.shelfType === 'tray' ) {
1394
+ fixtureCapacity += ( shelf?.productPerShelf * shelf?.trayRows );
1395
+ }
1396
+ }
1397
+
1398
+ if ( Array.isArray( brand ) ) {
1399
+ brand.forEach( ( b ) => productBrandName.add( b ) );
1400
+ }
1401
+
1402
+ if ( Array.isArray( category ) ) {
1403
+ category.forEach( ( c ) => productCategory.add( c ) );
1404
+ }
1405
+
1406
+ if ( Array.isArray( subCategory ) ) {
1407
+ subCategory.forEach( ( s ) => productSubCategory.add( s ) );
1408
+ }
1409
+ } );
1410
+
1411
+
1412
+ if ( data?.fixtureConfigId && currentFixtureDoc.fixtureConfigId.toString() !== data.fixtureConfigId ) {
1413
+ const newTemplate = await fixtureConfigService.findOne( { _id: data.fixtureConfigId } );
1414
+ currentFixtureDoc = {
1415
+ ...currentFixtureDoc,
1416
+ ...newTemplate.toObject(),
1417
+ fixtureConfigId: newTemplate.toObject()._id,
1418
+ fixtureCapacity: fixtureCapacity,
1419
+ productBrandName: [ ...productBrandName ],
1420
+ productCategory: [ ...productCategory ],
1421
+ productSubCategory: [ ...productSubCategory ],
1422
+ };
1423
+ } else {
1424
+ currentFixtureDoc = {
1425
+ ...currentFixtureDoc,
1426
+ ...data,
1427
+ fixtureCapacity: fixtureCapacity,
1428
+ productBrandName: [ ...productBrandName ],
1429
+ productCategory: [ ...productCategory ],
1430
+ productSubCategory: [ ...productSubCategory ],
1431
+ };
1432
+ }
1433
+
1434
+ if ( data.productResolutionLevel == 'L1' ) {
1435
+ currentFixtureDoc.productBrandName = [ ...new Set( data.productBrandName.map( ( ele ) => ele ) ) ];
1436
+ currentFixtureDoc.productCategory = [ ...new Set( data.productCategory.map( ( ele ) => ele ) ) ];
1437
+ currentFixtureDoc.productSubCategory = [ ...new Set( data.productSubCategory.map( ( ele ) => ele ) ) ];
1438
+ }
1439
+
1440
+
1441
+ delete currentFixtureDoc._id;
1442
+
1443
+
1444
+ await storeFixtureService.updateOne( { _id: new mongoose.Types.ObjectId( fixtureId ) }, currentFixtureDoc );
1445
+
1446
+ if ( data?.shelfConfig?.length ) {
1447
+ await fixtureShelfService.deleteMany( { fixtureId: new mongoose.Types.ObjectId( fixtureId ) } );
1448
+
1449
+
1450
+ data.shelfConfig.forEach( async ( shelf ) => {
1451
+ delete shelf?._id;
1452
+ const additionalMeta = {
1453
+ clientId: currentFixture.clientId,
1454
+ storeId: currentFixture.storeId,
1455
+ storeName: currentFixture.storeName,
1456
+ planoId: currentFixture.planoId,
1457
+ floorId: currentFixture.floorId,
1458
+ fixtureId: currentFixture._id,
1459
+ };
1460
+
1461
+ await fixtureShelfService.create( { ...additionalMeta, ...shelf } );
1462
+ } );
1463
+ }
1464
+ await planoService.updateOne( { _id: currentFixtureDoc?.planoId }, { $set: { updatedAt: new Date() } } );
1465
+ if ( data?.taskType?.includes( 'fixture' ) ) {
1466
+ let comments = {
1467
+ userId: req.user._id,
1468
+ userName: req.user.userName,
1469
+ role: req.user.role,
1470
+ responsetype: 'agree',
1471
+ comment: '',
1472
+ };
1473
+ await planoTaskService.updateOne( { planoId: currentFixture.planoId, floorId: currentFixture.floorId, fixtureId: currentFixture._id }, { 'answers.0.status': 'agree', 'approvalStatus': 'approved' } );
1474
+ await planoTaskService.updateOnefilters(
1475
+ { planoId: new mongoose.Types.ObjectId( currentFixture.planoId ), floorId: new mongoose.Types.ObjectId( currentFixture.floorId ), fixtureId: new mongoose.Types.ObjectId( currentFixture._id ) },
1476
+ {
1477
+ $push: { 'answers.0.comments': comments },
1478
+ },
1479
+ );
1480
+ }
1481
+ if ( req.body?.editMode ) {
1482
+ await floorService.updateOne( { _id: new mongoose.Types.ObjectId( currentFixture.floorId ) }, { isEdited: true } );
1483
+ }
1484
+ res.sendSuccess( { message: 'Updated Successfully', ...( configId && { id: configId } ) } );
1485
+ } catch ( e ) {
1486
+ logger.error( { functionName: 'updateStoreFixture', error: e } );
1487
+ return res.sendError( e, 500 );
1488
+ }
1489
+ }
1490
+
1491
+ export async function updateredostatus( req, res ) {
1492
+ try {
1493
+ if ( req.body.type === 'layout' ) {
1494
+ await planoTaskService.updateOne(
1495
+ {
1496
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1497
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1498
+ type: req.body.type,
1499
+ },
1500
+ {
1501
+ 'answers.$[].issues.$[].status': 'completed',
1502
+ 'answers.$[].issues.$[].Details.$[].status': 'agree',
1503
+ 'status': 'complete',
1504
+ },
1505
+ );
1506
+ } else {
1507
+ await planoTaskService.updateOne(
1508
+ {
1509
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1510
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1511
+ fixtureId: new mongoose.Types.ObjectId( req.body.fixtureId ),
1512
+ type: req.body.type,
1513
+ },
1514
+ {
1515
+ 'answers.$[].issues.$[].status': 'completed',
1516
+ 'answers.$[].issues.$[].Details.$[].status': 'agree',
1517
+ 'status': 'complete',
1518
+ },
1519
+ );
1520
+ }
1521
+ res.sendSuccess( 'updated successfully' );
1522
+ } catch ( e ) {
1523
+ logger.error( { functionName: 'updateredostatus', error: e } );
1524
+ return res.sendError( e, 500 );
1525
+ }
1526
+ }
1527
+
1528
+
1529
+ export async function updateGlobalComment( req, res ) {
1530
+ try {
1531
+ let payload = {
1532
+ userId: req.user._id,
1533
+ userName: req.user.userName,
1534
+ comment: req.body.comment,
1535
+ responsetype: req.body.responsetype,
1536
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1537
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1538
+ taskType: req.body.taskType,
1539
+ clientId: req.body.clientId,
1540
+ taskId: req.body.taskId,
1541
+ };
1542
+ await planoGlobalCommentService.create( payload );
1543
+ await insertOpenSearchData( JSON.parse( process.env.OPENSEARCH ).planoglobalcomments, payload );
1544
+
1545
+ res.sendSuccess( 'updated successfully' );
1546
+ } catch ( e ) {
1547
+ logger.error( { functionName: 'updateGlobalComment', error: e } );
1548
+ return res.sendError( e, 500 );
1549
+ }
1550
+ }
1551
+ export async function getGlobalComment( req, res ) {
1552
+ try {
1553
+ // let layoutComment = await planoGlobalCommentService.find( {
1554
+ // planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1555
+ // floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1556
+ // taskType: req.body.taskType,
1557
+ // } );
1558
+
1559
+ let taskId = [];
1560
+ if ( req.body?.taskId ) {
1561
+ taskId.push( new mongoose.Types.ObjectId( req.body.taskId ) );
1562
+ }
1563
+
1564
+ if ( req.body?.refTaskId ) {
1565
+ taskId.push( new mongoose.Types.ObjectId( req.body.refTaskId ) );
1566
+ }
1567
+
1568
+ let layoutComment = await planoGlobalCommentService.find( {
1569
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1570
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1571
+ ...( taskId.length && { taskId: { $in: taskId } } ),
1572
+ taskType: req.body.taskType,
1573
+ } );
1574
+
1575
+ res.sendSuccess( layoutComment );
1576
+ } catch ( e ) {
1577
+ logger.error( { functionName: 'getGlobalComment', error: e } );
1578
+ return res.sendError( e, 500 );
1579
+ }
1580
+ }
1581
+
1582
+ export async function getAllPlanoRevisions( req, res ) {
1583
+ try {
1584
+ const { clientId } = req.body;
1585
+
1586
+ if ( !clientId ) {
1587
+ return res.sendError( 'Client Id is required', 400 );
1588
+ }
1589
+
1590
+ const revisions = await planoRevisionService.find(
1591
+ { clientId },
1592
+ { storeName: 1, storeId: 1, planoId: 1, floorId: 1, createdAt: 1 },
1593
+ );
1594
+
1595
+ res.sendSuccess( revisions );
1596
+ } catch ( e ) {
1597
+ logger.error( { functionName: 'getAllPlanoRevisions', error: e } );
1598
+ res.sendError( 'Failed to fetch plano revisions', 500 );
1599
+ }
1600
+ }
1601
+
1602
+ export async function createPlanoRevision( req, res ) {
1603
+ try {
1604
+ const { storeName, storeId, clientId, planoId, floorId, floorData } = req.body;
1605
+
1606
+ if ( !storeName || !storeId || !clientId || !planoId || !floorId || !floorData ) {
1607
+ return res.sendError( 'Missing required fields', 400 );
1608
+ }
1609
+
1610
+ const newRevision = await planoRevisionService.create( {
1611
+ storeName,
1612
+ storeId,
1613
+ clientId,
1614
+ planoId,
1615
+ floorId,
1616
+ floorData,
1617
+ } );
1618
+
1619
+ res.sendSuccess( newRevision );
1620
+ } catch ( e ) {
1621
+ logger.error( { functionName: 'createPlanoRevision', error: e } );
1622
+ res.sendError( 'Failed to create plano revision', 500 );
1623
+ }
1624
+ }
1625
+
1626
+ export async function getPlanoRevisionById( req, res ) {
1627
+ try {
1628
+ const { id } = req.params;
1629
+
1630
+ if ( !id ) {
1631
+ return res.sendError( 'Revision ID is required', 400 );
1632
+ }
1633
+
1634
+ const revision = await planoRevisionService.findOne(
1635
+ { _id: id },
1636
+ );
1637
+
1638
+ if ( !revision ) {
1639
+ return res.sendError( 'Plano revision not found', 404 );
1640
+ }
1641
+
1642
+ res.sendSuccess( revision );
1643
+ } catch ( e ) {
1644
+ logger.error( { functionName: 'getPlanoRevisionById', error: e } );
1645
+ res.sendError( 'Failed to fetch plano revision', 500 );
1646
+ }
1647
+ }
1648
+ export async function getRolloutFeedback( req, res ) {
1649
+ try {
1650
+ const taskTypes = req.body.filterByTask && req.body.filterByTask.length > 0 ? req.body.filterByTask : [ 'merchRollout', 'vmRollout' ];
1651
+ const filterByStatus = req.body.filterByStatus || [];
1652
+ const filterByApprovalStatus = req.body.filterByApprovalStatus || '';
1653
+ const resultMap = {};
1654
+ const commentMap = {};
1655
+ if ( req.body.redo ) {
1656
+ let taskDetails = await planoTaskService.findOne( { taskId: req.body.taskId, fixtureId: req.body.fixtureId } );
1657
+ if ( !taskDetails ) {
1658
+ req.body.taskId = req.body.refTaskId;
1659
+ }
1660
+ }
1661
+ await Promise.all(
1662
+ taskTypes.map( async ( type, index ) => {
1663
+ const pipeline = buildPipelineByType( type, req.body.planoId, req.body.floorId, filterByStatus, filterByApprovalStatus, req.body.showtask, req.body.taskId );
1664
+
1665
+ let data = await planoTaskService.aggregate( pipeline );
1666
+ if ( filterByApprovalStatus && filterByApprovalStatus !== '' ) {
1667
+ data.forEach( ( element ) => {
1668
+ element.answers?.forEach( ( ans ) => {
1669
+ ans.issues = ans.issues?.filter(
1670
+ ( issue ) => issue.Details && issue.Details.length > 0,
1671
+ );
1672
+ } );
1673
+ element.answers = element.answers?.filter(
1674
+ ( ans ) => ans.issues && ans.issues.length > 0,
1675
+ );
1676
+ } );
1677
+ data = data.filter(
1678
+ ( element ) => element.answers && element.answers.length > 0,
1679
+ );
1680
+ }
1681
+
1682
+
1683
+ resultMap[type] = data;
1684
+
1685
+ let taskId = [];
1686
+ if ( req.body.taskId ) {
1687
+ taskId.push( new mongoose.Types.ObjectId( req.body.taskId ) );
1688
+ }
1689
+ if ( req.body.refTaskId ) {
1690
+ taskId.push( new mongoose.Types.ObjectId( req.body.refTaskId ) );
1691
+ }
1692
+
1693
+ const comments = await planoGlobalCommentService.find( {
1694
+ planoId: new mongoose.Types.ObjectId( req.body.planoId ),
1695
+ floorId: new mongoose.Types.ObjectId( req.body.floorId ),
1696
+ ...( taskId.length && { taskId: { $in: taskId } } ),
1697
+ taskType: type,
1698
+ } );
1699
+ commentMap[type] = comments;
1700
+ } ),
1701
+ );
1702
+ const response = {
1703
+ merchRolloutData: resultMap['merchRollout'] || [],
1704
+ vmRolloutData: resultMap['vmRollout'] || [],
1705
+ merchRolloutComment: commentMap['merchRollout'] || [],
1706
+ vmRolloutComment: commentMap['vmRollout'] || [],
1707
+ };
1708
+
1709
+ res.sendSuccess( response );
1710
+ } catch ( e ) {
1711
+ logger.error( { functionName: 'getRolloutFeedback', error: e } );
1712
+ return res.sendError( e, 500 );
1713
+ }
1714
+ }
1715
+
1716
+
1717
+ export async function getRolloutFeedbackv2( req, res ) {
1718
+ try {
1719
+ const taskTypes = req.body.filterByTask && req.body.filterByTask.length > 0 ? req.body.filterByTask : [ 'merchRollout', 'vmRollout' ];
1720
+ const filterByStatus = req.body.filterByStatus || [];
1721
+ const filterByApprovalStatus = req.body.filterByApprovalStatus || '';
1722
+ const resultMap = {};
1723
+ const commentMap = {};
1724
+ await Promise.all(
1725
+ taskTypes.map( async ( type, index ) => {
1726
+ const pipeline = pipelineStage( type, req.body.planoId, req.body.floorId, filterByStatus, filterByApprovalStatus, req.body.showtask );
1727
+ const data = await processedTaskService.aggregate( pipeline );
1728
+ resultMap[type] = data;
1729
+ } ) );
1730
+ const response = {
1731
+ merchRolloutData: resultMap['merchRollout'] || [],
1732
+ vmRolloutData: resultMap['vmRollout'] || [],
1733
+ merchRolloutComment: commentMap['merchRollout'] || [],
1734
+ vmRolloutComment: commentMap['vmRollout'] || [],
1735
+ };
1736
+ if ( filterByStatus.length || filterByApprovalStatus ) {
1737
+ let data = response[`${req.body.type}RolloutData`].filter( ( ele ) => ele.storeFixtureList.length );
1738
+ if ( filterByStatus.length == 1 ) {
1739
+ data = data.filter( ( type ) => {
1740
+ let fixtureList = type.storeFixtureList.filter( ( ele ) => filterByStatus.includes( ele.status ) );
1741
+ if ( filterByStatus.includes( 'complete' ) ) {
1742
+ return type.storeFixtureList.length == fixtureList.length;
1743
+ } else {
1744
+ return fixtureList.length;
1745
+ }
1746
+ },
1747
+ );
1748
+ }
1749
+ if ( filterByApprovalStatus ) {
1750
+ if ( filterByApprovalStatus === 'pending' ) {
1751
+ data = data.filter( ( type ) => {
1752
+ let fixtureList = type.storeFixtureList.filter( ( ele ) => {
1753
+ if ( ele.status === 'complete' && !ele.answers?.[0]?.issues.length && !ele.answers?.[0]?.status ) {
1754
+ return true;
1755
+ } else {
1756
+ return ele.answers?.some( ( ans ) =>
1757
+ ans.issues?.some( ( issue ) =>
1758
+ issue?.Details?.some( ( detail ) => detail.status === 'pending' ),
1759
+ ),
1760
+ );
1761
+ }
1762
+ } );
1763
+
1764
+ return fixtureList.length;
1765
+ } );
1766
+ } else {
1767
+ data = data.filter( ( type ) => {
1768
+ let fixtureList = type.storeFixtureList.filter( ( ele ) => {
1769
+ if ( ele.status === 'complete' && !ele.answers?.[0]?.issues.length && ele.answers?.[0]?.status ) {
1770
+ return true;
1771
+ } else {
1772
+ return ele.answers?.some( ( ans ) =>
1773
+ ans.issues?.some( ( issue ) =>
1774
+ issue?.Details?.some( ( detail ) => detail.status !== 'pending' ),
1775
+ ),
1776
+ );
1777
+ }
1778
+ } );
1779
+
1780
+ return fixtureList.length;
1781
+ } );
1782
+ }
1783
+ }
1784
+ response[`${req.body.type}RolloutData`] = data;
1785
+ }
1786
+ response.vmRolloutData = await Promise.all( response.vmRolloutData.map( async ( ele ) => {
1787
+ ele.storeFixtureList = await Promise.all( ele.storeFixtureList.map( async ( fixt ) => {
1788
+ if ( fixt?.FixtureData?.vmConfig?.length ) {
1789
+ fixt.FixtureData.vmConfig = await Promise.all( fixt?.FixtureData?.vmConfig?.map( async ( config ) => {
1790
+ let vmDetails = await planoVmService.findOne( { _id: config.vmId }, { vmType: 1, vmName: 1 } );
1791
+ return { ...config, vmType: vmDetails.vmType, vmName: vmDetails.vmName };
1792
+ } ) );
1793
+ }
1794
+ return fixt;
1795
+ } ) );
1796
+ return ele;
1797
+ } ) );
1798
+ res.sendSuccess( response );
1799
+ } catch ( e ) {
1800
+ logger.error( { functionName: 'getRolloutFeedbackv2', error: e } );
1801
+ return res.sendError( e, 500 );
1802
+ }
1803
+ }
1804
+
1805
+
1806
+ function pipelineStage( type, planoId, floorId, filterByStatus, filterByApprovalStatus, showtask, taskId = 0 ) {
1807
+ let matchStage = [ {
1808
+ $match: {
1809
+ 'planoType': type,
1810
+ 'planoId': new mongoose.Types.ObjectId( planoId ),
1811
+ 'floorId': new mongoose.Types.ObjectId( floorId ),
1812
+ },
1813
+ },
1814
+
1815
+ {
1816
+ $project: {
1817
+ 'planoId': 1,
1818
+ 'floorId': 1,
1819
+ 'planoType': 1,
1820
+ 'userName': 1,
1821
+ 'date_string': 1,
1822
+ 'createdByName': 1,
1823
+ 'createdAt': 1,
1824
+ 'submitTime_string': 1,
1825
+ 'redoStatus': 1,
1826
+ 'checklistStatus': 1,
1827
+ },
1828
+ },
1829
+ ];
1830
+ let taskLookup = {
1831
+ $lookup: {
1832
+ from: 'planotaskcompliances',
1833
+ let: { taskId: '$_id' },
1834
+ pipeline: [
1835
+ {
1836
+ $match: {
1837
+ $expr: {
1838
+ $and: [
1839
+ { $eq: [ '$taskId', '$$taskId' ] },
1840
+ ],
1841
+ },
1842
+ },
1843
+ },
1844
+ ],
1845
+ as: 'planoData',
1846
+ },
1847
+ };
1848
+ let unwindTask = {
1849
+ $unwind: {
1850
+ path: '$planoData',
1851
+ preserveNullAndEmptyArrays: true,
1852
+ },
1853
+ };
1854
+
1855
+ let commandLookup = {
1856
+ $lookup: {
1857
+ from: 'planoglobalcomments',
1858
+ let: { taskId: '$_id' },
1859
+ pipeline: [
1860
+ {
1861
+ $match: {
1862
+ $expr: {
1863
+ $eq: [ '$taskId', '$$taskId' ],
1864
+ },
1865
+ },
1866
+ },
1867
+
1868
+ ],
1869
+ as: 'commentData',
1870
+ },
1871
+ };
1872
+
1873
+
1874
+ const commonLookups = type === 'layout' ? [] : [
1875
+ {
1876
+ $lookup: {
1877
+ from: 'storefixtures',
1878
+ let: { fixtureId: '$planoData.fixtureId' },
1879
+ pipeline: [
1880
+ {
1881
+ $match: { $expr: { $eq: [ '$_id', '$$fixtureId' ] } },
1882
+ },
1883
+ ],
1884
+ as: 'FixtureData',
1885
+ },
1886
+ },
1887
+ {
1888
+ $unwind: { path: '$FixtureData', preserveNullAndEmptyArrays: true },
1889
+ },
1890
+ {
1891
+ $lookup: {
1892
+ from: 'fixtureshelves',
1893
+ let: { fixtureId: '$FixtureData._id' },
1894
+ pipeline: [
1895
+ {
1896
+ $match: { $expr: { $eq: [ '$fixtureId', '$$fixtureId' ] } },
1897
+ },
1898
+ ],
1899
+ as: 'Fixtureshelves',
1900
+ },
1901
+ },
1902
+
1903
+ {
1904
+ $set: {
1905
+ 'FixtureData.shelfConfig': '$Fixtureshelves',
1906
+ },
1907
+ },
1908
+ ];
1909
+
1910
+ let finalGrouping = [
1911
+ {
1912
+ $group: {
1913
+ '_id': '$_id',
1914
+ 'planoId': { $first: '$planoId' },
1915
+ 'floorId': { $first: '$floorId' },
1916
+ 'planoType': { $first: '$planoType' },
1917
+ 'userName': { $first: '$userName' },
1918
+ 'date_string': { $first: '$date_string' },
1919
+ 'createdByName': { $first: '$createdByName' },
1920
+ 'createdAt': { $first: '$createdAt' },
1921
+ 'submitTime_string': { $first: '$submitTime_string' },
1922
+ 'redoStatus': { $first: '$redoStatus' },
1923
+ 'checklistStatus': { $first: '$checklistStatus' },
1924
+ 'storeFixtureList': {
1925
+ $push: {
1926
+ $cond: [
1927
+ {
1928
+ $and: [
1929
+ {
1930
+ $or: [
1931
+ { $eq: [ '$redoStatus', true ] },
1932
+ {
1933
+ $and: [
1934
+ { $eq: [ '$redoStatus', false ] },
1935
+ { $eq: [ '$checklistStatus', 'submit' ] },
1936
+ ],
1937
+ },
1938
+ ],
1939
+ },
1940
+ { $ne: [ { $ifNull: [ '$FixtureData.fixtureName', null ] }, null ] },
1941
+ // { $gt: [ { $size: { $ifNull: [ '$FixtureData' ] } }, 0 ] },
1942
+ // { $gt: [ { $size: { $ifNull: [ '$FixtureData.shelfConfig', [] ] } }, 0 ] },
1943
+ ],
1944
+ },
1945
+ {
1946
+ FixtureData: '$FixtureData',
1947
+ Fixtureshelves: '$Fixtureshelves',
1948
+ approvalStatus: '$planoData.approvalStatus',
1949
+ answers: '$planoData.answers',
1950
+ createdAt: '$planoData.createdAt',
1951
+ date_iso: '$planoData.date_iso',
1952
+ date_string: '$planoData.date_string',
1953
+ fixtureId: '$planoData.fixtureId',
1954
+ floorId: '$planoData.floorId',
1955
+ planoId: '$planoData.planoId',
1956
+ status: '$planoData.status',
1957
+ storeId: '$planoData.storeId',
1958
+ storeName: '$planoData.storeName',
1959
+ taskId: '$planoData.taskId',
1960
+ taskType: '$planoData.taskType',
1961
+ type: '$planoData.type',
1962
+ updatedAt: '$planoData.updatedAt',
1963
+ _id: '$planoData._id',
1964
+ },
1965
+ '$$REMOVE',
1966
+ ],
1967
+ },
1968
+ },
1969
+ 'comments': { $first: '$commentData' },
1970
+ },
1971
+ },
1972
+ ];
1973
+ let pipeline = [
1974
+ ...matchStage,
1975
+ taskLookup,
1976
+ unwindTask,
1977
+ commandLookup,
1978
+ // unwindCommenTask,
1979
+ ...commonLookups,
1980
+ // ...vmStages,
1981
+ ...finalGrouping,
1982
+ { $sort: { _id: 1 } },
1983
+ ];
1984
+
1985
+ return pipeline;
1986
+ }