tango-app-api-store-builder 1.0.0-beta-85 → 1.0.0-beta-87

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.
@@ -9,6 +9,7 @@ import * as fixtureShelfService from '../service/fixtureShelf.service.js';
9
9
  import * as planoProductService from '../service/planoProduct.service.js';
10
10
  import * as planoMappingService from '../service/planoMapping.service.js';
11
11
  import * as planoTaskService from '../service/planoTask.service.js';
12
+ import * as processedTaskService from '../service/processedTaskservice.js';
12
13
  // import * as planoComplianceService from '../service/planoCompliance.service.js';
13
14
  // import * as planoTaskComplianceService from '../service/planoTask.service.js';
14
15
  // import * as planoQrConversionRequestService from '../service/planoQrConversionRequest.service.js';
@@ -18,7 +19,12 @@ import JSZip from 'jszip';
18
19
  import { signedUrl } from 'tango-app-api-middleware';
19
20
  import fs from 'fs';
20
21
  import https from 'https';
21
- import dayjs from 'dayjs';
22
+ import os from 'os';
23
+ import { fileURLToPath } from 'url';
24
+ import path from 'path';
25
+
26
+ const __filename = fileURLToPath( import.meta.url );
27
+ const __dirname = path.dirname( __filename );
22
28
 
23
29
 
24
30
  export async function getStoreNames( req, res ) {
@@ -2650,12 +2656,18 @@ export async function createCrestPlanogram( req, res ) {
2650
2656
  for ( let i = 0; i < fixture.productZones?.length; i++ ) {
2651
2657
  const vms = fixture.productZones[i].products.filter( ( vm ) => vm.isMerchandisingElement );
2652
2658
  const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === fixture.productZones[i].zoneName );
2659
+ const pids = fixture.productZones[i].products.filter( ( vm ) => !vm.isMerchandisingElement );
2660
+
2653
2661
 
2654
2662
  for ( const vm of vms ) {
2655
2663
  let configData = vmConfig[0];
2656
2664
 
2657
2665
  if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' ) {
2658
- configData = vmConfig.find( ( config ) => config.vmNumber === 3 );
2666
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
2667
+ }
2668
+
2669
+ if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' && pids.length ) {
2670
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
2659
2671
  }
2660
2672
 
2661
2673
  if ( !configData ) continue;
@@ -2809,12 +2821,17 @@ export async function createCrestPlanogram( req, res ) {
2809
2821
  for ( let i = 0; i < fixture.productZones?.length; i++ ) {
2810
2822
  const vms = fixture.productZones[i].products.filter( ( vm ) => vm.isMerchandisingElement );
2811
2823
  const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === fixture.productZones[i].zoneName );
2824
+ const pids = fixture.productZones[i].products.filter( ( vm ) => !vm.isMerchandisingElement );
2812
2825
 
2813
2826
  for ( const vm of vms ) {
2814
2827
  let configData = vmConfig[0];
2815
2828
 
2816
2829
  if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' ) {
2817
- configData = vmConfig.find( ( config ) => config.vmNumber === 3 );
2830
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
2831
+ }
2832
+
2833
+ if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' && pids.length ) {
2834
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
2818
2835
  }
2819
2836
 
2820
2837
 
@@ -2969,13 +2986,19 @@ export async function createCrestPlanogram( req, res ) {
2969
2986
  for ( let i = 0; i < fixture.productZones?.length; i++ ) {
2970
2987
  const vms = fixture.productZones[i].products.filter( ( vm ) => vm.isMerchandisingElement );
2971
2988
  const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === fixture.productZones[i].zoneName );
2989
+ const pids = fixture.productZones[i].products.filter( ( vm ) => !vm.isMerchandisingElement );
2990
+
2972
2991
 
2973
2992
  for ( const vm of vms ) {
2974
2993
  let configData = vmConfig[0];
2975
2994
 
2976
2995
 
2977
2996
  if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' ) {
2978
- configData = vmConfig.find( ( config ) => config.vmNumber === 3 );
2997
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
2998
+ }
2999
+
3000
+ if ( vm.productName === 'Creatr' && fixture.productZones[i].zoneName === 'Mid' && pids.length ) {
3001
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
2979
3002
  }
2980
3003
 
2981
3004
 
@@ -3289,361 +3312,606 @@ export async function updateCrestVms( req, res ) {
3289
3312
  }
3290
3313
  }
3291
3314
 
3292
- export async function updatePlanoFixtureLayout( planoId, floorId ) {
3293
- try {
3294
- console.log( 'dfghj' );
3295
- const constantFixtureLength = 1220;
3296
- const constantDetailedFixtureLength = 1220;
3297
3315
 
3298
- const constantFixtureWidth = 610;
3299
- const constantDetailedFixtureWidth = 1524;
3316
+ async function filterStores() {
3317
+ const stores = [
3318
+ 'ST338', 'LKST1304', 'ST303', 'LKST1228', 'LKST499', 'LKST479', 'ST320',
3319
+ 'LKST515', 'LKST406', 'ST392', 'ST328', 'ST332', 'LKST487', 'ST312',
3320
+ 'LKST615', 'LKST352', 'LKST457', 'LKST439', 'LKST347', 'LKST1084',
3321
+ 'LKST380', 'ST298', 'LKST356', 'LKST223', 'LKST480', 'ST30', 'ST285',
3322
+ 'LKST500', 'LKST466', 'LKST321', 'LKST678', 'LKST383', 'LKST365',
3323
+ 'LKST374', 'ST302', 'LKST420', 'LKST394', 'LKST444', 'LKST314',
3324
+ 'LKST01', 'LKST357', 'LKST465', 'LKST331', 'LKST440', 'LKST345',
3325
+ 'LKST389', 'ST330', 'LKST299', 'ST244', 'LKST294', 'ST314', 'LKST361',
3326
+ 'LKST377', 'LKST390', 'ST335', 'LKST416', 'ST293', 'LKST341', 'LKST501',
3327
+ 'LKST408', 'LKST227', 'LKST353', 'LKST364', 'ST318', 'LKST326',
3328
+ 'LKST344', 'ST321', 'LKST384', 'LKST496', 'LKST427', 'LKST325',
3329
+ 'LKST282', 'ST197', 'LKST388', 'LKST338', 'LKST371', 'ST326', 'LKST428',
3330
+ 'LKST112', 'LKST334', 'ST319', 'LKST464', 'LKST490', 'LKST11',
3331
+ 'LKST502', 'LKST1438',
3332
+ ];
3333
+
3334
+ const rawData = fs.readFileSync( 'crest_scrap_v1.json', 'utf8' );
3335
+ const allStores = JSON.parse( rawData );
3336
+
3337
+ const filteredStores = allStores.filter( ( store ) => stores.includes( store.storeName ) );
3338
+
3339
+ fs.writeFileSync( 'crest_filtered.json', JSON.stringify( filteredStores, null, 2 ) );
3340
+ }
3300
3341
 
3301
- const mmToFeet = 305;
3342
+ import fsp from 'fs/promises';
3302
3343
 
3303
- function roundToTwo( num ) {
3304
- return Math.round( num * 100 ) / 100;
3305
- }
3344
+ import sharp from 'sharp';
3306
3345
 
3307
- const insertedPlano = await planoService.findOne( { _id: planoId } );
3308
3346
 
3309
- const planoDoc = insertedPlano.toObject();
3347
+ const stitchImagesFromZips = async () => {
3348
+ const zip1Path = 'crest_plano.zip';
3349
+ const zip2Path = 'tango_plano.zip';
3350
+ const outputDir = 'stitched';
3310
3351
 
3311
- const fixtureData = await storeFixtureService.findAndSort( { planoId: planoId, floorId: floorId }, {}, { fixtureNumber: 1 } );
3352
+ const loadZip = async ( zipPath ) => {
3353
+ const buffer = await fsp.readFile( zipPath );
3354
+ return await JSZip.loadAsync( buffer );
3355
+ };
3312
3356
 
3313
- const leftFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 1 );
3314
- const rightFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 3 );
3315
- const backFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 2 );
3316
- const floorFixtures = fixtureData.filter( ( fixture ) => fixture.fixtureType == 'floor' );
3357
+ const extractImages = async ( zip ) => {
3358
+ const imageFiles = {};
3359
+ const imageRegex = /\.(png|jpe?g)$/i;
3317
3360
 
3318
- // console.log( leftFixtures, 'left' );
3319
- // console.log( rightFixtures, 'rightFixtures' );
3320
- // console.log( backFixtures, 'backFixtures' );
3321
- // console.log( floorFixtures, 'floorFixtures' );
3361
+ for ( const [ name, file ] of Object.entries( zip.files ) ) {
3362
+ const base = path.basename( name );
3363
+ if ( !file.dir && imageRegex.test( base ) ) {
3364
+ imageFiles[base] = await file.async( 'nodebuffer' );
3365
+ }
3366
+ }
3322
3367
 
3323
- const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3324
- const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
3368
+ return imageFiles;
3369
+ };
3370
+
3371
+ const stitchOrCenter = async ( buffer1, buffer2, outputPath ) => {
3372
+ const totalWidth = 7000;
3373
+ const width1 = Math.round( totalWidth * 0.3 );
3374
+ const width2 = Math.round( totalWidth * 0.7 );
3375
+
3376
+ let img1 = buffer1 ? await sharp( buffer1 ).resize( { width: width1 } ).toBuffer() : null;
3377
+ let img2 = buffer2 ? await sharp( buffer2 ).resize( { width: width2 } ).toBuffer() : null;
3378
+
3379
+ const [ meta1, meta2 ] = await Promise.all( [
3380
+ img1 ? sharp( img1 ).metadata() : Promise.resolve( { height: 0 } ),
3381
+ img2 ? sharp( img2 ).metadata() : Promise.resolve( { height: 0 } ),
3382
+ ] );
3383
+
3384
+ const maxHeight = Math.max( meta1.height, meta2.height );
3385
+
3386
+ const composites = [];
3387
+ if ( img1 ) composites.push( { input: img1, top: 0, left: 0 } );
3388
+ if ( img2 ) composites.push( { input: img2, top: 0, left: width1 } );
3389
+
3390
+ await sharp( {
3391
+ create: {
3392
+ width: totalWidth,
3393
+ height: maxHeight,
3394
+ channels: 4,
3395
+ background: { r: 255, g: 255, b: 255, alpha: 0 },
3396
+ },
3397
+ } )
3398
+ .composite( composites )
3399
+ .png()
3400
+ .toFile( outputPath );
3401
+ };
3325
3402
 
3326
- const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
3327
- const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
3403
+ try {
3404
+ await fsp.mkdir( outputDir, { recursive: true } );
3328
3405
 
3329
- const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3330
- const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
3406
+ const [ zip1, zip2 ] = await Promise.all( [ loadZip( zip1Path ), loadZip( zip2Path ) ] );
3407
+ const [ images1, images2 ] = await Promise.all( [ extractImages( zip1 ), extractImages( zip2 ) ] );
3331
3408
 
3332
- const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
3333
- const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
3409
+ const allFilenames = new Set( [ ...Object.keys( images1 ), ...Object.keys( images2 ) ] );
3334
3410
 
3335
- const maxFixturesPerRow = floorFixtures.length/2;
3336
- const totalRows = 2;
3411
+ for ( const name of allFilenames ) {
3412
+ const buffer1 = images1[name] || null;
3413
+ const buffer2 = images2[name] || null;
3414
+ const outputPath = path.join( outputDir, `${name.replace( /\.[^/.]+$/, '' )}.png` );
3337
3415
 
3338
- const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3339
- const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
3416
+ try {
3417
+ await stitchOrCenter( buffer1, buffer2, outputPath );
3418
+ console.log( ` Processed: ${name}` );
3419
+ } catch ( err ) {
3420
+ console.error( ` Error processing ${name}:`, err.message );
3421
+ }
3422
+ }
3423
+ } catch ( err ) {
3424
+ console.error( ' Unexpected error:', err.message );
3425
+ }
3426
+ };
3340
3427
 
3341
- const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( 2 * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
3342
- const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( 2 * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
3428
+ // stitchImagesFromZips();
3343
3429
 
3344
- const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
3345
- const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
3346
3430
 
3347
- const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
3348
- const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
3431
+ import { Builder, By, until } from 'selenium-webdriver';
3432
+ import chrome from 'selenium-webdriver/chrome.js';
3433
+ import fetch from 'node-fetch';
3349
3434
 
3350
- const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
3351
- const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
3435
+ import fetchCookie from 'fetch-cookie';
3352
3436
 
3353
- const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet );
3354
- const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet );
3355
3437
 
3356
- const finalXDistance = roundToTwo( ( maxXDistance < ( backXDistanceFeet + floorXDistanceFeet )? ( ( backXDistanceFeet + floorXDistanceFeet ) + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDistance + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : maxXDistance ) );
3357
- const finalXDetailedDistance = roundToTwo( ( maxXDetailedDistance < ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet )? ( ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDetailedDistance + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : maxXDetailedDistance ) );
3438
+ async function downloadCrestImages() {
3439
+ const storeList = await planoService.find( {} );
3440
+ const storeIds = storeList.map( ( store ) => store.toObject().storeName );
3358
3441
 
3359
- const finalYDistance = roundToTwo( ( maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : ( maxYDistance + ( ( constantFixtureWidth )/mmToFeet ) ) ) );
3360
- const finalYDetailedDistance = roundToTwo( ( maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : ( maxYDetailedDistance + ( ( constantDetailedFixtureWidth )/mmToFeet ) ) ) );
3442
+ const invalidateUrl = 'https://app.getcrest.ai/api/ms_iam/user/session/override/';
3443
+ const tokenUrl = 'https://app.getcrest.ai/api/ms_iam/token/';
3361
3444
 
3362
- const floorInsertData = {
3363
- storeName: planoDoc.storeName,
3364
- storeId: planoDoc.storeId,
3365
- layoutName: `${planoDoc.storeName} - Layout`,
3366
- clientId: '11',
3367
- floorNumber: 1,
3368
- floorName: 'floor 1',
3369
- layoutPolygon: [
3370
- {
3371
- elementType: 'wall',
3372
- distance: finalXDistance,
3373
- unit: 'ft',
3374
- direction: 'right',
3375
- angle: 90,
3376
- elementNumber: 1,
3377
- detailedDistance: finalXDetailedDistance,
3378
- },
3379
- {
3380
- elementType: 'wall',
3381
- distance: finalYDistance,
3382
- unit: 'ft',
3383
- direction: 'down',
3384
- angle: 90,
3385
- elementNumber: 2,
3386
- detailedDistance: finalYDetailedDistance,
3387
- },
3388
- {
3389
- elementType: 'wall',
3390
- distance: finalXDistance,
3391
- unit: 'ft',
3392
- direction: 'left',
3393
- angle: 90,
3394
- elementNumber: 3,
3395
- detailedDistance: finalXDetailedDistance,
3396
- },
3397
- {
3398
- elementType: 'wall',
3399
- distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
3400
- unit: 'ft',
3401
- direction: 'up',
3402
- angle: 90,
3403
- elementNumber: 4,
3404
- detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
3405
- },
3406
- {
3407
- elementType: 'entrance',
3408
- distance: roundToTwo( ( ( finalYDistance * 20 ) / 100 ) ),
3409
- unit: 'ft',
3410
- direction: 'up',
3411
- angle: 90,
3412
- elementNumber: 1,
3413
- detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 30 ) / 100 ) ),
3414
- },
3415
- {
3416
- elementType: 'wall',
3417
- distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
3418
- unit: 'ft',
3419
- direction: 'up',
3420
- angle: 90,
3421
- elementNumber: 5,
3422
- detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
3423
- },
3424
- ],
3425
- createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
3426
- createdByName: 'Bejan',
3427
- createdByEmail: 'bejan@tangotech.co.in',
3428
- status: 'completed',
3429
- planoId: planoDoc._id,
3430
- };
3445
+ let authToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQ0NzE4MjUzLCJpYXQiOjE3NDQ3MTQ2NTMsImp0aSI6Ijk5ZTAyYjU4ODg2NjQ3MDk4Y2NlY2NmODZlYzYzYTU4IiwidXNlcl9pZCI6MTA4NSwiaWQiOjEwODUsImN1c3RvbWVyX2dyb3VwIjozOTgsImxpY2VuY2Vfc2NvcGVzIjpbeyJyZXNvdXJjZV9zZXQiOiJwcF9zZXQiLCJzY29wZV9yb2xlIjoiY29udHJpYnV0b3IifSx7InJlc291cmNlX3NldCI6ImRwX3NldCIsInNjb3BlX3JvbGUiOiJjb250cmlidXRvciJ9LHsicmVzb3VyY2Vfc2V0IjoiZGZfc2V0Iiwic2NvcGVfcm9sZSI6ImNvbnRyaWJ1dG9yIn0seyJyZXNvdXJjZV9zZXQiOiJkZWZhdWx0X3NldCIsInNjb3BlX3JvbGUiOiJjb250cmlidXRvciJ9XX0.7g1XS48l7mVsOcDXHLC7VFLFyJ-p13e2kkJlb8uFc_g';
3431
3446
 
3432
- await storeBuilderService.upsertOne( { planoId: planoDoc._id }, floorInsertData );
3447
+ const fetchWithCookies = fetchCookie( fetch );
3433
3448
 
3434
- for ( let index = 0; index < leftFixtures.length; index++ ) {
3435
- const fixture = leftFixtures[index];
3449
+ async function fetchNewToken() {
3450
+ const invalidate = await fetchWithCookies( invalidateUrl, {
3451
+ 'method': 'POST',
3452
+ 'headers': { 'Content-Type': 'application/json' },
3453
+ 'body': JSON.stringify( { 'email': 'tango.lenskart@getcrest.ai', 'password': 'Tangolenskart@123' } ),
3454
+ } );
3436
3455
 
3437
- const fixtureData = {
3438
- 'fixtureHeight': {
3439
- 'value': 0,
3440
- 'unit': 'mm',
3441
- },
3442
- 'fixtureLength': {
3443
- 'value': constantFixtureLength,
3444
- 'unit': 'mm',
3445
- },
3446
- 'fixtureWidth': {
3447
- 'value': constantFixtureWidth,
3448
- 'unit': 'mm',
3449
- },
3450
- 'relativePosition': {
3451
- 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
3452
- 'y': 0,
3453
- 'unit': 'ft',
3454
- },
3455
- 'detailedFixtureLength': {
3456
- 'value': constantDetailedFixtureLength,
3457
- 'unit': 'mm',
3458
- },
3459
- 'detailedFixtureWidth': {
3460
- 'value': constantDetailedFixtureWidth,
3461
- 'unit': 'mm',
3462
- },
3463
- 'relativeDetailedPosition': {
3464
- 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
3465
- 'y': 0,
3466
- 'unit': 'ft',
3467
- },
3468
- };
3456
+ const invalidateData = await invalidate.json();
3469
3457
 
3470
- await storeFixtureService.updateOne(
3471
- {
3472
- _id: fixture._id,
3473
- },
3474
- fixtureData );
3458
+ console.log( invalidateData );
3459
+
3460
+ console.log( 'Fetching new token...' );
3461
+ const res = await fetchWithCookies( tokenUrl, {
3462
+ 'method': 'POST',
3463
+ 'Accept': 'application/json, text/javascript, */*; q=0.01',
3464
+ 'headers': { 'Content-Type': 'application/json' },
3465
+ 'body': JSON.stringify( { 'email': 'tango.lenskart@getcrest.ai', 'password': 'Tangolenskart@123' } ),
3466
+ } );
3467
+
3468
+ if ( !res.ok ) {
3469
+ throw new Error( `Failed to fetch token: ${res.status}` );
3475
3470
  }
3476
3471
 
3477
- for ( let index = 0; index < backFixtures.length; index++ ) {
3478
- const fixture = backFixtures[index];
3472
+ const data = await res.json();
3473
+ console.log( data );
3474
+ authToken = data.access;
3475
+ return authToken;
3476
+ }
3479
3477
 
3480
- const fixtureData = {
3481
- 'fixtureHeight': {
3482
- 'value': 0,
3483
- 'unit': 'mm',
3484
- },
3485
- 'fixtureLength': {
3486
- 'value': constantFixtureWidth,
3487
- 'unit': 'mm',
3488
- },
3489
- 'fixtureWidth': {
3490
- 'value': constantFixtureLength,
3491
- 'unit': 'mm',
3492
- },
3493
- 'relativePosition': {
3494
- 'x': roundToTwo( ( finalXDistance - ( constantFixtureWidth/mmToFeet ) ) ),
3495
- 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
3496
- 'unit': 'ft',
3497
- },
3498
- 'detailedFixtureLength': {
3499
- 'value': constantDetailedFixtureLength,
3500
- 'unit': 'mm',
3501
- },
3502
- 'detailedFixtureWidth': {
3503
- 'value': constantDetailedFixtureWidth,
3504
- 'unit': 'mm',
3505
- },
3506
- 'relativeDetailedPosition': {
3507
- 'x': roundToTwo( ( finalXDetailedDistance - ( constantDetailedFixtureLength/mmToFeet ) ) ),
3508
- 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
3509
- 'unit': 'ft',
3510
- },
3511
- };
3478
+ async function runAutomation( token, storeId, retries = 3 ) {
3479
+ let attempts = 0;
3480
+
3481
+ while ( attempts < retries ) {
3482
+ const options = new chrome.Options();
3483
+ options.addArguments( 'headless' );
3484
+ options.addArguments( 'disable-gpu' );
3485
+
3486
+ const driver = await new Builder()
3487
+ .forBrowser( 'chrome' )
3488
+ .setChromeOptions( options )
3489
+ .build();
3490
+
3491
+ try {
3492
+ const url = `https://app.getcrest.ai/pvt/?auth=${token}&module=planno&store_id=${storeId}`;
3493
+ await driver.get( url );
3494
+ const currentUrl = await driver.getCurrentUrl();
3495
+ if ( currentUrl.includes( '/login' ) ) {
3496
+ console.warn( `Redirected to login for store ${storeId}. Retrying with new token...` );
3497
+ await driver.quit();
3498
+ const newToken = await fetchNewToken();
3499
+ return await runAutomation( newToken, storeId, retries );
3500
+ }
3512
3501
 
3513
- await storeFixtureService.updateOne(
3514
- {
3515
- _id: fixture._id,
3516
- },
3517
- fixtureData );
3502
+ const button = await driver.wait( until.elementLocated(
3503
+ By.xpath( '//*[contains(@class, "MuiButtonBase-root") and contains(@class, "MuiButton-root") and contains(@class, "MuiButton-text") and contains(@class, "MuiButton-disableElevation") and contains(@class, "jss35") and contains(@class, "jss47") and contains(@class, "jss37") and contains(@class, "jss144")]' ),
3504
+ ), 10000 );
3505
+ await button.click();
3506
+
3507
+ const checkbox = await driver.wait(
3508
+ until.elementLocated(
3509
+ By.xpath( '//*[contains(@class, "MuiCheckbox-root") and contains(@class, "MuiIconButton-root")]//input[@type="checkbox"]' ),
3510
+ ),
3511
+ 10000,
3512
+ );
3513
+ await checkbox.click();
3514
+
3515
+ const downloadIcon = await driver.wait(
3516
+ until.elementLocated(
3517
+ By.xpath( '//button[contains(@class, "cool-tooltip")]' ),
3518
+ ),
3519
+ 10000,
3520
+ );
3521
+ await downloadIcon.click();
3522
+
3523
+
3524
+ console.log( `Download triggered for store: ${storeId}` );
3525
+ await driver.sleep( 5000 );
3526
+ return;
3527
+ } catch ( err ) {
3528
+ attempts++;
3529
+ console.error( `Error for store ${storeId}, attempt ${attempts}: ${err.message}` );
3530
+ if ( attempts >= retries ) {
3531
+ console.error( `Failed for store ${storeId} after ${retries} attempts` );
3532
+ } else {
3533
+ console.log( `Retrying for store ${storeId}, attempt ${attempts + 1}` );
3534
+ }
3535
+ } finally {
3536
+ try {
3537
+ if ( driver && ( await driver.getSession() ) ) {
3538
+ await driver.quit();
3539
+ }
3540
+ } catch ( e ) {
3541
+
3542
+ }
3543
+ }
3518
3544
  }
3545
+ }
3519
3546
 
3520
- for ( let index = 0; index < rightFixtures.length; index++ ) {
3521
- const fixture = rightFixtures[index];
3547
+ for ( const storeId of storeIds ) {
3548
+ try {
3549
+ console.log( `Starting automation for store: ${storeId}` );
3550
+ await runAutomation( authToken, storeId );
3551
+ } catch ( error ) {
3552
+ console.error( `Automation failed for store ${storeId}: ${error.message}` );
3553
+ }
3554
+ }
3555
+ }
3522
3556
 
3523
- const fixtureData = {
3524
- 'fixtureHeight': {
3525
- 'value': 0,
3526
- 'unit': 'mm',
3527
- },
3528
- 'fixtureLength': {
3529
- 'value': constantFixtureLength,
3530
- 'unit': 'mm',
3531
- },
3532
- 'fixtureWidth': {
3533
- 'value': constantFixtureWidth,
3534
- 'unit': 'mm',
3535
- },
3536
- 'relativePosition': {
3537
- 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
3538
- 'y': roundToTwo( ( finalYDistance - ( constantFixtureWidth / mmToFeet ) ) ),
3539
- 'unit': 'ft',
3540
- },
3541
- 'detailedFixtureLength': {
3542
- 'value': constantDetailedFixtureLength,
3543
- 'unit': 'mm',
3544
- },
3545
- 'detailedFixtureWidth': {
3546
- 'value': constantDetailedFixtureWidth,
3547
- 'unit': 'mm',
3548
- },
3549
- 'relativeDetailedPosition': {
3550
- 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
3551
- 'y': roundToTwo( ( finalYDetailedDistance - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
3552
- 'unit': 'ft',
3553
- },
3554
- };
3557
+ // downloadCrestImages();
3558
+ export async function updatePlanoFixtureLayout( planoId, floorId ) {
3559
+ try {
3560
+ console.log( 'dfghj' );
3561
+ const constantFixtureLength = 1220;
3562
+ const constantDetailedFixtureLength = 1220;
3555
3563
 
3556
- await storeFixtureService.updateOne(
3557
- {
3558
- _id: fixture._id,
3559
- },
3560
- fixtureData );
3564
+ const constantFixtureWidth = 610;
3565
+ const constantDetailedFixtureWidth = 1524;
3566
+
3567
+ const mmToFeet = 305;
3568
+
3569
+ function roundToTwo( num ) {
3570
+ return Math.round( num * 100 ) / 100;
3561
3571
  }
3562
3572
 
3563
- for ( let index = 0; index < floorFixtures.length; index++ ) {
3564
- const fixture = floorFixtures[index];
3565
- const centerRow = Math.floor( totalRows / 2 );
3573
+ const insertedPlano = await planoService.findOne( { _id: planoId } );
3574
+ if ( insertedPlano ) {
3575
+ const planoDoc = insertedPlano.toObject();
3566
3576
 
3567
- const startingX =roundToTwo( ( ( finalXDistance / 2 ) - ( ( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) ) ) );
3568
- const detailedStartingX = roundToTwo( ( ( finalXDetailedDistance / 2 ) - ( ( maxFixturesPerRow / 2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ) );
3577
+ const fixtureData = await storeFixtureService.findAndSort( { planoId: planoId, floorId: floorId }, {}, { fixtureNumber: 1 } );
3569
3578
 
3570
- const startingY = ( finalYDistance / 2 ) - ( centerRow * ( constantFixtureWidth / mmToFeet ) );
3571
- const detailedStartingY = ( finalYDetailedDistance / 2 ) - ( centerRow * ( constantDetailedFixtureWidth / mmToFeet ) );
3579
+ const leftFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 1 );
3580
+ const rightFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 3 );
3581
+ const backFixtures = fixtureData.filter( ( fixture ) => fixture.associatedElementType == 'wall' && fixture.associatedElementNumber == 2 );
3582
+ const floorFixtures = fixtureData.filter( ( fixture ) => fixture.fixtureType == 'floor' );
3572
3583
 
3573
- const colIndex = Math.floor( index / 2 );
3574
- const rowIndex = index % 2 === 0 ? 1 : 0;
3584
+ // console.log( leftFixtures, 'left' );
3585
+ // console.log( rightFixtures, 'rightFixtures' );
3586
+ // console.log( backFixtures, 'backFixtures' );
3587
+ // console.log( floorFixtures, 'floorFixtures' );
3575
3588
 
3589
+ const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3590
+ const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
3576
3591
 
3577
- const xPos = roundToTwo( ( startingX + colIndex * ( constantFixtureLength / mmToFeet ) ) );
3578
- const yPos = roundToTwo( ( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) ) );
3592
+ const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
3593
+ const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
3579
3594
 
3580
- const detailedXPos = roundToTwo( ( detailedStartingX + colIndex * ( constantDetailedFixtureLength / mmToFeet ) ) );
3581
- const detailedYPos = roundToTwo( ( detailedStartingY + rowIndex * ( constantDetailedFixtureWidth / mmToFeet ) ) );
3595
+ const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3596
+ const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
3582
3597
 
3598
+ const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
3599
+ const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
3583
3600
 
3584
- const fixtureData = {
3585
- 'fixtureHeight': {
3586
- 'value': 0,
3587
- 'unit': 'mm',
3588
- },
3589
- 'fixtureLength': {
3590
- 'value': constantFixtureLength,
3591
- 'unit': 'mm',
3592
- },
3593
- 'fixtureWidth': {
3594
- 'value': constantFixtureWidth,
3595
- 'unit': 'mm',
3596
- },
3597
- 'relativePosition': {
3598
- 'x': xPos,
3599
- 'y': yPos,
3600
- 'unit': 'ft',
3601
- },
3602
- 'detailedFixtureLength': {
3603
- 'value': constantDetailedFixtureLength,
3604
- 'unit': 'mm',
3605
- },
3606
- 'detailedFixtureWidth': {
3607
- 'value': constantDetailedFixtureWidth,
3608
- 'unit': 'mm',
3609
- },
3610
- 'relativeDetailedPosition': {
3611
- 'x': detailedXPos,
3612
- 'y': detailedYPos,
3613
- 'unit': 'ft',
3614
- },
3615
- };
3601
+ const maxFixturesPerRow = floorFixtures.length/2;
3602
+ const totalRows = 2;
3603
+
3604
+ const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantFixtureLength / mmToFeet ) ) ) : 0;
3605
+ const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
3606
+
3607
+ const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( 2 * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
3608
+ const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( 2 * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
3609
+
3610
+ const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
3611
+ const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
3612
+
3613
+ const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
3614
+ const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
3615
+
3616
+ const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
3617
+ const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
3618
+
3619
+ const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet );
3620
+ const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet );
3616
3621
 
3617
- await storeFixtureService.updateOne(
3622
+ const finalXDistance = roundToTwo( ( maxXDistance < ( backXDistanceFeet + floorXDistanceFeet )? ( ( backXDistanceFeet + floorXDistanceFeet ) + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDistance + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : maxXDistance ) );
3623
+ const finalXDetailedDistance = roundToTwo( ( maxXDetailedDistance < ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet )? ( ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDetailedDistance + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : maxXDetailedDistance ) );
3624
+
3625
+ const finalYDistance = roundToTwo( ( maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : ( maxYDistance + ( ( constantFixtureWidth )/mmToFeet ) ) ) );
3626
+ const finalYDetailedDistance = roundToTwo( ( maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : ( maxYDetailedDistance + ( ( constantDetailedFixtureWidth )/mmToFeet ) ) ) );
3627
+
3628
+ const floorInsertData = {
3629
+ storeName: planoDoc.storeName,
3630
+ storeId: planoDoc.storeId,
3631
+ layoutName: `${planoDoc.storeName} - Layout`,
3632
+ clientId: '11',
3633
+ floorNumber: 1,
3634
+ floorName: 'floor 1',
3635
+ layoutPolygon: [
3618
3636
  {
3619
- _id: fixture._id,
3637
+ elementType: 'wall',
3638
+ distance: finalXDistance,
3639
+ unit: 'ft',
3640
+ direction: 'right',
3641
+ angle: 90,
3642
+ elementNumber: 1,
3643
+ detailedDistance: finalXDetailedDistance,
3620
3644
  },
3621
- fixtureData );
3622
- }
3623
- } catch ( e ) {
3624
- logger.error( { functionName: 'createCrestPlanogram', error: e } );
3625
- return res.sendError( e.message || 'Internal Server Error', 500 );
3626
- }
3627
- }
3645
+ {
3646
+ elementType: 'wall',
3647
+ distance: finalYDistance,
3648
+ unit: 'ft',
3649
+ direction: 'down',
3650
+ angle: 90,
3651
+ elementNumber: 2,
3652
+ detailedDistance: finalYDetailedDistance,
3653
+ },
3654
+ {
3655
+ elementType: 'wall',
3656
+ distance: finalXDistance,
3657
+ unit: 'ft',
3658
+ direction: 'left',
3659
+ angle: 90,
3660
+ elementNumber: 3,
3661
+ detailedDistance: finalXDetailedDistance,
3662
+ },
3663
+ {
3664
+ elementType: 'wall',
3665
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
3666
+ unit: 'ft',
3667
+ direction: 'up',
3668
+ angle: 90,
3669
+ elementNumber: 4,
3670
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
3671
+ },
3672
+ {
3673
+ elementType: 'entrance',
3674
+ distance: roundToTwo( ( ( finalYDistance * 20 ) / 100 ) ),
3675
+ unit: 'ft',
3676
+ direction: 'up',
3677
+ angle: 90,
3678
+ elementNumber: 1,
3679
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 30 ) / 100 ) ),
3680
+ },
3681
+ {
3682
+ elementType: 'wall',
3683
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
3684
+ unit: 'ft',
3685
+ direction: 'up',
3686
+ angle: 90,
3687
+ elementNumber: 5,
3688
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
3689
+ },
3690
+ ],
3691
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
3692
+ createdByName: 'Bejan',
3693
+ createdByEmail: 'bejan@tangotech.co.in',
3694
+ status: 'completed',
3695
+ planoId: planoDoc._id,
3696
+ };
3628
3697
 
3629
- export async function updatelayout( req, res ) {
3630
- try {
3631
- let getLayoutTaskDetails = await planoTaskService.find( { date_string: dayjs().format( 'YYYY-MM-DD' ), status: 'incomplete', type: 'layout' } );
3632
- if ( !getLayoutTaskDetails.length ) {
3633
- return res.sendError( 'No data found', 204 );
3634
- }
3635
- for ( let layout of getLayoutTaskDetails ) {
3636
- let layoutAnswer = layout.answers[1];
3637
- let planoDetails = await planoService.findOne( { _id: layout.planoId } );
3638
- let fixtureDetails = await storeFixtureService.findAndSort( { planoId: layout.planoId, floorId: layout.floorId }, {}, { fixtureNumber: 1 } );
3639
- if ( layoutAnswer?.extraFixture?.length ) {
3640
- let deletedFixtureList = layoutAnswer.extraFixture.map( ( fixture ) => fixture.fixtureId );
3641
- fixtureDetails = fixtureDetails.filter( ( fixture ) => !deletedFixtureList.includes( fixture._id.toString() ) );
3642
- await storeFixtureService.deleteMany( { _id: { $in: deletedFixtureList } } );
3643
- await fixtureShelfService.deleteMany( { fixtureId: { $in: deletedFixtureList } } );
3644
- await planoMappingService.deleteMany( { fixtureId: { $in: deletedFixtureList } } );
3645
- }
3646
- if ( layoutAnswer?.wronglyLocatedFixtures?.length ) {
3698
+ await storeBuilderService.upsertOne( { planoId: planoDoc._id }, floorInsertData );
3699
+
3700
+ for ( let index = 0; index < leftFixtures.length; index++ ) {
3701
+ const fixture = leftFixtures[index];
3702
+
3703
+ const fixtureData = {
3704
+ 'fixtureHeight': {
3705
+ 'value': 0,
3706
+ 'unit': 'mm',
3707
+ },
3708
+ 'fixtureLength': {
3709
+ 'value': constantFixtureLength,
3710
+ 'unit': 'mm',
3711
+ },
3712
+ 'fixtureWidth': {
3713
+ 'value': constantFixtureWidth,
3714
+ 'unit': 'mm',
3715
+ },
3716
+ 'relativePosition': {
3717
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
3718
+ 'y': 0,
3719
+ 'unit': 'ft',
3720
+ },
3721
+ 'detailedFixtureLength': {
3722
+ 'value': constantDetailedFixtureLength,
3723
+ 'unit': 'mm',
3724
+ },
3725
+ 'detailedFixtureWidth': {
3726
+ 'value': constantDetailedFixtureWidth,
3727
+ 'unit': 'mm',
3728
+ },
3729
+ 'relativeDetailedPosition': {
3730
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
3731
+ 'y': 0,
3732
+ 'unit': 'ft',
3733
+ },
3734
+ };
3735
+
3736
+ await storeFixtureService.updateOne(
3737
+ {
3738
+ _id: fixture._id,
3739
+ },
3740
+ fixtureData );
3741
+ }
3742
+
3743
+ for ( let index = 0; index < backFixtures.length; index++ ) {
3744
+ const fixture = backFixtures[index];
3745
+
3746
+ const fixtureData = {
3747
+ 'fixtureHeight': {
3748
+ 'value': 0,
3749
+ 'unit': 'mm',
3750
+ },
3751
+ 'fixtureLength': {
3752
+ 'value': constantFixtureWidth,
3753
+ 'unit': 'mm',
3754
+ },
3755
+ 'fixtureWidth': {
3756
+ 'value': constantFixtureLength,
3757
+ 'unit': 'mm',
3758
+ },
3759
+ 'relativePosition': {
3760
+ 'x': roundToTwo( ( finalXDistance - ( constantFixtureWidth/mmToFeet ) ) ),
3761
+ 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
3762
+ 'unit': 'ft',
3763
+ },
3764
+ 'detailedFixtureLength': {
3765
+ 'value': constantDetailedFixtureLength,
3766
+ 'unit': 'mm',
3767
+ },
3768
+ 'detailedFixtureWidth': {
3769
+ 'value': constantDetailedFixtureWidth,
3770
+ 'unit': 'mm',
3771
+ },
3772
+ 'relativeDetailedPosition': {
3773
+ 'x': roundToTwo( ( finalXDetailedDistance - ( constantDetailedFixtureLength/mmToFeet ) ) ),
3774
+ 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
3775
+ 'unit': 'ft',
3776
+ },
3777
+ };
3778
+
3779
+ await storeFixtureService.updateOne(
3780
+ {
3781
+ _id: fixture._id,
3782
+ },
3783
+ fixtureData );
3784
+ }
3785
+
3786
+ for ( let index = 0; index < rightFixtures.length; index++ ) {
3787
+ const fixture = rightFixtures[index];
3788
+
3789
+ const fixtureData = {
3790
+ 'fixtureHeight': {
3791
+ 'value': 0,
3792
+ 'unit': 'mm',
3793
+ },
3794
+ 'fixtureLength': {
3795
+ 'value': constantFixtureLength,
3796
+ 'unit': 'mm',
3797
+ },
3798
+ 'fixtureWidth': {
3799
+ 'value': constantFixtureWidth,
3800
+ 'unit': 'mm',
3801
+ },
3802
+ 'relativePosition': {
3803
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
3804
+ 'y': roundToTwo( ( finalYDistance - ( constantFixtureWidth / mmToFeet ) ) ),
3805
+ 'unit': 'ft',
3806
+ },
3807
+ 'detailedFixtureLength': {
3808
+ 'value': constantDetailedFixtureLength,
3809
+ 'unit': 'mm',
3810
+ },
3811
+ 'detailedFixtureWidth': {
3812
+ 'value': constantDetailedFixtureWidth,
3813
+ 'unit': 'mm',
3814
+ },
3815
+ 'relativeDetailedPosition': {
3816
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
3817
+ 'y': roundToTwo( ( finalYDetailedDistance - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
3818
+ 'unit': 'ft',
3819
+ },
3820
+ };
3821
+
3822
+ await storeFixtureService.updateOne(
3823
+ {
3824
+ _id: fixture._id,
3825
+ },
3826
+ fixtureData );
3827
+ }
3828
+
3829
+ for ( let index = 0; index < floorFixtures.length; index++ ) {
3830
+ const fixture = floorFixtures[index];
3831
+ const centerRow = Math.floor( totalRows / 2 );
3832
+
3833
+ const startingX =roundToTwo( ( ( finalXDistance / 2 ) - ( ( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) ) ) );
3834
+ const detailedStartingX = roundToTwo( ( ( finalXDetailedDistance / 2 ) - ( ( maxFixturesPerRow / 2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ) );
3835
+
3836
+ const startingY = ( finalYDistance / 2 ) - ( centerRow * ( constantFixtureWidth / mmToFeet ) );
3837
+ const detailedStartingY = ( finalYDetailedDistance / 2 ) - ( centerRow * ( constantDetailedFixtureWidth / mmToFeet ) );
3838
+
3839
+ const colIndex = Math.floor( index / 2 );
3840
+ const rowIndex = index % 2 === 0 ? 1 : 0;
3841
+
3842
+
3843
+ const xPos = roundToTwo( ( startingX + colIndex * ( constantFixtureLength / mmToFeet ) ) );
3844
+ const yPos = roundToTwo( ( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) ) );
3845
+
3846
+ const detailedXPos = roundToTwo( ( detailedStartingX + colIndex * ( constantDetailedFixtureLength / mmToFeet ) ) );
3847
+ const detailedYPos = roundToTwo( ( detailedStartingY + rowIndex * ( constantDetailedFixtureWidth / mmToFeet ) ) );
3848
+
3849
+
3850
+ const fixtureData = {
3851
+ 'fixtureHeight': {
3852
+ 'value': 0,
3853
+ 'unit': 'mm',
3854
+ },
3855
+ 'fixtureLength': {
3856
+ 'value': constantFixtureLength,
3857
+ 'unit': 'mm',
3858
+ },
3859
+ 'fixtureWidth': {
3860
+ 'value': constantFixtureWidth,
3861
+ 'unit': 'mm',
3862
+ },
3863
+ 'relativePosition': {
3864
+ 'x': xPos,
3865
+ 'y': yPos,
3866
+ 'unit': 'ft',
3867
+ },
3868
+ 'detailedFixtureLength': {
3869
+ 'value': constantDetailedFixtureLength,
3870
+ 'unit': 'mm',
3871
+ },
3872
+ 'detailedFixtureWidth': {
3873
+ 'value': constantDetailedFixtureWidth,
3874
+ 'unit': 'mm',
3875
+ },
3876
+ 'relativeDetailedPosition': {
3877
+ 'x': detailedXPos,
3878
+ 'y': detailedYPos,
3879
+ 'unit': 'ft',
3880
+ },
3881
+ };
3882
+
3883
+ await storeFixtureService.updateOne(
3884
+ {
3885
+ _id: fixture._id,
3886
+ },
3887
+ fixtureData );
3888
+ }
3889
+ }
3890
+ } catch ( e ) {
3891
+ console.log( e );
3892
+ logger.error( { functionName: 'createCrestPlanogram', error: e } );
3893
+ return false;
3894
+ }
3895
+ }
3896
+
3897
+ export async function updatelayout( req, res ) {
3898
+ try {
3899
+ let getLayoutTaskDetails = await planoTaskService.find( { date_string: { $gte: req.body.date }, status: 'incomplete', type: 'layout' } );
3900
+ if ( !getLayoutTaskDetails.length ) {
3901
+ return res.sendError( 'No data found', 204 );
3902
+ }
3903
+ for ( let layout of getLayoutTaskDetails ) {
3904
+ let layoutAnswer = layout.answers[1];
3905
+ let planoDetails = await planoService.findOne( { _id: layout.planoId } );
3906
+ let fixtureDetails = await storeFixtureService.findAndSort( { planoId: layout.planoId, floorId: layout.floorId }, {}, { fixtureNumber: 1 } );
3907
+ if ( layoutAnswer?.extraFixture?.length ) {
3908
+ let deletedFixtureList = layoutAnswer.extraFixture.map( ( fixture ) => fixture.fixtureId );
3909
+ fixtureDetails = fixtureDetails.filter( ( fixture ) => !deletedFixtureList.includes( fixture._id.toString() ) );
3910
+ await storeFixtureService.deleteMany( { _id: { $in: deletedFixtureList } } );
3911
+ await fixtureShelfService.deleteMany( { fixtureId: { $in: deletedFixtureList } } );
3912
+ await planoMappingService.deleteMany( { fixtureId: { $in: deletedFixtureList } } );
3913
+ }
3914
+ if ( layoutAnswer?.wronglyLocatedFixtures?.length ) {
3647
3915
  layoutAnswer.wronglyLocatedFixtures.sort( ( a, b ) => a.position - b.position );
3648
3916
  let elementsGroup = layoutAnswer.wronglyLocatedFixtures.reduce( ( acc, ele ) => {
3649
3917
  ele.location = ele?.location == 'centre' ? 'floor' : ele.location;
@@ -3655,6 +3923,11 @@ export async function updatelayout( req, res ) {
3655
3923
  return acc;
3656
3924
  }, {} );
3657
3925
  Object.entries( elementsGroup ).forEach( async ( [ key, values ] ) => {
3926
+ let removeFixtures = layoutAnswer.wronglyLocatedFixtures.filter( ( rmFx ) => {
3927
+ if ( rmFx.fixtureElement == key && rmFx.location != key ) {
3928
+ return rmFx;
3929
+ }
3930
+ } ).map( ( fxtu ) => fxtu.fixtureId );
3658
3931
  let matchingFixtures = [];
3659
3932
  let maxFixtureNumber = 0;
3660
3933
  let elementType = '';
@@ -3675,6 +3948,7 @@ export async function updatelayout( req, res ) {
3675
3948
  }
3676
3949
  if ( matchingFixtures.length ) {
3677
3950
  matchingFixtures.sort( ( a, b ) => a.associatedElementFixtureNumber - b.associatedElementFixtureNumber );
3951
+ matchingFixtures = matchingFixtures.filter( ( fxt ) => !removeFixtures.includes( fxt._id.toString() ) );
3678
3952
  maxFixtureNumber = Math.max(
3679
3953
  ...matchingFixtures.map( ( f ) => f.associatedElementFixtureNumber ),
3680
3954
  );
@@ -3844,3 +4118,2611 @@ export async function updatelayout( req, res ) {
3844
4118
  }
3845
4119
  }
3846
4120
 
4121
+
4122
+ export async function downloadPlanoImage( req, res ) {
4123
+ try {
4124
+ let query = [
4125
+ {
4126
+ $match: {
4127
+ date_string: { $gte: req.body.fromDate, $lte: req.body.toDate },
4128
+ type: 'layout',
4129
+ status: 'incomplete',
4130
+ },
4131
+ },
4132
+ {
4133
+ $lookup: {
4134
+ from: 'planograms',
4135
+ let: { plano_id: '$planoId' },
4136
+ pipeline: [
4137
+ {
4138
+ $match: {
4139
+ $expr: {
4140
+ $and: [
4141
+ { $eq: [ '$_id', '$$plano_id' ] },
4142
+ ],
4143
+ },
4144
+ },
4145
+ },
4146
+ {
4147
+ $project: {
4148
+ storeName: 1,
4149
+ _id: 0,
4150
+ },
4151
+ },
4152
+ ],
4153
+ as: 'planogram',
4154
+ },
4155
+ },
4156
+ { $unwind: { path: '$planogram', preserveNullAndEmptyArrays: true } },
4157
+ {
4158
+ $project: {
4159
+ storeName: '$planogram.storeName',
4160
+ answers: 1,
4161
+ type: 1,
4162
+ status: 1,
4163
+ planoId: 1,
4164
+ floorId: 1,
4165
+ },
4166
+ },
4167
+ ];
4168
+
4169
+ let taskDetails = await planoTaskService.aggregate( query );
4170
+ if ( !taskDetails.length ) {
4171
+ return res.sendError( 'No data found', 204 );
4172
+ }
4173
+ let planoList = taskDetails.map( ( ele ) => ele.planoId );
4174
+ let storeList = taskDetails.map( ( ele ) => ele.storeName );
4175
+ async function sleep( ms ) {
4176
+ return new Promise( ( resolve ) => setTimeout( resolve, ms ) );
4177
+ }
4178
+
4179
+ async function openPlanoUrls( planoList ) {
4180
+ for ( let id of planoList ) {
4181
+ const url = `http://localhost:8080/#/plano?planoId=${id}&token&url=http://localhost:3008`;
4182
+ const driver = await new Builder().forBrowser( 'chrome' ).build();
4183
+
4184
+ try {
4185
+ await driver.get( url );
4186
+ await sleep( 20000 );
4187
+ } catch ( err ) {
4188
+ console.error( `Error while opening planoId ${id}:`, err );
4189
+ } finally {
4190
+ try {
4191
+ if ( driver && await driver.getSession() ) {
4192
+ await driver.quit();
4193
+ }
4194
+ } catch ( quitErr ) {
4195
+ console.warn( `Error while quitting driver for planoId ${id}:`, quitErr );
4196
+ }
4197
+ }
4198
+ }
4199
+ }
4200
+ if ( !req.body?.merge ) {
4201
+ const downloadsPath = path.join( os.homedir(), 'Downloads' );
4202
+ const targetFolder = path.join( __dirname, '..', '..', `${req.body.file}Images` );
4203
+ await openPlanoUrls( planoList );
4204
+ if ( !fs.existsSync( targetFolder ) ) {
4205
+ fs.mkdirSync( targetFolder, { recursive: true } );
4206
+ console.log( 'Created folder:', targetFolder );
4207
+ }
4208
+ fs.readdir( downloadsPath, async ( err, files ) => {
4209
+ if ( err ) {
4210
+ return console.error( 'Failed to read Downloads folder:', err );
4211
+ }
4212
+ console.log( files );
4213
+ for ( let file of files ) {
4214
+ let fileName = file.split( '.' )[0];
4215
+ const sourcePath = path.join( downloadsPath, file );
4216
+ const targetPath = path.join( targetFolder, file );
4217
+ if ( storeList.includes( fileName ) ) {
4218
+ if ( fs.existsSync( sourcePath ) ) {
4219
+ // let chckFixtureCount = await storeFixtureService.findAndSort( { storeName: fileName }, { associatedElementFixtureNumber: 1 }, { associatedElementFixtureNumber: -1 } );
4220
+ // console.log( chckFixtureCount[0].associatedElementFixtureNumber );
4221
+ // if ( chckFixtureCount[0].associatedElementFixtureNumber < 10 ) {
4222
+ // sharp( sourcePath )
4223
+ // .extract( { left: 1200, top: 30, width: 3800, height: 3200 } )
4224
+ // .toFile( targetPath )
4225
+ // .then( () => {
4226
+ // fs.unlinkSync( sourcePath );
4227
+ // console.log( 'Image cropped successfully!' );
4228
+ // } )
4229
+ // .catch( ( err ) => {
4230
+ // console.error( 'Error cropping image:', err );
4231
+ // } );
4232
+ // } else {
4233
+ fs.copyFile( sourcePath, targetPath, ( err ) => {
4234
+ if ( err ) {
4235
+ console.error( 'Error copying file:', err );
4236
+ } else {
4237
+ fs.unlinkSync( sourcePath );
4238
+ console.log( `File moved from Downloads to ${targetFolder}` );
4239
+ }
4240
+ } );
4241
+ // }
4242
+ } else {
4243
+ console.warn( 'File not found in Downloads:', fileName );
4244
+ }
4245
+ }
4246
+ }
4247
+ } );
4248
+ return res.sendSuccess( 'Image Generated SuccessFully' );
4249
+ }
4250
+
4251
+ if ( req.body?.merge ) {
4252
+ const targetFolder = path.join( __dirname, '..', '..', `mergedImages` );
4253
+ if ( !fs.existsSync( targetFolder ) ) {
4254
+ fs.mkdirSync( targetFolder, { recursive: true } );
4255
+ console.log( 'Created folder:', targetFolder );
4256
+ }
4257
+ let spacing = 500;
4258
+ for ( let store of storeList ) {
4259
+ if ( store != undefined ) {
4260
+ const image1 = path.join( __dirname, '..', '..', `oldImages/${store}.png` );
4261
+ const image2 = path.join( __dirname, '..', '..', `newImages/${store}.png` );
4262
+
4263
+ const output = path.join( targetFolder, `/${store}.png` );
4264
+
4265
+
4266
+ const [ img1, img2 ] = await Promise.all( [
4267
+ sharp( image1 ),
4268
+ sharp( image2 ),
4269
+ ] );
4270
+
4271
+ const [ meta1, meta2 ] = await Promise.all( [ img1.metadata(), img2.metadata() ] );
4272
+
4273
+ const canvasWidth = meta1.width + meta2.width + spacing;
4274
+ const canvasHeight = Math.max( meta1.height, meta2.height );
4275
+
4276
+ const [ img1Buffer, img2Buffer ] = await Promise.all( [
4277
+ img1.toBuffer(),
4278
+ img2.toBuffer(),
4279
+ ] );
4280
+
4281
+ await sharp( {
4282
+ create: {
4283
+ width: canvasWidth,
4284
+ height: canvasHeight,
4285
+ channels: 4,
4286
+ background: { r: 255, g: 255, b: 255 },
4287
+ },
4288
+ } )
4289
+ .composite( [
4290
+ { input: img1Buffer, top: 0, left: 0 },
4291
+ { input: img2Buffer, top: 0, left: meta1.width + spacing },
4292
+ ] )
4293
+ .toFile( output );
4294
+
4295
+ console.log( 'Images merged successfully!' );
4296
+ }
4297
+ }
4298
+ let zip = new JSZip;
4299
+ const promises = storeList.map( async ( store ) => {
4300
+ try {
4301
+ const file = fs.readFileSync( `${targetFolder}/${store}.png` );
4302
+ zip.file( `${store}.png`, file );
4303
+ } catch ( err ) {
4304
+ console.error( `Error reading ${store}.png:`, err );
4305
+ }
4306
+ } );
4307
+
4308
+ await Promise.all( promises );
4309
+
4310
+ const zipBuffer = await zip.generateAsync( { type: 'nodebuffer' } );
4311
+
4312
+ res.set( {
4313
+ 'Content-Type': 'application/zip',
4314
+ 'Content-Disposition': 'attachment; filename=download.zip',
4315
+ } );
4316
+
4317
+ let rmFolderList = [ 'oldImages', 'newImages', 'mergedImages' ];
4318
+ for ( let folder of rmFolderList ) {
4319
+ const filePath = path.join( __dirname, '..', '..', folder );
4320
+ fs.rm( filePath, { recursive: true, force: true } );
4321
+ }
4322
+
4323
+ return res.send( zipBuffer );
4324
+ }
4325
+ } catch ( e ) {
4326
+ console.log( e );
4327
+ return res.sendError( e, 500 );
4328
+ }
4329
+ }
4330
+
4331
+
4332
+ export async function updateCrestPlanogram( req, res ) {
4333
+ try {
4334
+ if ( req?.headers?.authorization?.split( ' ' )[1] !== 'hwjXfCD6TgMvc82cuSGZ9bNv9MuXsaiQ6uvx' ) {
4335
+ return res.sendError( 'Unauthorized', 401 );
4336
+ }
4337
+
4338
+ const startTime = Date.now();
4339
+
4340
+ const layoutApiUrl = 'https://api.getcrest.ai/api/ms_shelfsensei/layout/';
4341
+ let staticToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQ1NTE3MjQxLCJpYXQiOjE3NDU1MTM2NDEsImp0aSI6Ijg3MWVlNTA3ODY2OTQ5OTVhMTQ0YTk4NzQyNzY0MzEzIiwidXNlcl9pZCI6MTA4NSwiaWQiOjEwODUsImlzX21lZXNlZWtfYWNjb3VudCI6ZmFsc2UsImN1c3RvbWVyX2dyb3VwIjozOTgsImxpY2VuY2Vfc2NvcGVzIjpbeyJyZXNvdXJjZV9zZXQiOiJwcF9zZXQiLCJzY29wZV9yb2xlIjoiY29udHJpYnV0b3IifSx7InJlc291cmNlX3NldCI6ImRwX3NldCIsInNjb3BlX3JvbGUiOiJjb250cmlidXRvciJ9LHsicmVzb3VyY2Vfc2V0IjoiZGZfc2V0Iiwic2NvcGVfcm9sZSI6ImNvbnRyaWJ1dG9yIn0seyJyZXNvdXJjZV9zZXQiOiJkZWZhdWx0X3NldCIsInNjb3BlX3JvbGUiOiJjb250cmlidXRvciJ9XX0.eGzTMGwwstr13M0Hu1Ls5-gkE_oSPMJJBL2wgygT6Ac';
4342
+
4343
+ async function fetchStoreData( store, bearerToken, res ) {
4344
+ const payload = JSON.stringify( { store_id: store.toObject().storeName } );
4345
+
4346
+ try {
4347
+ const response = await fetch( layoutApiUrl, {
4348
+ method: 'POST',
4349
+ headers: {
4350
+ 'Authorization': `Bearer ${bearerToken}`,
4351
+ 'Content-Type': 'application/json',
4352
+ 'Content-Length': Buffer.byteLength( payload ),
4353
+ },
4354
+ body: payload,
4355
+ } );
4356
+
4357
+ const data = await response.text();
4358
+ let jsonData = null;
4359
+
4360
+ try {
4361
+ jsonData = JSON.parse( data );
4362
+ } catch ( parseError ) {
4363
+ logger.error( { functionName: `Warning: Received invalid JSON for store ${store.toObject().storeName}`, error: parseError } );
4364
+ console.warn( `Warning: Received invalid JSON for store ${store.toObject().storeName}` );
4365
+ return { storeName: store.toObject().storeName, data: null };
4366
+ }
4367
+
4368
+ if ( jsonData.result === 'Token is invalid or expired' ) {
4369
+ console.log( 'Token expired, retrying...' );
4370
+ try {
4371
+ const newToken = await fetchNewToken();
4372
+ staticToken = newToken;
4373
+ return await fetchStoreData( store, newToken, res );
4374
+ } catch ( retryError ) {
4375
+ logger.error( { functionName: 'Failed to refresh token', error: retryError } );
4376
+ console.log( retryError );
4377
+ return res.sendError( 'Failed to refresh token', 401 );
4378
+ }
4379
+ }
4380
+
4381
+ return { storeName: store.toObject().storeName, data: jsonData };
4382
+ } catch ( error ) {
4383
+ logger.error( { functionName: `Error fetching data for ${store.toObject().storeName}:`, error } );
4384
+ console.error( `Error fetching data for ${store.toObject().storeName}:`, error.message );
4385
+ return { storeName: store.toObject().storeName, data: null };
4386
+ }
4387
+ }
4388
+
4389
+
4390
+ const fetchNewToken = async () => {
4391
+ const fetchWithCookies = fetchCookie( fetch );
4392
+
4393
+ const email = 'tango.lenskart@getcrest.ai';
4394
+ const password = 'Tangolenskart@123';
4395
+
4396
+ const credentials = JSON.stringify( { email, password } );
4397
+
4398
+ const invalidateUrl = 'https://app.getcrest.ai/api/ms_iam/user/session/override/';
4399
+ const tokenUrl = 'https://app.getcrest.ai/api/ms_iam/token/';
4400
+
4401
+ try {
4402
+ const invalidateRes = await fetchWithCookies( invalidateUrl, {
4403
+ method: 'POST',
4404
+ headers: {
4405
+ 'Content-Type': 'application/json',
4406
+ },
4407
+ body: credentials,
4408
+ } );
4409
+
4410
+ const invalidateData = await invalidateRes.json();
4411
+ console.log( 'Invalidate response:', invalidateData );
4412
+
4413
+ console.log( 'Fetching new token...' );
4414
+ const tokenRes = await fetchWithCookies( tokenUrl, {
4415
+ method: 'POST',
4416
+ headers: {
4417
+ 'Content-Type': 'application/json',
4418
+ 'Accept': 'application/json, text/javascript, */*; q=0.01',
4419
+ },
4420
+ body: credentials,
4421
+ } );
4422
+
4423
+ const tokenData = await tokenRes.json();
4424
+ console.log( 'Token response:', tokenData );
4425
+
4426
+ return tokenData.access;
4427
+ } catch ( error ) {
4428
+ console.error( 'Error fetching new token:', error );
4429
+ throw error;
4430
+ }
4431
+ };
4432
+
4433
+ let storeList = await storeService.find( { ...( req?.body?.storeName ) ? { storeName: req?.body?.storeName } : {}, clientId: '11' } );
4434
+
4435
+ const constantFixtureLength = 1220;
4436
+ const constantDetailedFixtureLength = 1220;
4437
+
4438
+ const constantFixtureWidth = 610;
4439
+ const constantDetailedFixtureWidth = 1524;
4440
+
4441
+ const mmToFeet = 305;
4442
+
4443
+ function roundToTwo( num ) {
4444
+ return Math.round( num * 100 ) / 100;
4445
+ }
4446
+
4447
+ for ( let i = 0; i < storeList.length; i++ ) {
4448
+ const storeData = await fetchStoreData( storeList[i], staticToken, res );
4449
+
4450
+ if ( storeData?.data?.message !== 'SUCCESS' ) continue;
4451
+
4452
+ const storeDetails = storeList[i];
4453
+
4454
+ const existingPlanogram = await planoService.findOne( { storeName: storeData.storeName } );
4455
+
4456
+ if ( existingPlanogram ) {
4457
+ const checkTaskSubmitted = await planoTaskService.findOne( { planoId: existingPlanogram.toObject()._id } );
4458
+
4459
+ const checkTaskCreated = await processedTaskService.findOne( { storeName: storeData.storeName, date_iso: { $gte: new Date( ), $lte: new Date( ) }, isPlano: true } );
4460
+
4461
+ if ( checkTaskSubmitted || checkTaskCreated ) {
4462
+ continue;
4463
+ }
4464
+ }
4465
+
4466
+
4467
+ if ( existingPlanogram?.toObject()?._id && mongoose.Types.ObjectId.isValid( existingPlanogram?.toObject()?._id ) ) {
4468
+ await Promise.all( [ planoService.deleteOne( { _id: existingPlanogram?.toObject()?._id } ), storeBuilderService.deleteOne( { planoId: existingPlanogram?.toObject()?._id } ),
4469
+ storeFixtureService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ), fixtureShelfService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ),
4470
+ planoMappingService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ),
4471
+ ] );
4472
+ }
4473
+
4474
+
4475
+ const planoInsertData = {
4476
+ storeName: storeData.storeName,
4477
+ storeId: storeDetails?.toObject()?.storeId ? storeDetails.toObject().storeId : 'nil',
4478
+ layoutName: `${storeData.storeName} - Layout`,
4479
+ clientId: '11',
4480
+ attachments: [],
4481
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
4482
+ createdByName: 'Bejan',
4483
+ createdByEmail: 'bejan@tangotech.co.in',
4484
+ status: 'completed',
4485
+ floorNumber: 1,
4486
+ productResolutionLevel: 'L2',
4487
+ scanType: 'qr',
4488
+ };
4489
+
4490
+ const insertedPlano = await planoService.upsertOne( { storeName: storeData.storeName }, planoInsertData );
4491
+ const planoDoc = insertedPlano.toObject();
4492
+
4493
+ const leftWall = storeData.data.result.filter( ( entry ) => entry['main'] === 'LEFT WALL' );
4494
+ const leftFixtures = leftWall.flatMap( ( wall ) => wall.fixtures );
4495
+ const rightWall = storeData.data.result.filter( ( entry ) => entry['main'] === 'RIGHT WALL' );
4496
+ const rightFixtures = rightWall.flatMap( ( wall ) => wall.fixtures );
4497
+ const backWall = storeData.data.result.filter( ( entry ) => entry['main'] === 'RIGHT VERTICAL WALL' );
4498
+ const backFixtures = backWall.flatMap( ( wall ) => wall.fixtures );
4499
+ const floorFixtures = storeData.data.result.filter(
4500
+ ( entry ) => entry['main'] === 'Euro Center' || entry['main'] === 'Euro Center Dr',
4501
+ );
4502
+
4503
+ const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
4504
+ const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
4505
+
4506
+ const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
4507
+ const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
4508
+
4509
+ const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
4510
+ const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
4511
+
4512
+ const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
4513
+ const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
4514
+
4515
+ const maxFixturesPerRow = floorFixtures.length/2;
4516
+ const totalRows = 2;
4517
+
4518
+ const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantFixtureLength / mmToFeet ) ) ) : 0;
4519
+ const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
4520
+
4521
+ const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( 2 * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
4522
+ const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( 2 * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
4523
+
4524
+ const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
4525
+ const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
4526
+
4527
+ const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
4528
+ const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
4529
+
4530
+ const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
4531
+ const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
4532
+
4533
+ const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet );
4534
+ const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet );
4535
+
4536
+ const finalXDistance = roundToTwo( ( maxXDistance < ( backXDistanceFeet + floorXDistanceFeet )? ( ( backXDistanceFeet + floorXDistanceFeet ) + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDistance + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : maxXDistance ) );
4537
+ const finalXDetailedDistance = roundToTwo( ( maxXDetailedDistance < ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet )? ( ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDetailedDistance + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : maxXDetailedDistance ) );
4538
+
4539
+ const finalYDistance = roundToTwo( ( maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : ( maxYDistance + ( ( constantFixtureWidth )/mmToFeet ) ) ) );
4540
+ const finalYDetailedDistance = roundToTwo( ( maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : ( maxYDetailedDistance + ( ( constantDetailedFixtureWidth )/mmToFeet ) ) ) );
4541
+
4542
+
4543
+ const floorInsertData = {
4544
+ storeName: planoDoc.storeName,
4545
+ storeId: planoDoc.storeId,
4546
+ layoutName: `${planoDoc.storeName} - Layout`,
4547
+ clientId: '11',
4548
+ floorNumber: 1,
4549
+ floorName: 'floor 1',
4550
+ layoutPolygon: [
4551
+ {
4552
+ elementType: 'wall',
4553
+ distance: finalXDistance,
4554
+ unit: 'ft',
4555
+ direction: 'right',
4556
+ angle: 90,
4557
+ elementNumber: 1,
4558
+ detailedDistance: finalXDetailedDistance,
4559
+ },
4560
+ {
4561
+ elementType: 'wall',
4562
+ distance: finalYDistance,
4563
+ unit: 'ft',
4564
+ direction: 'down',
4565
+ angle: 90,
4566
+ elementNumber: 2,
4567
+ detailedDistance: finalYDetailedDistance,
4568
+ },
4569
+ {
4570
+ elementType: 'wall',
4571
+ distance: finalXDistance,
4572
+ unit: 'ft',
4573
+ direction: 'left',
4574
+ angle: 90,
4575
+ elementNumber: 3,
4576
+ detailedDistance: finalXDetailedDistance,
4577
+ },
4578
+ {
4579
+ elementType: 'wall',
4580
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
4581
+ unit: 'ft',
4582
+ direction: 'up',
4583
+ angle: 90,
4584
+ elementNumber: 4,
4585
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
4586
+ },
4587
+ {
4588
+ elementType: 'entrance',
4589
+ distance: roundToTwo( ( ( finalYDistance * 20 ) / 100 ) ),
4590
+ unit: 'ft',
4591
+ direction: 'up',
4592
+ angle: 90,
4593
+ elementNumber: 1,
4594
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 30 ) / 100 ) ),
4595
+ },
4596
+ {
4597
+ elementType: 'wall',
4598
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
4599
+ unit: 'ft',
4600
+ direction: 'up',
4601
+ angle: 90,
4602
+ elementNumber: 5,
4603
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
4604
+ },
4605
+ ],
4606
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
4607
+ createdByName: 'Bejan',
4608
+ createdByEmail: 'bejan@tangotech.co.in',
4609
+ status: 'completed',
4610
+ planoId: planoDoc._id,
4611
+ };
4612
+
4613
+ const layoutDoc = await storeBuilderService.upsertOne( { planoId: planoDoc._id }, floorInsertData );
4614
+
4615
+ let fixtureCounter = 1;
4616
+
4617
+ const leftFixturePromises = leftFixtures.map( async ( fixture, index ) => {
4618
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
4619
+ if ( !fixtureConfig ) return;
4620
+ const fixtureConfigDoc = fixtureConfig.toObject();
4621
+
4622
+ const fixtureData = {
4623
+ 'clientId': layoutDoc.clientId,
4624
+ 'storeName': layoutDoc.storeName,
4625
+ 'storeId': layoutDoc.storeId,
4626
+ 'planoId': layoutDoc.planoId,
4627
+ 'floorId': layoutDoc._id,
4628
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
4629
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
4630
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4631
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4632
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
4633
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
4634
+ 'fixtureType': 'wall',
4635
+ 'fixtureHeight': {
4636
+ 'value': 0,
4637
+ 'unit': 'mm',
4638
+ },
4639
+ 'fixtureLength': {
4640
+ 'value': constantFixtureLength,
4641
+ 'unit': 'mm',
4642
+ },
4643
+ 'fixtureWidth': {
4644
+ 'value': constantFixtureWidth,
4645
+ 'unit': 'mm',
4646
+ },
4647
+ 'associatedElementType': 'wall',
4648
+ 'associatedElementNumber': 1,
4649
+ 'relativePosition': {
4650
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
4651
+ 'y': 0,
4652
+ 'unit': 'ft',
4653
+ },
4654
+ 'fixtureNumber': fixtureCounter,
4655
+ 'detailedFixtureLength': {
4656
+ 'value': constantDetailedFixtureLength,
4657
+ 'unit': 'mm',
4658
+ },
4659
+ 'detailedFixtureWidth': {
4660
+ 'value': constantDetailedFixtureWidth,
4661
+ 'unit': 'mm',
4662
+ },
4663
+ 'relativeDetailedPosition': {
4664
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
4665
+ 'y': 0,
4666
+ 'unit': 'ft',
4667
+ },
4668
+ 'productResolutionLevel': 'L2',
4669
+ 'associatedElementFixtureNumber': index+1,
4670
+ 'header': fixture.header,
4671
+ 'footer': fixture.footer,
4672
+ 'fixtureConfigId': fixtureConfigDoc._id,
4673
+ };
4674
+
4675
+ const createdFixture = await storeFixtureService.upsertOne(
4676
+ {
4677
+ floorId: layoutDoc._id,
4678
+ fixtureNumber: fixtureCounter++,
4679
+ },
4680
+ fixtureData,
4681
+ );
4682
+
4683
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
4684
+
4685
+ await Promise.all(
4686
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
4687
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
4688
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
4689
+
4690
+ const shelfData = {
4691
+ 'clientId': '11',
4692
+ 'storeName': layoutDoc.storeName,
4693
+ 'storeId': layoutDoc.storeId,
4694
+ 'planoId': layoutDoc.planoId,
4695
+ 'floorId': layoutDoc._id,
4696
+ 'fixtureId': createdFixture._id,
4697
+ 'shelfNumber': j + 1,
4698
+ 'shelfOrder': 'LTR',
4699
+ 'shelfCapacity': configShelf.shelfCapacity,
4700
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
4701
+ 'sectionZone': configShelf.shelfZone,
4702
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
4703
+ };
4704
+
4705
+ await fixtureShelfService.upsertOne(
4706
+ {
4707
+ fixtureId: createdFixture._id,
4708
+ shelfNumber: j + 1,
4709
+ },
4710
+ shelfData,
4711
+ );
4712
+ } ),
4713
+ );
4714
+
4715
+ await Promise.all(
4716
+ fixture.productZones?.map( async ( zone ) => {
4717
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
4718
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
4719
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
4720
+
4721
+ await Promise.all(
4722
+ vms.map( async ( vm ) => {
4723
+ let configData = vmConfig[0];
4724
+
4725
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
4726
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
4727
+ }
4728
+
4729
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
4730
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
4731
+ }
4732
+
4733
+ if ( !configData ) return;
4734
+
4735
+ const insertData = {
4736
+ 'clientId': '11',
4737
+ 'productId': 'VMCR',
4738
+ 'type': 'vm',
4739
+ 'productName': vm.productName,
4740
+ 'productHeight': {
4741
+ 'value': configData.vmHeightmm,
4742
+ 'unit': 'mm',
4743
+ },
4744
+ 'productWidth': {
4745
+ 'value': configData.vmWidthmm,
4746
+ 'unit': 'mm',
4747
+ },
4748
+ 'startYPosition': configData.startShelf,
4749
+ 'endYPosition': configData.endShelf,
4750
+ 'xZone': configData.zone,
4751
+ 'fixtureConfigId': fixtureConfig._id,
4752
+ };
4753
+
4754
+ const vmTemplate = await planoProductService.upsertOne(
4755
+ {
4756
+ 'productName': vm.productName,
4757
+ 'fixtureConfigId': fixtureConfig._id,
4758
+ 'productHeight.value': configData.vmHeightmm,
4759
+ 'productWidth.value': configData.vmWidthmm,
4760
+ 'startYPosition': configData.startShelf,
4761
+ 'endYPosition': configData.endShelf,
4762
+ 'xZone': configData.zone,
4763
+ },
4764
+ insertData,
4765
+ );
4766
+
4767
+ const vmData = {
4768
+ 'clientId': layoutDoc.clientId,
4769
+ 'storeName': layoutDoc.storeName,
4770
+ 'storeId': layoutDoc.storeId,
4771
+ 'planoId': layoutDoc.planoId,
4772
+ 'floorId': layoutDoc._id,
4773
+ 'type': 'vm',
4774
+ 'fixtureId': createdFixture._id,
4775
+ 'productId': vmTemplate._id,
4776
+ };
4777
+
4778
+ await planoMappingService.upsertOne(
4779
+ {
4780
+ fixtureId: createdFixture._id,
4781
+ productId: vmTemplate._id,
4782
+ },
4783
+ vmData,
4784
+ );
4785
+ } ),
4786
+ );
4787
+ } ) || [],
4788
+ );
4789
+ } );
4790
+
4791
+ const backFixturePromises = backFixtures.map( async ( fixture, index ) => {
4792
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
4793
+ if ( !fixtureConfig ) return;
4794
+ const fixtureConfigDoc = fixtureConfig.toObject();
4795
+
4796
+ const fixtureData = {
4797
+ 'clientId': layoutDoc.clientId,
4798
+ 'storeName': layoutDoc.storeName,
4799
+ 'storeId': layoutDoc.storeId,
4800
+ 'planoId': layoutDoc.planoId,
4801
+ 'floorId': layoutDoc._id,
4802
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
4803
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
4804
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4805
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4806
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
4807
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
4808
+ 'fixtureType': 'wall',
4809
+ 'fixtureHeight': {
4810
+ 'value': 0,
4811
+ 'unit': 'mm',
4812
+ },
4813
+ 'fixtureLength': {
4814
+ 'value': constantFixtureWidth,
4815
+ 'unit': 'mm',
4816
+ },
4817
+ 'fixtureWidth': {
4818
+ 'value': constantFixtureLength,
4819
+ 'unit': 'mm',
4820
+ },
4821
+ 'associatedElementType': 'wall',
4822
+ 'associatedElementNumber': 2,
4823
+ 'relativePosition': {
4824
+ 'x': roundToTwo( ( finalXDistance - ( constantFixtureWidth/mmToFeet ) ) ),
4825
+ 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
4826
+ 'unit': 'ft',
4827
+ },
4828
+ 'fixtureNumber': fixtureCounter,
4829
+ 'detailedFixtureLength': {
4830
+ 'value': constantDetailedFixtureLength,
4831
+ 'unit': 'mm',
4832
+ },
4833
+ 'detailedFixtureWidth': {
4834
+ 'value': constantDetailedFixtureWidth,
4835
+ 'unit': 'mm',
4836
+ },
4837
+ 'relativeDetailedPosition': {
4838
+ 'x': roundToTwo( ( finalXDetailedDistance - ( constantDetailedFixtureLength/mmToFeet ) ) ),
4839
+ 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
4840
+ 'unit': 'ft',
4841
+ },
4842
+ 'productResolutionLevel': 'L2',
4843
+ 'associatedElementFixtureNumber': index+1,
4844
+ 'header': fixture.header,
4845
+ 'footer': fixture.footer,
4846
+ 'fixtureConfigId': fixtureConfigDoc._id,
4847
+ };
4848
+
4849
+ const createdFixture = await storeFixtureService.upsertOne(
4850
+ {
4851
+ floorId: layoutDoc._id,
4852
+ fixtureNumber: fixtureCounter++,
4853
+ },
4854
+ fixtureData,
4855
+ );
4856
+
4857
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
4858
+
4859
+ await Promise.all(
4860
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
4861
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
4862
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
4863
+
4864
+ const shelfData = {
4865
+ 'clientId': '11',
4866
+ 'storeName': layoutDoc.storeName,
4867
+ 'storeId': layoutDoc.storeId,
4868
+ 'planoId': layoutDoc.planoId,
4869
+ 'floorId': layoutDoc._id,
4870
+ 'fixtureId': createdFixture._id,
4871
+ 'shelfNumber': j + 1,
4872
+ 'shelfOrder': 'LTR',
4873
+ 'shelfCapacity': configShelf.shelfCapacity,
4874
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
4875
+ 'sectionZone': configShelf.shelfZone,
4876
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
4877
+ }; ;
4878
+
4879
+ await fixtureShelfService.upsertOne(
4880
+ {
4881
+ fixtureId: createdFixture._id,
4882
+ shelfNumber: j + 1,
4883
+ },
4884
+ shelfData,
4885
+ );
4886
+ } ),
4887
+ );
4888
+
4889
+ await Promise.all(
4890
+ fixture.productZones?.map( async ( zone ) => {
4891
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
4892
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
4893
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
4894
+
4895
+ await Promise.all(
4896
+ vms.map( async ( vm ) => {
4897
+ let configData = vmConfig[0];
4898
+
4899
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
4900
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
4901
+ }
4902
+
4903
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
4904
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
4905
+ }
4906
+
4907
+ if ( !configData ) return;
4908
+
4909
+ const insertData = {
4910
+ 'clientId': '11',
4911
+ 'productId': 'VMCR',
4912
+ 'type': 'vm',
4913
+ 'productName': vm.productName,
4914
+ 'productHeight': {
4915
+ 'value': configData.vmHeightmm,
4916
+ 'unit': 'mm',
4917
+ },
4918
+ 'productWidth': {
4919
+ 'value': configData.vmWidthmm,
4920
+ 'unit': 'mm',
4921
+ },
4922
+ 'startYPosition': configData.startShelf,
4923
+ 'endYPosition': configData.endShelf,
4924
+ 'xZone': configData.zone,
4925
+ 'fixtureConfigId': fixtureConfig._id,
4926
+ };
4927
+
4928
+ const vmTemplate = await planoProductService.upsertOne(
4929
+ {
4930
+ 'productName': vm.productName,
4931
+ 'fixtureConfigId': fixtureConfig._id,
4932
+ 'productHeight.value': configData.vmHeightmm,
4933
+ 'productWidth.value': configData.vmWidthmm,
4934
+ 'startYPosition': configData.startShelf,
4935
+ 'endYPosition': configData.endShelf,
4936
+ 'xZone': configData.zone,
4937
+ },
4938
+ insertData,
4939
+ );
4940
+
4941
+ const vmData = {
4942
+ 'clientId': layoutDoc.clientId,
4943
+ 'storeName': layoutDoc.storeName,
4944
+ 'storeId': layoutDoc.storeId,
4945
+ 'planoId': layoutDoc.planoId,
4946
+ 'floorId': layoutDoc._id,
4947
+ 'type': 'vm',
4948
+ 'fixtureId': createdFixture._id,
4949
+ 'productId': vmTemplate._id,
4950
+ };
4951
+
4952
+ await planoMappingService.upsertOne(
4953
+ {
4954
+ fixtureId: createdFixture._id,
4955
+ productId: vmTemplate._id,
4956
+ },
4957
+ vmData,
4958
+ );
4959
+ } ),
4960
+ );
4961
+ } ) || [],
4962
+ );
4963
+ } );
4964
+
4965
+ const rightFixturePromises = rightFixtures.map( async ( fixture, index ) => {
4966
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
4967
+ if ( !fixtureConfig ) return;
4968
+ const fixtureConfigDoc = fixtureConfig.toObject();
4969
+
4970
+ const fixtureData = {
4971
+ 'clientId': layoutDoc.clientId,
4972
+ 'storeName': layoutDoc.storeName,
4973
+ 'storeId': layoutDoc.storeId,
4974
+ 'planoId': layoutDoc.planoId,
4975
+ 'floorId': layoutDoc._id,
4976
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
4977
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
4978
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4979
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
4980
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
4981
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
4982
+ 'fixtureType': 'wall',
4983
+ 'fixtureHeight': {
4984
+ 'value': 0,
4985
+ 'unit': 'mm',
4986
+ },
4987
+ 'fixtureLength': {
4988
+ 'value': constantFixtureLength,
4989
+ 'unit': 'mm',
4990
+ },
4991
+ 'fixtureWidth': {
4992
+ 'value': constantFixtureWidth,
4993
+ 'unit': 'mm',
4994
+ },
4995
+ 'associatedElementType': 'wall',
4996
+ 'associatedElementNumber': 3,
4997
+ 'relativePosition': {
4998
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
4999
+ 'y': roundToTwo( ( finalYDistance - ( constantFixtureWidth / mmToFeet ) ) ),
5000
+ 'unit': 'ft',
5001
+ },
5002
+ 'fixtureNumber': fixtureCounter,
5003
+ 'detailedFixtureLength': {
5004
+ 'value': constantDetailedFixtureLength,
5005
+ 'unit': 'mm',
5006
+ },
5007
+ 'detailedFixtureWidth': {
5008
+ 'value': constantDetailedFixtureWidth,
5009
+ 'unit': 'mm',
5010
+ },
5011
+ 'relativeDetailedPosition': {
5012
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
5013
+ 'y': roundToTwo( ( finalYDetailedDistance - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
5014
+ 'unit': 'ft',
5015
+ },
5016
+ 'productResolutionLevel': 'L2',
5017
+ 'associatedElementFixtureNumber': index+1,
5018
+ 'header': fixture.header,
5019
+ 'footer': fixture.footer,
5020
+ 'fixtureConfigId': fixtureConfigDoc._id,
5021
+ };
5022
+
5023
+ const createdFixture = await storeFixtureService.upsertOne(
5024
+ {
5025
+ floorId: layoutDoc._id,
5026
+ fixtureNumber: fixtureCounter++,
5027
+ },
5028
+ fixtureData,
5029
+ );
5030
+
5031
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
5032
+
5033
+ await Promise.all(
5034
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
5035
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
5036
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
5037
+
5038
+ const shelfData = {
5039
+ 'clientId': '11',
5040
+ 'storeName': layoutDoc.storeName,
5041
+ 'storeId': layoutDoc.storeId,
5042
+ 'planoId': layoutDoc.planoId,
5043
+ 'floorId': layoutDoc._id,
5044
+ 'fixtureId': createdFixture._id,
5045
+ 'shelfNumber': j + 1,
5046
+ 'shelfOrder': 'LTR',
5047
+ 'shelfCapacity': configShelf.shelfCapacity,
5048
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
5049
+ 'sectionZone': configShelf.shelfZone,
5050
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
5051
+ };
5052
+
5053
+ await fixtureShelfService.upsertOne(
5054
+ {
5055
+ fixtureId: createdFixture._id,
5056
+ shelfNumber: j + 1,
5057
+ },
5058
+ shelfData,
5059
+ );
5060
+ } ),
5061
+ );
5062
+
5063
+ await Promise.all(
5064
+ fixture.productZones?.map( async ( zone ) => {
5065
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
5066
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
5067
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
5068
+
5069
+ await Promise.all(
5070
+ vms.map( async ( vm ) => {
5071
+ let configData = vmConfig[0];
5072
+
5073
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
5074
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
5075
+ }
5076
+
5077
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
5078
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
5079
+ }
5080
+
5081
+ if ( !configData ) return;
5082
+
5083
+ const insertData = {
5084
+ 'clientId': '11',
5085
+ 'productId': 'VMCR',
5086
+ 'type': 'vm',
5087
+ 'productName': vm.productName,
5088
+ 'productHeight': {
5089
+ 'value': configData.vmHeightmm,
5090
+ 'unit': 'mm',
5091
+ },
5092
+ 'productWidth': {
5093
+ 'value': configData.vmWidthmm,
5094
+ 'unit': 'mm',
5095
+ },
5096
+ 'startYPosition': configData.startShelf,
5097
+ 'endYPosition': configData.endShelf,
5098
+ 'xZone': configData.zone,
5099
+ 'fixtureConfigId': fixtureConfig._id,
5100
+ };
5101
+
5102
+ const vmTemplate = await planoProductService.upsertOne(
5103
+ {
5104
+ 'productName': vm.productName,
5105
+ 'fixtureConfigId': fixtureConfig._id,
5106
+ 'productHeight.value': configData.vmHeightmm,
5107
+ 'productWidth.value': configData.vmWidthmm,
5108
+ 'startYPosition': configData.startShelf,
5109
+ 'endYPosition': configData.endShelf,
5110
+ 'xZone': configData.zone,
5111
+ },
5112
+ insertData,
5113
+ );
5114
+
5115
+ const vmData = {
5116
+ 'clientId': layoutDoc.clientId,
5117
+ 'storeName': layoutDoc.storeName,
5118
+ 'storeId': layoutDoc.storeId,
5119
+ 'planoId': layoutDoc.planoId,
5120
+ 'floorId': layoutDoc._id,
5121
+ 'type': 'vm',
5122
+ 'fixtureId': createdFixture._id,
5123
+ 'productId': vmTemplate._id,
5124
+ };
5125
+
5126
+ await planoMappingService.upsertOne(
5127
+ {
5128
+ fixtureId: createdFixture._id,
5129
+ productId: vmTemplate._id,
5130
+ },
5131
+ vmData,
5132
+ );
5133
+ } ),
5134
+ );
5135
+ } ) || [],
5136
+ );
5137
+ } );
5138
+
5139
+ const floorFixturePromises = floorFixtures.map( async ( fixture, index ) => {
5140
+ const centerRow = Math.floor( totalRows / 2 );
5141
+
5142
+ const startingX = roundToTwo( ( finalXDistance / 2 - ( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) ) );
5143
+ const detailedStartingX = roundToTwo( ( finalXDetailedDistance / 2 - ( maxFixturesPerRow / 2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) );
5144
+
5145
+ const startingY = finalYDistance / 2 - centerRow * ( constantFixtureWidth / mmToFeet );
5146
+ const detailedStartingY = finalYDetailedDistance / 2 - centerRow * ( constantDetailedFixtureWidth / mmToFeet );
5147
+
5148
+ const colIndex = Math.floor( index / 2 );
5149
+ const rowIndex = index % 2 === 0 ? 1 : 0;
5150
+
5151
+ const xPos = roundToTwo( startingX + colIndex * ( constantFixtureLength / mmToFeet ) );
5152
+ const yPos = roundToTwo( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) );
5153
+
5154
+ const detailedXPos = roundToTwo( ( detailedStartingX + colIndex * ( constantDetailedFixtureLength / mmToFeet ) ) );
5155
+ const detailedYPos = roundToTwo( ( detailedStartingY + rowIndex * ( constantDetailedFixtureWidth / mmToFeet ) ) );
5156
+
5157
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.main } );
5158
+ if ( !fixtureConfig ) return;
5159
+
5160
+ const fixtureConfigDoc = fixtureConfig.toObject();
5161
+
5162
+ const fixtureData = {
5163
+ 'clientId': layoutDoc.clientId,
5164
+ 'storeName': layoutDoc.storeName,
5165
+ 'storeId': layoutDoc.storeId,
5166
+ 'planoId': layoutDoc.planoId,
5167
+ 'floorId': layoutDoc._id,
5168
+ 'fixtureName': `Fixture ${index+1} - ${fixture.main}`,
5169
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
5170
+ 'fixtureBrandCategory': fixture.centerSubMain ? fixture.centerSubMain : undefined,
5171
+ 'fixtureBrandSubCategory': fixture.centerSubMain ? fixture.centerSubMain : undefined,
5172
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
5173
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
5174
+ 'fixtureType': 'floor',
5175
+ 'fixtureHeight': {
5176
+ 'value': 0,
5177
+ 'unit': 'mm',
5178
+ },
5179
+ 'fixtureLength': {
5180
+ 'value': constantFixtureLength,
5181
+ 'unit': 'mm',
5182
+ },
5183
+ 'fixtureWidth': {
5184
+ 'value': constantFixtureWidth,
5185
+ 'unit': 'mm',
5186
+ },
5187
+ 'relativePosition': {
5188
+ 'x': xPos,
5189
+ 'y': yPos,
5190
+ 'unit': 'ft',
5191
+ },
5192
+ 'fixtureNumber': fixtureCounter,
5193
+ 'detailedFixtureLength': {
5194
+ 'value': constantDetailedFixtureLength,
5195
+ 'unit': 'mm',
5196
+ },
5197
+ 'detailedFixtureWidth': {
5198
+ 'value': constantDetailedFixtureWidth,
5199
+ 'unit': 'mm',
5200
+ },
5201
+ 'relativeDetailedPosition': {
5202
+ 'x': detailedXPos,
5203
+ 'y': detailedYPos,
5204
+ 'unit': 'ft',
5205
+ },
5206
+ 'productResolutionLevel': 'L2',
5207
+ 'associatedElementFixtureNumber': index+1,
5208
+ 'fixtureConfigId': fixtureConfigDoc._id,
5209
+ };
5210
+
5211
+
5212
+ const createdFixture = await storeFixtureService.upsertOne(
5213
+ {
5214
+ floorId: layoutDoc._id,
5215
+ fixtureNumber: fixtureCounter++,
5216
+ },
5217
+ fixtureData,
5218
+ );
5219
+
5220
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
5221
+
5222
+ await Promise.all(
5223
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
5224
+ const shelfSection = fixture.centerSuperSubMain.find(
5225
+ ( product ) => product.isVisualMerchandiser === false || product.isVisualMerchandiser === true,
5226
+ );
5227
+
5228
+ const shelfData = {
5229
+ 'clientId': '11',
5230
+ 'storeName': layoutDoc.storeName,
5231
+ 'storeId': layoutDoc.storeId,
5232
+ 'planoId': layoutDoc.planoId,
5233
+ 'floorId': layoutDoc._id,
5234
+ 'fixtureId': createdFixture._id,
5235
+ 'shelfNumber': j + 1,
5236
+ 'shelfOrder': 'LTR',
5237
+ 'shelfCapacity': configShelf.shelfCapacity,
5238
+ 'sectionName': fixture.centerSuperSubMain.find( ( product ) => product.isVisualMerchandiser === true ) ? shelfSection?.name + ' PIDs' : shelfSection?.name,
5239
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
5240
+ };
5241
+
5242
+ await fixtureShelfService.upsertOne(
5243
+ {
5244
+ fixtureId: createdFixture._id,
5245
+ shelfNumber: j + 1,
5246
+ },
5247
+ shelfData,
5248
+ );
5249
+ } ),
5250
+ );
5251
+
5252
+ const vm = fixture.centerSuperSubMain.find( ( vm ) => vm.isVisualMerchandiser );
5253
+ const vmConfig = fixtureConfigDoc.vmConfig;
5254
+
5255
+ if ( vm ) {
5256
+ const [ configData1, configData2 ] = [ vmConfig[0], vmConfig[1] ];
5257
+
5258
+ const insertData1 = {
5259
+ 'clientId': '11',
5260
+ 'productId': 'VMCR',
5261
+ 'type': 'vm',
5262
+ 'productName': vm.name,
5263
+ 'productHeight': {
5264
+ 'value': configData1.vmHeightmm,
5265
+ 'unit': 'mm',
5266
+ },
5267
+ 'productWidth': {
5268
+ 'value': configData1.vmWidthmm,
5269
+ 'unit': 'mm',
5270
+ },
5271
+ 'startYPosition': configData1.startShelf,
5272
+ 'endYPosition': configData1.endShelf,
5273
+ 'xZone': configData1.zone,
5274
+ 'fixtureConfigId': fixtureConfig._id,
5275
+ };
5276
+ const insertData2 = {
5277
+ 'clientId': '11',
5278
+ 'productId': 'VMCR',
5279
+ 'type': 'vm',
5280
+ 'productName': ' ',
5281
+ 'productHeight': {
5282
+ 'value': configData2.vmHeightmm,
5283
+ 'unit': 'mm',
5284
+ },
5285
+ 'productWidth': {
5286
+ 'value': configData2.vmWidthmm,
5287
+ 'unit': 'mm',
5288
+ },
5289
+ 'startYPosition': configData2.startShelf,
5290
+ 'endYPosition': configData2.endShelf,
5291
+ 'xZone': configData2.zone,
5292
+ 'fixtureConfigId': fixtureConfig._id,
5293
+ };
5294
+
5295
+ const [ vmTemplate1, vmTemplate2 ] = await Promise.all( [
5296
+ planoProductService.upsertOne(
5297
+ {
5298
+ 'productName': vm.name,
5299
+ 'fixtureConfigId': fixtureConfig._id,
5300
+ 'productHeight.value': configData1.vmHeightmm,
5301
+ 'productWidth.value': configData1.vmWidthmm,
5302
+ 'startYPosition': configData1.startShelf,
5303
+ 'endYPosition': configData1.endShelf,
5304
+ 'xZone': configData1.zone,
5305
+ },
5306
+ insertData1,
5307
+ ),
5308
+ planoProductService.upsertOne(
5309
+ {
5310
+ 'productName': ' ',
5311
+ 'fixtureConfigId': fixtureConfig._id,
5312
+ 'productHeight.value': configData2.vmHeightmm,
5313
+ 'productWidth.value': configData2.vmWidthmm,
5314
+ 'startYPosition': configData2.startShelf,
5315
+ 'endYPosition': configData2.endShelf,
5316
+ 'xZone': configData2.zone,
5317
+ },
5318
+ insertData2,
5319
+ ),
5320
+ ] );
5321
+
5322
+ const vmData1 = {
5323
+ 'clientId': layoutDoc.clientId,
5324
+ 'storeName': layoutDoc.storeName,
5325
+ 'storeId': layoutDoc.storeId,
5326
+ 'planoId': layoutDoc.planoId,
5327
+ 'floorId': layoutDoc._id,
5328
+ 'type': 'vm',
5329
+ 'fixtureId': createdFixture._id,
5330
+ 'productId': vmTemplate1._id,
5331
+ };
5332
+ const vmData2 = {
5333
+ 'clientId': layoutDoc.clientId,
5334
+ 'storeName': layoutDoc.storeName,
5335
+ 'storeId': layoutDoc.storeId,
5336
+ 'planoId': layoutDoc.planoId,
5337
+ 'floorId': layoutDoc._id,
5338
+ 'type': 'vm',
5339
+ 'fixtureId': createdFixture._id,
5340
+ 'productId': vmTemplate2._id,
5341
+ };
5342
+
5343
+ await Promise.all( [
5344
+ planoMappingService.upsertOne(
5345
+ {
5346
+ fixtureId: createdFixture._id,
5347
+ productId: vmTemplate1._id,
5348
+ },
5349
+ vmData1,
5350
+ ),
5351
+ planoMappingService.upsertOne(
5352
+ {
5353
+ fixtureId: createdFixture._id,
5354
+ productId: vmTemplate2._id,
5355
+ },
5356
+ vmData2,
5357
+ ),
5358
+ ] );
5359
+ }
5360
+ } );
5361
+
5362
+ await Promise.all( [ ...leftFixturePromises, ...backFixturePromises, ...rightFixturePromises, ...floorFixturePromises ] );
5363
+
5364
+ const now = Date.now();
5365
+ const elapsedMinutes = ( now - startTime ) / 1000 / 60;
5366
+ console.log( `Store name: ${storeData.storeName}, Iteration ${i + 1}: total elapsed time = ${elapsedMinutes.toFixed( 2 )} minutes` );
5367
+ }
5368
+
5369
+ res.sendSuccess( 'Updated Successfully' );
5370
+ } catch ( e ) {
5371
+ logger.error( { functionName: 'updateCrestPlanogram', error: e } );
5372
+ return res.sendError( e.message || 'Internal Server Error', 500 );
5373
+ }
5374
+ }
5375
+
5376
+ export async function getVideoLinks( req, res ) {
5377
+ if ( req?.headers?.authorization?.split( ' ' )[1] !== 'hwjXfCD6TgMvc82cuSGZ9bNv9MuXsaiQ6uvx' ) {
5378
+ return res.sendError( 'Unauthorized', 401 );
5379
+ }
5380
+ try {
5381
+ const query = [
5382
+ {
5383
+ $match: {
5384
+ date_string: { $gte: req.body.fromDate, $lte: req.body.toDate },
5385
+ type: 'layout',
5386
+ status: req.body.status,
5387
+ },
5388
+ },
5389
+ {
5390
+ $lookup: {
5391
+ from: 'planograms',
5392
+ let: { plano_id: '$planoId' },
5393
+ pipeline: [
5394
+ {
5395
+ $match: {
5396
+ $expr: {
5397
+ $and: [
5398
+ { $eq: [ '$_id', '$$plano_id' ] },
5399
+ ],
5400
+ },
5401
+ },
5402
+ },
5403
+ {
5404
+ $project: {
5405
+ storeName: 1,
5406
+ _id: 0,
5407
+ },
5408
+ },
5409
+ ],
5410
+ as: 'planogram',
5411
+ },
5412
+ },
5413
+ { $unwind: { path: '$planogram', preserveNullAndEmptyArrays: true } },
5414
+ {
5415
+ $project: {
5416
+ storeName: '$planogram.storeName',
5417
+ answers: 1,
5418
+ type: 1,
5419
+ status: 1,
5420
+ planoId: 1,
5421
+ floorId: 1,
5422
+ },
5423
+ },
5424
+ ];
5425
+ const responseData = await planoTaskService.aggregate( query );
5426
+
5427
+ const videoData = [];
5428
+
5429
+ const seenStores = new Set();
5430
+
5431
+ for ( const data of responseData ) {
5432
+ const store = await planoService.findOne( { _id: data.planoId } );
5433
+ if ( seenStores.has( store?.toObject()?.storeName ) ) {
5434
+ continue;
5435
+ }
5436
+ const [ q1, q2, q3 ] = data.answers;
5437
+
5438
+ const params = {
5439
+ Bucket: 'tango-planogram',
5440
+ file_path: q3?.video || null,
5441
+ };
5442
+
5443
+ const videoUrl = await signedUrl( params );
5444
+
5445
+ videoData.push( {
5446
+ date: data.date_string,
5447
+ store: store?.toObject()?.storeName,
5448
+ videoUrl,
5449
+ } );
5450
+
5451
+ seenStores.add( store?.toObject()?.storeName );
5452
+ }
5453
+
5454
+ const resData = {
5455
+ count: videoData.length,
5456
+ data: videoData,
5457
+ };
5458
+
5459
+ res.sendSuccess( resData );
5460
+ } catch ( e ) {
5461
+ console.log( e );
5462
+ return res.sendError( e, 500 );
5463
+ }
5464
+ }
5465
+
5466
+ export async function updateExcelPlanogram( req, res ) {
5467
+ try {
5468
+ if ( req?.headers?.authorization?.split( ' ' )[1] !== 'hwjXfCD6TgMvc82cuSGZ9bNv9MuXsaiQ6uvx' ) {
5469
+ return res.sendError( 'Unauthorized', 401 );
5470
+ }
5471
+
5472
+ const startTime = Date.now();
5473
+
5474
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
5475
+ const sheetName = 'Sheet1';
5476
+ const raw = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
5477
+
5478
+ const storeNames = new Set();
5479
+
5480
+ raw.forEach( ( entry ) => storeNames.add( entry?.['Store ID'] ) );
5481
+
5482
+ const storeNameArr = Array.from( storeNames );
5483
+
5484
+ const storeList = await storeService.find( { clientId: '11', storeName: { $in: storeNameArr } }, { storeName: 1, storeId: 1 } );
5485
+
5486
+ function transformFixtureData( inputData ) {
5487
+ const storeMap = new Map();
5488
+
5489
+ inputData.forEach( ( item ) => {
5490
+ const storeName = item['Store ID'];
5491
+ if ( !storeMap.has( storeName ) ) {
5492
+ storeMap.set( storeName, [] );
5493
+ }
5494
+ storeMap.get( storeName ).push( item );
5495
+ } );
5496
+
5497
+ const result = [];
5498
+
5499
+ for ( const [ storeName, storeItems ] of storeMap.entries() ) {
5500
+ const wallGroups = new Map();
5501
+ const fixtureGroupMap = new Map();
5502
+
5503
+ function addZoneProduct( fixture, zoneName, productName, isVM ) {
5504
+ let zone = fixture.productZones.find( ( z ) => z.zoneName === zoneName );
5505
+ if ( !zone ) {
5506
+ zone = { zoneName, products: [] };
5507
+ fixture.productZones.push( zone );
5508
+ }
5509
+
5510
+ if ( !zone.products.some( ( p ) => p.productName === productName && p.isMerchandisingElement === isVM ) ) {
5511
+ zone.products.push( {
5512
+ productName,
5513
+ isMerchandisingElement: isVM,
5514
+ } );
5515
+ }
5516
+ }
5517
+
5518
+ storeItems.forEach( ( item ) => {
5519
+ const wall = item.Wall || 'Unknown';
5520
+ const locator = item['Store Fixture Locator'];
5521
+ const wallUpper = wall.toUpperCase();
5522
+ const isCenterWall = wallUpper.includes( 'CENTRE' );
5523
+ const fixtureKey = wall + '::' + locator;
5524
+
5525
+ if ( !fixtureGroupMap.has( fixtureKey ) ) {
5526
+ if ( isCenterWall ) {
5527
+ console.log();
5528
+ fixtureGroupMap.set( fixtureKey, {
5529
+ type: 'center',
5530
+ id: item['Store Fixture ID'],
5531
+ main: item['Fixture Type'],
5532
+ centerSubMain: item['Brand-Category'],
5533
+ fixtureLocator: locator,
5534
+ centerSuperSubMain: [
5535
+ {
5536
+ name: item['Brand-Category'],
5537
+ isVisualMerchandiser: false,
5538
+ },
5539
+ ],
5540
+ } );
5541
+ } else {
5542
+ fixtureGroupMap.set( fixtureKey, {
5543
+ type: 'wall',
5544
+ id: item['Store Fixture ID'],
5545
+ footer: 'Storage Box',
5546
+ header: item['Brand-Category'],
5547
+ fixtureName: `${locator} - ${item['Fixture Type']}`,
5548
+ fixtureType: item['Fixture Type'],
5549
+ productZones: [],
5550
+ fixtureSpacing: 0,
5551
+ fixtureSubname: [ item['Brand - Sub Category'] ],
5552
+ wall: wallUpper + ' WALL',
5553
+ } );
5554
+ }
5555
+ }
5556
+
5557
+ const fixture = fixtureGroupMap.get( fixtureKey );
5558
+
5559
+ if ( fixture.type === 'wall' ) {
5560
+ if ( item.VM ) addZoneProduct( fixture, item.Zone, item.VM, true );
5561
+ if ( item.Allocation ) addZoneProduct( fixture, item.Zone, item.Allocation, false );
5562
+ }
5563
+ } );
5564
+
5565
+ const structuredData = [];
5566
+
5567
+ for ( const fixture of fixtureGroupMap.values() ) {
5568
+ if ( fixture.type === 'center' ) {
5569
+ structuredData.push( {
5570
+ id: fixture.id,
5571
+ main: fixture.main,
5572
+ centerSubMain: fixture.centerSubMain,
5573
+ fixtureLocator: fixture.fixtureLocator,
5574
+ centerSuperSubMain: fixture.centerSuperSubMain,
5575
+ } );
5576
+ } else {
5577
+ const wallName = fixture.wall;
5578
+ if ( !wallGroups.has( wallName ) ) {
5579
+ wallGroups.set( wallName, {
5580
+ main: wallName,
5581
+ fixtures: [],
5582
+ } );
5583
+ }
5584
+ wallGroups.get( wallName ).fixtures.push( fixture );
5585
+ }
5586
+ }
5587
+
5588
+ result.push( {
5589
+ storeName,
5590
+ data: [
5591
+ ...Array.from( wallGroups.values() ),
5592
+ ...structuredData,
5593
+ ],
5594
+ } );
5595
+ }
5596
+
5597
+ return result;
5598
+ }
5599
+
5600
+
5601
+ const finalData = transformFixtureData( raw );
5602
+
5603
+
5604
+ const constantFixtureLength = 1220;
5605
+ const constantDetailedFixtureLength = 1220;
5606
+
5607
+ const constantFixtureWidth = 610;
5608
+ const constantDetailedFixtureWidth = 1524;
5609
+
5610
+ const mmToFeet = 305;
5611
+
5612
+ function roundToTwo( num ) {
5613
+ return Math.round( num * 100 ) / 100;
5614
+ }
5615
+
5616
+ for ( let i = 0; i < storeList.length; i++ ) {
5617
+ const storeData = finalData.find( ( store ) => store.storeName === storeList[i].toObject().storeName );
5618
+
5619
+ const storeDetails = storeList[i];
5620
+
5621
+ const existingPlanogram = await planoService.findOne( { storeName: storeData.storeName } );
5622
+
5623
+ if ( existingPlanogram ) {
5624
+ const checkTaskSubmitted = await planoTaskService.findOne( { planoId: existingPlanogram.toObject()._id } );
5625
+
5626
+ const checkTaskCreated = await processedTaskService.findOne( { storeName: storeData.storeName, date_iso: { $gte: new Date( ), $lte: new Date( ) }, isPlano: true } );
5627
+
5628
+ if ( checkTaskSubmitted || checkTaskCreated ) {
5629
+ continue;
5630
+ }
5631
+ }
5632
+
5633
+
5634
+ if ( existingPlanogram?.toObject()?._id && mongoose.Types.ObjectId.isValid( existingPlanogram?.toObject()?._id ) ) {
5635
+ await Promise.all( [ planoService.deleteOne( { _id: existingPlanogram?.toObject()?._id } ), storeBuilderService.deleteOne( { planoId: existingPlanogram?.toObject()?._id } ),
5636
+ storeFixtureService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ), fixtureShelfService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ),
5637
+ planoMappingService.deleteMany( { planoId: existingPlanogram?.toObject()?._id } ),
5638
+ ] );
5639
+ }
5640
+
5641
+
5642
+ const planoInsertData = {
5643
+ storeName: storeData.storeName,
5644
+ storeId: storeDetails?.toObject()?.storeId ? storeDetails.toObject().storeId : 'nil',
5645
+ layoutName: `${storeData.storeName} - Layout`,
5646
+ clientId: '11',
5647
+ attachments: [],
5648
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
5649
+ createdByName: 'Bejan',
5650
+ createdByEmail: 'bejan@tangotech.co.in',
5651
+ status: 'completed',
5652
+ floorNumber: 1,
5653
+ productResolutionLevel: 'L2',
5654
+ scanType: 'qr',
5655
+ };
5656
+
5657
+ const insertedPlano = await planoService.upsertOne( { storeName: storeData.storeName }, planoInsertData );
5658
+ const planoDoc = insertedPlano.toObject();
5659
+
5660
+ const leftWall = storeData.data.filter( ( entry ) => entry['main'] === 'LEFT WALL' );
5661
+ const leftFixtures = leftWall.flatMap( ( wall ) => wall.fixtures );
5662
+ const rightWall = storeData.data.filter( ( entry ) => entry['main'] === 'RIGHT WALL' );
5663
+ const rightFixtures = rightWall.flatMap( ( wall ) => wall.fixtures );
5664
+ const backWall = storeData.data.filter( ( entry ) => entry['main'] === 'BACK WALL' );
5665
+ const backFixtures = backWall.flatMap( ( wall ) => wall.fixtures );
5666
+ const frontWall = storeData.data.filter( ( entry ) => entry['main'] === 'FRONT WALL' );
5667
+ const frontFixtures = frontWall.flatMap( ( wall ) => wall.fixtures );
5668
+ const floorFixtures = storeData.data.filter( ( entry ) => entry['main'] === 'Euro Center' );
5669
+
5670
+
5671
+ const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
5672
+ const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
5673
+
5674
+ const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
5675
+ const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
5676
+
5677
+ const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
5678
+ const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
5679
+
5680
+ const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
5681
+ const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
5682
+
5683
+ const maxFixturesPerRow = floorFixtures.length/2;
5684
+ const totalRows = 2;
5685
+
5686
+ const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantFixtureLength / mmToFeet ) ) ) : 0;
5687
+ const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( ( floorFixtures.length/2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
5688
+
5689
+ const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( 2 * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
5690
+ const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( 2 * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
5691
+
5692
+ const frontXDistanceFeet = frontFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
5693
+ const frontXDetailedDistanceFeet = frontFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
5694
+
5695
+ const frontYDistanceFeet = frontFixtures.length ? roundToTwo( ( ( frontFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
5696
+ const frontYDetailedDistanceFeet = frontFixtures.length ? roundToTwo( ( ( frontFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
5697
+
5698
+ const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
5699
+ const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
5700
+
5701
+ const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
5702
+ const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
5703
+
5704
+ const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
5705
+ const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
5706
+
5707
+ const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet, frontYDistanceFeet );
5708
+ const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet, frontYDetailedDistanceFeet );
5709
+
5710
+ const finalXDistance = roundToTwo( ( maxXDistance < ( backXDistanceFeet + floorXDistanceFeet + frontXDistanceFeet )? ( ( backXDistanceFeet + floorXDistanceFeet + frontXDistanceFeet ) + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDistance + ( ( 2 * constantFixtureLength )/mmToFeet ) ) : maxXDistance ) );
5711
+ const finalXDetailedDistance = roundToTwo( ( maxXDetailedDistance < ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet + frontXDetailedDistanceFeet )? ( ( backXDetailedDistanceFeet + floorXDetailedDistanceFeet + frontXDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : ( floorFixtures.length && backFixtures.length ) ? ( maxXDetailedDistance + ( ( 2 * constantDetailedFixtureLength )/mmToFeet ) ) : maxXDetailedDistance ) );
5712
+
5713
+ const finalYDistance = roundToTwo( ( maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : ( maxYDistance + ( ( constantFixtureWidth )/mmToFeet ) ) ) );
5714
+ const finalYDetailedDistance = roundToTwo( ( maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : ( maxYDetailedDistance + ( ( constantDetailedFixtureWidth )/mmToFeet ) ) ) );
5715
+
5716
+
5717
+ const floorInsertData = {
5718
+ storeName: planoDoc.storeName,
5719
+ storeId: planoDoc.storeId,
5720
+ layoutName: `${planoDoc.storeName} - Layout`,
5721
+ clientId: '11',
5722
+ floorNumber: 1,
5723
+ floorName: 'floor 1',
5724
+ layoutPolygon: [
5725
+ {
5726
+ elementType: 'wall',
5727
+ distance: finalXDistance,
5728
+ unit: 'ft',
5729
+ direction: 'right',
5730
+ angle: 90,
5731
+ elementNumber: 1,
5732
+ detailedDistance: finalXDetailedDistance,
5733
+ },
5734
+ {
5735
+ elementType: 'wall',
5736
+ distance: finalYDistance,
5737
+ unit: 'ft',
5738
+ direction: 'down',
5739
+ angle: 90,
5740
+ elementNumber: 2,
5741
+ detailedDistance: finalYDetailedDistance,
5742
+ },
5743
+ {
5744
+ elementType: 'wall',
5745
+ distance: finalXDistance,
5746
+ unit: 'ft',
5747
+ direction: 'left',
5748
+ angle: 90,
5749
+ elementNumber: 3,
5750
+ detailedDistance: finalXDetailedDistance,
5751
+ },
5752
+ {
5753
+ elementType: 'wall',
5754
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
5755
+ unit: 'ft',
5756
+ direction: 'up',
5757
+ angle: 90,
5758
+ elementNumber: 4,
5759
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
5760
+ },
5761
+ {
5762
+ elementType: 'entrance',
5763
+ distance: roundToTwo( ( ( finalYDistance * 20 ) / 100 ) ),
5764
+ unit: 'ft',
5765
+ direction: 'up',
5766
+ angle: 90,
5767
+ elementNumber: 1,
5768
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 30 ) / 100 ) ),
5769
+ },
5770
+ {
5771
+ elementType: 'wall',
5772
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
5773
+ unit: 'ft',
5774
+ direction: 'up',
5775
+ angle: 90,
5776
+ elementNumber: 5,
5777
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
5778
+ },
5779
+ ],
5780
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
5781
+ createdByName: 'Bejan',
5782
+ createdByEmail: 'bejan@tangotech.co.in',
5783
+ status: 'completed',
5784
+ planoId: planoDoc._id,
5785
+ };
5786
+
5787
+ const layoutDoc = await storeBuilderService.upsertOne( { planoId: planoDoc._id }, floorInsertData );
5788
+
5789
+ let fixtureCounter = 1;
5790
+ const frontFixturePromises = frontFixtures.map( async ( fixture, index ) => {
5791
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
5792
+ if ( !fixtureConfig ) return;
5793
+ const fixtureConfigDoc = fixtureConfig.toObject();
5794
+
5795
+ const fixtureData = {
5796
+ 'clientId': layoutDoc.clientId,
5797
+ 'storeName': layoutDoc.storeName,
5798
+ 'storeId': layoutDoc.storeId,
5799
+ 'planoId': layoutDoc.planoId,
5800
+ 'floorId': layoutDoc._id,
5801
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
5802
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
5803
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
5804
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
5805
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
5806
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
5807
+ 'fixtureType': 'wall',
5808
+ 'fixtureHeight': {
5809
+ 'value': 0,
5810
+ 'unit': 'mm',
5811
+ },
5812
+ 'fixtureLength': {
5813
+ 'value': constantFixtureWidth,
5814
+ 'unit': 'mm',
5815
+ },
5816
+ 'fixtureWidth': {
5817
+ 'value': constantFixtureLength,
5818
+ 'unit': 'mm',
5819
+ },
5820
+ 'associatedElementType': 'wall',
5821
+ 'associatedElementNumber': 2,
5822
+ 'relativePosition': {
5823
+ 'x': 0,
5824
+ 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( frontFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
5825
+ 'unit': 'ft',
5826
+ },
5827
+ 'fixtureNumber': fixtureCounter,
5828
+ 'detailedFixtureLength': {
5829
+ 'value': constantDetailedFixtureLength,
5830
+ 'unit': 'mm',
5831
+ },
5832
+ 'detailedFixtureWidth': {
5833
+ 'value': constantDetailedFixtureWidth,
5834
+ 'unit': 'mm',
5835
+ },
5836
+ 'relativeDetailedPosition': {
5837
+ 'x': 0,
5838
+ 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( frontFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
5839
+ 'unit': 'ft',
5840
+ },
5841
+ 'productResolutionLevel': 'L2',
5842
+ 'associatedElementFixtureNumber': index+1,
5843
+ 'header': fixture.header,
5844
+ 'footer': fixture.footer,
5845
+ 'fixtureConfigId': fixtureConfigDoc._id,
5846
+ };
5847
+
5848
+ const createdFixture = await storeFixtureService.upsertOne(
5849
+ {
5850
+ floorId: layoutDoc._id,
5851
+ fixtureNumber: fixtureCounter++,
5852
+ },
5853
+ fixtureData,
5854
+ );
5855
+
5856
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
5857
+
5858
+ await Promise.all(
5859
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
5860
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
5861
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
5862
+
5863
+ const shelfData = {
5864
+ 'clientId': '11',
5865
+ 'storeName': layoutDoc.storeName,
5866
+ 'storeId': layoutDoc.storeId,
5867
+ 'planoId': layoutDoc.planoId,
5868
+ 'floorId': layoutDoc._id,
5869
+ 'fixtureId': createdFixture._id,
5870
+ 'shelfNumber': j + 1,
5871
+ 'shelfOrder': 'LTR',
5872
+ 'shelfCapacity': configShelf.shelfCapacity,
5873
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
5874
+ 'sectionZone': configShelf.shelfZone,
5875
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
5876
+ }; ;
5877
+
5878
+ await fixtureShelfService.upsertOne(
5879
+ {
5880
+ fixtureId: createdFixture._id,
5881
+ shelfNumber: j + 1,
5882
+ },
5883
+ shelfData,
5884
+ );
5885
+ } ),
5886
+ );
5887
+
5888
+ await Promise.all(
5889
+ fixture.productZones?.map( async ( zone ) => {
5890
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
5891
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
5892
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
5893
+
5894
+ await Promise.all(
5895
+ vms.map( async ( vm ) => {
5896
+ let configData = vmConfig[0];
5897
+
5898
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
5899
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
5900
+ }
5901
+
5902
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
5903
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
5904
+ }
5905
+
5906
+ if ( !configData ) return;
5907
+
5908
+ const insertData = {
5909
+ 'clientId': '11',
5910
+ 'productId': 'VMCR',
5911
+ 'type': 'vm',
5912
+ 'productName': vm.productName,
5913
+ 'productHeight': {
5914
+ 'value': configData.vmHeightmm,
5915
+ 'unit': 'mm',
5916
+ },
5917
+ 'productWidth': {
5918
+ 'value': configData.vmWidthmm,
5919
+ 'unit': 'mm',
5920
+ },
5921
+ 'startYPosition': configData.startShelf,
5922
+ 'endYPosition': configData.endShelf,
5923
+ 'xZone': configData.zone,
5924
+ 'fixtureConfigId': fixtureConfig._id,
5925
+ };
5926
+
5927
+ const vmTemplate = await planoProductService.upsertOne(
5928
+ {
5929
+ 'productName': vm.productName,
5930
+ 'fixtureConfigId': fixtureConfig._id,
5931
+ 'productHeight.value': configData.vmHeightmm,
5932
+ 'productWidth.value': configData.vmWidthmm,
5933
+ 'startYPosition': configData.startShelf,
5934
+ 'endYPosition': configData.endShelf,
5935
+ 'xZone': configData.zone,
5936
+ },
5937
+ insertData,
5938
+ );
5939
+
5940
+ const vmData = {
5941
+ 'clientId': layoutDoc.clientId,
5942
+ 'storeName': layoutDoc.storeName,
5943
+ 'storeId': layoutDoc.storeId,
5944
+ 'planoId': layoutDoc.planoId,
5945
+ 'floorId': layoutDoc._id,
5946
+ 'type': 'vm',
5947
+ 'fixtureId': createdFixture._id,
5948
+ 'productId': vmTemplate._id,
5949
+ };
5950
+
5951
+ await planoMappingService.upsertOne(
5952
+ {
5953
+ fixtureId: createdFixture._id,
5954
+ productId: vmTemplate._id,
5955
+ },
5956
+ vmData,
5957
+ );
5958
+ } ),
5959
+ );
5960
+ } ) || [],
5961
+ );
5962
+ } );
5963
+
5964
+ const leftFixturePromises = leftFixtures.map( async ( fixture, index ) => {
5965
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
5966
+ if ( !fixtureConfig ) return;
5967
+ const fixtureConfigDoc = fixtureConfig.toObject();
5968
+
5969
+ const fixtureData = {
5970
+ 'clientId': layoutDoc.clientId,
5971
+ 'storeName': layoutDoc.storeName,
5972
+ 'storeId': layoutDoc.storeId,
5973
+ 'planoId': layoutDoc.planoId,
5974
+ 'floorId': layoutDoc._id,
5975
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
5976
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
5977
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
5978
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
5979
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
5980
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
5981
+ 'fixtureType': 'wall',
5982
+ 'fixtureHeight': {
5983
+ 'value': 0,
5984
+ 'unit': 'mm',
5985
+ },
5986
+ 'fixtureLength': {
5987
+ 'value': constantFixtureLength,
5988
+ 'unit': 'mm',
5989
+ },
5990
+ 'fixtureWidth': {
5991
+ 'value': constantFixtureWidth,
5992
+ 'unit': 'mm',
5993
+ },
5994
+ 'associatedElementType': 'wall',
5995
+ 'associatedElementNumber': 1,
5996
+ 'relativePosition': {
5997
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
5998
+ 'y': 0,
5999
+ 'unit': 'ft',
6000
+ },
6001
+ 'fixtureNumber': fixtureCounter,
6002
+ 'detailedFixtureLength': {
6003
+ 'value': constantDetailedFixtureLength,
6004
+ 'unit': 'mm',
6005
+ },
6006
+ 'detailedFixtureWidth': {
6007
+ 'value': constantDetailedFixtureWidth,
6008
+ 'unit': 'mm',
6009
+ },
6010
+ 'relativeDetailedPosition': {
6011
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
6012
+ 'y': 0,
6013
+ 'unit': 'ft',
6014
+ },
6015
+ 'productResolutionLevel': 'L2',
6016
+ 'associatedElementFixtureNumber': index+1,
6017
+ 'header': fixture.header,
6018
+ 'footer': fixture.footer,
6019
+ 'fixtureConfigId': fixtureConfigDoc._id,
6020
+ };
6021
+
6022
+ const createdFixture = await storeFixtureService.upsertOne(
6023
+ {
6024
+ floorId: layoutDoc._id,
6025
+ fixtureNumber: fixtureCounter++,
6026
+ },
6027
+ fixtureData,
6028
+ );
6029
+
6030
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
6031
+
6032
+ await Promise.all(
6033
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
6034
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
6035
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
6036
+
6037
+ const shelfData = {
6038
+ 'clientId': '11',
6039
+ 'storeName': layoutDoc.storeName,
6040
+ 'storeId': layoutDoc.storeId,
6041
+ 'planoId': layoutDoc.planoId,
6042
+ 'floorId': layoutDoc._id,
6043
+ 'fixtureId': createdFixture._id,
6044
+ 'shelfNumber': j + 1,
6045
+ 'shelfOrder': 'LTR',
6046
+ 'shelfCapacity': configShelf.shelfCapacity,
6047
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
6048
+ 'sectionZone': configShelf.shelfZone,
6049
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
6050
+ };
6051
+
6052
+ await fixtureShelfService.upsertOne(
6053
+ {
6054
+ fixtureId: createdFixture._id,
6055
+ shelfNumber: j + 1,
6056
+ },
6057
+ shelfData,
6058
+ );
6059
+ } ),
6060
+ );
6061
+
6062
+ await Promise.all(
6063
+ fixture.productZones?.map( async ( zone ) => {
6064
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
6065
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
6066
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
6067
+
6068
+ await Promise.all(
6069
+ vms.map( async ( vm ) => {
6070
+ let configData = vmConfig[0];
6071
+
6072
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
6073
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
6074
+ }
6075
+
6076
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
6077
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
6078
+ }
6079
+
6080
+ if ( !configData ) return;
6081
+
6082
+ const insertData = {
6083
+ 'clientId': '11',
6084
+ 'productId': 'VMCR',
6085
+ 'type': 'vm',
6086
+ 'productName': vm.productName,
6087
+ 'productHeight': {
6088
+ 'value': configData.vmHeightmm,
6089
+ 'unit': 'mm',
6090
+ },
6091
+ 'productWidth': {
6092
+ 'value': configData.vmWidthmm,
6093
+ 'unit': 'mm',
6094
+ },
6095
+ 'startYPosition': configData.startShelf,
6096
+ 'endYPosition': configData.endShelf,
6097
+ 'xZone': configData.zone,
6098
+ 'fixtureConfigId': fixtureConfig._id,
6099
+ };
6100
+
6101
+ const vmTemplate = await planoProductService.upsertOne(
6102
+ {
6103
+ 'productName': vm.productName,
6104
+ 'fixtureConfigId': fixtureConfig._id,
6105
+ 'productHeight.value': configData.vmHeightmm,
6106
+ 'productWidth.value': configData.vmWidthmm,
6107
+ 'startYPosition': configData.startShelf,
6108
+ 'endYPosition': configData.endShelf,
6109
+ 'xZone': configData.zone,
6110
+ },
6111
+ insertData,
6112
+ );
6113
+
6114
+ const vmData = {
6115
+ 'clientId': layoutDoc.clientId,
6116
+ 'storeName': layoutDoc.storeName,
6117
+ 'storeId': layoutDoc.storeId,
6118
+ 'planoId': layoutDoc.planoId,
6119
+ 'floorId': layoutDoc._id,
6120
+ 'type': 'vm',
6121
+ 'fixtureId': createdFixture._id,
6122
+ 'productId': vmTemplate._id,
6123
+ };
6124
+
6125
+ await planoMappingService.upsertOne(
6126
+ {
6127
+ fixtureId: createdFixture._id,
6128
+ productId: vmTemplate._id,
6129
+ },
6130
+ vmData,
6131
+ );
6132
+ } ),
6133
+ );
6134
+ } ) || [],
6135
+ );
6136
+ } );
6137
+
6138
+ const backFixturePromises = backFixtures.map( async ( fixture, index ) => {
6139
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
6140
+ if ( !fixtureConfig ) return;
6141
+ const fixtureConfigDoc = fixtureConfig.toObject();
6142
+
6143
+ const fixtureData = {
6144
+ 'clientId': layoutDoc.clientId,
6145
+ 'storeName': layoutDoc.storeName,
6146
+ 'storeId': layoutDoc.storeId,
6147
+ 'planoId': layoutDoc.planoId,
6148
+ 'floorId': layoutDoc._id,
6149
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
6150
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
6151
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
6152
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
6153
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
6154
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
6155
+ 'fixtureType': 'wall',
6156
+ 'fixtureHeight': {
6157
+ 'value': 0,
6158
+ 'unit': 'mm',
6159
+ },
6160
+ 'fixtureLength': {
6161
+ 'value': constantFixtureWidth,
6162
+ 'unit': 'mm',
6163
+ },
6164
+ 'fixtureWidth': {
6165
+ 'value': constantFixtureLength,
6166
+ 'unit': 'mm',
6167
+ },
6168
+ 'associatedElementType': 'wall',
6169
+ 'associatedElementNumber': 2,
6170
+ 'relativePosition': {
6171
+ 'x': roundToTwo( ( finalXDistance - ( constantFixtureWidth/mmToFeet ) ) ),
6172
+ 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
6173
+ 'unit': 'ft',
6174
+ },
6175
+ 'fixtureNumber': fixtureCounter,
6176
+ 'detailedFixtureLength': {
6177
+ 'value': constantDetailedFixtureLength,
6178
+ 'unit': 'mm',
6179
+ },
6180
+ 'detailedFixtureWidth': {
6181
+ 'value': constantDetailedFixtureWidth,
6182
+ 'unit': 'mm',
6183
+ },
6184
+ 'relativeDetailedPosition': {
6185
+ 'x': roundToTwo( ( finalXDetailedDistance - ( constantDetailedFixtureLength/mmToFeet ) ) ),
6186
+ 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
6187
+ 'unit': 'ft',
6188
+ },
6189
+ 'productResolutionLevel': 'L2',
6190
+ 'associatedElementFixtureNumber': index+1,
6191
+ 'header': fixture.header,
6192
+ 'footer': fixture.footer,
6193
+ 'fixtureConfigId': fixtureConfigDoc._id,
6194
+ };
6195
+
6196
+ const createdFixture = await storeFixtureService.upsertOne(
6197
+ {
6198
+ floorId: layoutDoc._id,
6199
+ fixtureNumber: fixtureCounter++,
6200
+ },
6201
+ fixtureData,
6202
+ );
6203
+
6204
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
6205
+
6206
+ await Promise.all(
6207
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
6208
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
6209
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
6210
+
6211
+ const shelfData = {
6212
+ 'clientId': '11',
6213
+ 'storeName': layoutDoc.storeName,
6214
+ 'storeId': layoutDoc.storeId,
6215
+ 'planoId': layoutDoc.planoId,
6216
+ 'floorId': layoutDoc._id,
6217
+ 'fixtureId': createdFixture._id,
6218
+ 'shelfNumber': j + 1,
6219
+ 'shelfOrder': 'LTR',
6220
+ 'shelfCapacity': configShelf.shelfCapacity,
6221
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
6222
+ 'sectionZone': configShelf.shelfZone,
6223
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
6224
+ }; ;
6225
+
6226
+ await fixtureShelfService.upsertOne(
6227
+ {
6228
+ fixtureId: createdFixture._id,
6229
+ shelfNumber: j + 1,
6230
+ },
6231
+ shelfData,
6232
+ );
6233
+ } ),
6234
+ );
6235
+
6236
+ await Promise.all(
6237
+ fixture.productZones?.map( async ( zone ) => {
6238
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
6239
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
6240
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
6241
+
6242
+ await Promise.all(
6243
+ vms.map( async ( vm ) => {
6244
+ let configData = vmConfig[0];
6245
+
6246
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
6247
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
6248
+ }
6249
+
6250
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
6251
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
6252
+ }
6253
+
6254
+ if ( !configData ) return;
6255
+
6256
+ const insertData = {
6257
+ 'clientId': '11',
6258
+ 'productId': 'VMCR',
6259
+ 'type': 'vm',
6260
+ 'productName': vm.productName,
6261
+ 'productHeight': {
6262
+ 'value': configData.vmHeightmm,
6263
+ 'unit': 'mm',
6264
+ },
6265
+ 'productWidth': {
6266
+ 'value': configData.vmWidthmm,
6267
+ 'unit': 'mm',
6268
+ },
6269
+ 'startYPosition': configData.startShelf,
6270
+ 'endYPosition': configData.endShelf,
6271
+ 'xZone': configData.zone,
6272
+ 'fixtureConfigId': fixtureConfig._id,
6273
+ };
6274
+
6275
+ const vmTemplate = await planoProductService.upsertOne(
6276
+ {
6277
+ 'productName': vm.productName,
6278
+ 'fixtureConfigId': fixtureConfig._id,
6279
+ 'productHeight.value': configData.vmHeightmm,
6280
+ 'productWidth.value': configData.vmWidthmm,
6281
+ 'startYPosition': configData.startShelf,
6282
+ 'endYPosition': configData.endShelf,
6283
+ 'xZone': configData.zone,
6284
+ },
6285
+ insertData,
6286
+ );
6287
+
6288
+ const vmData = {
6289
+ 'clientId': layoutDoc.clientId,
6290
+ 'storeName': layoutDoc.storeName,
6291
+ 'storeId': layoutDoc.storeId,
6292
+ 'planoId': layoutDoc.planoId,
6293
+ 'floorId': layoutDoc._id,
6294
+ 'type': 'vm',
6295
+ 'fixtureId': createdFixture._id,
6296
+ 'productId': vmTemplate._id,
6297
+ };
6298
+
6299
+ await planoMappingService.upsertOne(
6300
+ {
6301
+ fixtureId: createdFixture._id,
6302
+ productId: vmTemplate._id,
6303
+ },
6304
+ vmData,
6305
+ );
6306
+ } ),
6307
+ );
6308
+ } ) || [],
6309
+ );
6310
+ } );
6311
+
6312
+ const rightFixturePromises = rightFixtures.map( async ( fixture, index ) => {
6313
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.fixtureType } );
6314
+ if ( !fixtureConfig ) return;
6315
+ const fixtureConfigDoc = fixtureConfig.toObject();
6316
+
6317
+ const fixtureData = {
6318
+ 'clientId': layoutDoc.clientId,
6319
+ 'storeName': layoutDoc.storeName,
6320
+ 'storeId': layoutDoc.storeId,
6321
+ 'planoId': layoutDoc.planoId,
6322
+ 'floorId': layoutDoc._id,
6323
+ 'fixtureName': `Fixture ${index+1} - ${fixture.fixtureType}`,
6324
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
6325
+ 'fixtureBrandCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
6326
+ 'fixtureBrandSubCategory': fixture.fixtureSubname.length ? ( fixture.fixtureSubname.length > 1 ? fixture.fixtureSubname.join( ' + ' ) : fixture.fixtureSubname[0] ) : undefined,
6327
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
6328
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
6329
+ 'fixtureType': 'wall',
6330
+ 'fixtureHeight': {
6331
+ 'value': 0,
6332
+ 'unit': 'mm',
6333
+ },
6334
+ 'fixtureLength': {
6335
+ 'value': constantFixtureLength,
6336
+ 'unit': 'mm',
6337
+ },
6338
+ 'fixtureWidth': {
6339
+ 'value': constantFixtureWidth,
6340
+ 'unit': 'mm',
6341
+ },
6342
+ 'associatedElementType': 'wall',
6343
+ 'associatedElementNumber': 3,
6344
+ 'relativePosition': {
6345
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
6346
+ 'y': roundToTwo( ( finalYDistance - ( constantFixtureWidth / mmToFeet ) ) ),
6347
+ 'unit': 'ft',
6348
+ },
6349
+ 'fixtureNumber': fixtureCounter,
6350
+ 'detailedFixtureLength': {
6351
+ 'value': constantDetailedFixtureLength,
6352
+ 'unit': 'mm',
6353
+ },
6354
+ 'detailedFixtureWidth': {
6355
+ 'value': constantDetailedFixtureWidth,
6356
+ 'unit': 'mm',
6357
+ },
6358
+ 'relativeDetailedPosition': {
6359
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
6360
+ 'y': roundToTwo( ( finalYDetailedDistance - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
6361
+ 'unit': 'ft',
6362
+ },
6363
+ 'productResolutionLevel': 'L2',
6364
+ 'associatedElementFixtureNumber': index+1,
6365
+ 'header': fixture.header,
6366
+ 'footer': fixture.footer,
6367
+ 'fixtureConfigId': fixtureConfigDoc._id,
6368
+ };
6369
+
6370
+ const createdFixture = await storeFixtureService.upsertOne(
6371
+ {
6372
+ floorId: layoutDoc._id,
6373
+ fixtureNumber: fixtureCounter++,
6374
+ },
6375
+ fixtureData,
6376
+ );
6377
+
6378
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
6379
+
6380
+ await Promise.all(
6381
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
6382
+ const shelfZone = fixture.productZones.find( ( zone ) => zone.zoneName === configShelf.shelfZone );
6383
+ const shelfSection = shelfZone.products.find( ( product ) => !product.isMerchandisingElement );
6384
+
6385
+ const shelfData = {
6386
+ 'clientId': '11',
6387
+ 'storeName': layoutDoc.storeName,
6388
+ 'storeId': layoutDoc.storeId,
6389
+ 'planoId': layoutDoc.planoId,
6390
+ 'floorId': layoutDoc._id,
6391
+ 'fixtureId': createdFixture._id,
6392
+ 'shelfNumber': j + 1,
6393
+ 'shelfOrder': 'LTR',
6394
+ 'shelfCapacity': configShelf.shelfCapacity,
6395
+ 'sectionName': shelfSection?.productName ? shelfSection.productName : 'Unknown',
6396
+ 'sectionZone': configShelf.shelfZone,
6397
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
6398
+ };
6399
+
6400
+ await fixtureShelfService.upsertOne(
6401
+ {
6402
+ fixtureId: createdFixture._id,
6403
+ shelfNumber: j + 1,
6404
+ },
6405
+ shelfData,
6406
+ );
6407
+ } ),
6408
+ );
6409
+
6410
+ await Promise.all(
6411
+ fixture.productZones?.map( async ( zone ) => {
6412
+ const vms = zone.products.filter( ( vm ) => vm.isMerchandisingElement );
6413
+ const vmConfig = fixtureConfigDoc.vmConfig.filter( ( vm ) => vm.position === zone.zoneName );
6414
+ const pids = zone.products.filter( ( vm ) => !vm.isMerchandisingElement );
6415
+
6416
+ await Promise.all(
6417
+ vms.map( async ( vm ) => {
6418
+ let configData = vmConfig[0];
6419
+
6420
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' ) {
6421
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 905 );
6422
+ }
6423
+
6424
+ if ( vm.productName === 'Creatr' && zone.zoneName === 'Mid' && pids.length ) {
6425
+ configData = vmConfig.find( ( config ) => config.vmWidthmm === 230 );
6426
+ }
6427
+
6428
+ if ( !configData ) return;
6429
+
6430
+ const insertData = {
6431
+ 'clientId': '11',
6432
+ 'productId': 'VMCR',
6433
+ 'type': 'vm',
6434
+ 'productName': vm.productName,
6435
+ 'productHeight': {
6436
+ 'value': configData.vmHeightmm,
6437
+ 'unit': 'mm',
6438
+ },
6439
+ 'productWidth': {
6440
+ 'value': configData.vmWidthmm,
6441
+ 'unit': 'mm',
6442
+ },
6443
+ 'startYPosition': configData.startShelf,
6444
+ 'endYPosition': configData.endShelf,
6445
+ 'xZone': configData.zone,
6446
+ 'fixtureConfigId': fixtureConfig._id,
6447
+ };
6448
+
6449
+ const vmTemplate = await planoProductService.upsertOne(
6450
+ {
6451
+ 'productName': vm.productName,
6452
+ 'fixtureConfigId': fixtureConfig._id,
6453
+ 'productHeight.value': configData.vmHeightmm,
6454
+ 'productWidth.value': configData.vmWidthmm,
6455
+ 'startYPosition': configData.startShelf,
6456
+ 'endYPosition': configData.endShelf,
6457
+ 'xZone': configData.zone,
6458
+ },
6459
+ insertData,
6460
+ );
6461
+
6462
+ const vmData = {
6463
+ 'clientId': layoutDoc.clientId,
6464
+ 'storeName': layoutDoc.storeName,
6465
+ 'storeId': layoutDoc.storeId,
6466
+ 'planoId': layoutDoc.planoId,
6467
+ 'floorId': layoutDoc._id,
6468
+ 'type': 'vm',
6469
+ 'fixtureId': createdFixture._id,
6470
+ 'productId': vmTemplate._id,
6471
+ };
6472
+
6473
+ await planoMappingService.upsertOne(
6474
+ {
6475
+ fixtureId: createdFixture._id,
6476
+ productId: vmTemplate._id,
6477
+ },
6478
+ vmData,
6479
+ );
6480
+ } ),
6481
+ );
6482
+ } ) || [],
6483
+ );
6484
+ } );
6485
+
6486
+ const floorFixturePromises = floorFixtures.map( async ( fixture, index ) => {
6487
+ const centerRow = Math.floor( totalRows / 2 );
6488
+
6489
+ const startingX = roundToTwo( ( finalXDistance / 2 - ( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) ) );
6490
+ const detailedStartingX = roundToTwo( ( finalXDetailedDistance / 2 - ( maxFixturesPerRow / 2 ) * ( constantDetailedFixtureLength / mmToFeet ) ) );
6491
+
6492
+ const startingY = finalYDistance / 2 - centerRow * ( constantFixtureWidth / mmToFeet );
6493
+ const detailedStartingY = finalYDetailedDistance / 2 - centerRow * ( constantDetailedFixtureWidth / mmToFeet );
6494
+
6495
+ const colIndex = Math.floor( index / 2 );
6496
+ const rowIndex = index % 2 === 0 ? 1 : 0;
6497
+
6498
+ const xPos = roundToTwo( startingX + colIndex * ( constantFixtureLength / mmToFeet ) );
6499
+ const yPos = roundToTwo( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) );
6500
+
6501
+ const detailedXPos = roundToTwo( ( detailedStartingX + colIndex * ( constantDetailedFixtureLength / mmToFeet ) ) );
6502
+ const detailedYPos = roundToTwo( ( detailedStartingY + rowIndex * ( constantDetailedFixtureWidth / mmToFeet ) ) );
6503
+
6504
+ console.log( fixture );
6505
+
6506
+ console.log( fixture.main );
6507
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCategory: fixture.main } );
6508
+ if ( !fixtureConfig ) return;
6509
+
6510
+ const fixtureConfigDoc = fixtureConfig.toObject();
6511
+
6512
+ console.log( fixtureConfigDoc );
6513
+
6514
+ const fixtureData = {
6515
+ 'clientId': layoutDoc.clientId,
6516
+ 'storeName': layoutDoc.storeName,
6517
+ 'storeId': layoutDoc.storeId,
6518
+ 'planoId': layoutDoc.planoId,
6519
+ 'floorId': layoutDoc._id,
6520
+ 'fixtureName': `Fixture ${index+1} - ${fixture.main}`,
6521
+ 'fixtureCategory': fixtureConfigDoc.fixtureConfigType,
6522
+ 'fixtureBrandCategory': fixture.centerSubMain ? fixture.centerSubMain : undefined,
6523
+ 'fixtureBrandSubCategory': fixture.centerSubMain ? fixture.centerSubMain : undefined,
6524
+ 'fixtureCode': fixtureConfigDoc?.fixtureCode,
6525
+ 'fixtureCapacity': fixtureConfigDoc?.fixtureCapacity,
6526
+ 'fixtureType': 'floor',
6527
+ 'fixtureHeight': {
6528
+ 'value': 0,
6529
+ 'unit': 'mm',
6530
+ },
6531
+ 'fixtureLength': {
6532
+ 'value': constantFixtureLength,
6533
+ 'unit': 'mm',
6534
+ },
6535
+ 'fixtureWidth': {
6536
+ 'value': constantFixtureWidth,
6537
+ 'unit': 'mm',
6538
+ },
6539
+ 'relativePosition': {
6540
+ 'x': xPos,
6541
+ 'y': yPos,
6542
+ 'unit': 'ft',
6543
+ },
6544
+ 'fixtureNumber': fixtureCounter,
6545
+ 'detailedFixtureLength': {
6546
+ 'value': constantDetailedFixtureLength,
6547
+ 'unit': 'mm',
6548
+ },
6549
+ 'detailedFixtureWidth': {
6550
+ 'value': constantDetailedFixtureWidth,
6551
+ 'unit': 'mm',
6552
+ },
6553
+ 'relativeDetailedPosition': {
6554
+ 'x': detailedXPos,
6555
+ 'y': detailedYPos,
6556
+ 'unit': 'ft',
6557
+ },
6558
+ 'productResolutionLevel': 'L2',
6559
+ 'associatedElementFixtureNumber': index+1,
6560
+ 'fixtureConfigId': fixtureConfigDoc._id,
6561
+ };
6562
+
6563
+ console.log( fixtureData );
6564
+
6565
+
6566
+ const createdFixture = await storeFixtureService.upsertOne(
6567
+ {
6568
+ floorId: layoutDoc._id,
6569
+ fixtureNumber: fixtureCounter++,
6570
+ },
6571
+ fixtureData,
6572
+ );
6573
+
6574
+ if ( !fixtureConfigDoc.shelfConfig.length || fixture.header === 'CL' || fixture.fixtureSubname?.includes( 'CL' ) ) return;
6575
+
6576
+ await Promise.all(
6577
+ fixtureConfigDoc.shelfConfig.map( async ( configShelf, j ) => {
6578
+ const shelfSection = fixture.centerSuperSubMain.find(
6579
+ ( product ) => product.isVisualMerchandiser === false || product.isVisualMerchandiser === true,
6580
+ );
6581
+
6582
+ const shelfData = {
6583
+ 'clientId': '11',
6584
+ 'storeName': layoutDoc.storeName,
6585
+ 'storeId': layoutDoc.storeId,
6586
+ 'planoId': layoutDoc.planoId,
6587
+ 'floorId': layoutDoc._id,
6588
+ 'fixtureId': createdFixture._id,
6589
+ 'shelfNumber': j + 1,
6590
+ 'shelfOrder': 'LTR',
6591
+ 'shelfCapacity': configShelf.shelfCapacity,
6592
+ 'sectionName': fixture.centerSuperSubMain.find( ( product ) => product.isVisualMerchandiser === true ) ? shelfSection?.name + ' PIDs' : shelfSection?.name,
6593
+ 'shelfSplitup': configShelf?.shelfSplitup ? configShelf.shelfSplitup : 0,
6594
+ };
6595
+
6596
+ await fixtureShelfService.upsertOne(
6597
+ {
6598
+ fixtureId: createdFixture._id,
6599
+ shelfNumber: j + 1,
6600
+ },
6601
+ shelfData,
6602
+ );
6603
+ } ),
6604
+ );
6605
+
6606
+ const vm = fixture.centerSuperSubMain.find( ( vm ) => vm.isVisualMerchandiser );
6607
+ const vmConfig = fixtureConfigDoc.vmConfig;
6608
+
6609
+ if ( vm ) {
6610
+ const [ configData1, configData2 ] = [ vmConfig[0], vmConfig[1] ];
6611
+
6612
+ const insertData1 = {
6613
+ 'clientId': '11',
6614
+ 'productId': 'VMCR',
6615
+ 'type': 'vm',
6616
+ 'productName': vm.name,
6617
+ 'productHeight': {
6618
+ 'value': configData1.vmHeightmm,
6619
+ 'unit': 'mm',
6620
+ },
6621
+ 'productWidth': {
6622
+ 'value': configData1.vmWidthmm,
6623
+ 'unit': 'mm',
6624
+ },
6625
+ 'startYPosition': configData1.startShelf,
6626
+ 'endYPosition': configData1.endShelf,
6627
+ 'xZone': configData1.zone,
6628
+ 'fixtureConfigId': fixtureConfig._id,
6629
+ };
6630
+ const insertData2 = {
6631
+ 'clientId': '11',
6632
+ 'productId': 'VMCR',
6633
+ 'type': 'vm',
6634
+ 'productName': ' ',
6635
+ 'productHeight': {
6636
+ 'value': configData2.vmHeightmm,
6637
+ 'unit': 'mm',
6638
+ },
6639
+ 'productWidth': {
6640
+ 'value': configData2.vmWidthmm,
6641
+ 'unit': 'mm',
6642
+ },
6643
+ 'startYPosition': configData2.startShelf,
6644
+ 'endYPosition': configData2.endShelf,
6645
+ 'xZone': configData2.zone,
6646
+ 'fixtureConfigId': fixtureConfig._id,
6647
+ };
6648
+
6649
+ const [ vmTemplate1, vmTemplate2 ] = await Promise.all( [
6650
+ planoProductService.upsertOne(
6651
+ {
6652
+ 'productName': vm.name,
6653
+ 'fixtureConfigId': fixtureConfig._id,
6654
+ 'productHeight.value': configData1.vmHeightmm,
6655
+ 'productWidth.value': configData1.vmWidthmm,
6656
+ 'startYPosition': configData1.startShelf,
6657
+ 'endYPosition': configData1.endShelf,
6658
+ 'xZone': configData1.zone,
6659
+ },
6660
+ insertData1,
6661
+ ),
6662
+ planoProductService.upsertOne(
6663
+ {
6664
+ 'productName': ' ',
6665
+ 'fixtureConfigId': fixtureConfig._id,
6666
+ 'productHeight.value': configData2.vmHeightmm,
6667
+ 'productWidth.value': configData2.vmWidthmm,
6668
+ 'startYPosition': configData2.startShelf,
6669
+ 'endYPosition': configData2.endShelf,
6670
+ 'xZone': configData2.zone,
6671
+ },
6672
+ insertData2,
6673
+ ),
6674
+ ] );
6675
+
6676
+ const vmData1 = {
6677
+ 'clientId': layoutDoc.clientId,
6678
+ 'storeName': layoutDoc.storeName,
6679
+ 'storeId': layoutDoc.storeId,
6680
+ 'planoId': layoutDoc.planoId,
6681
+ 'floorId': layoutDoc._id,
6682
+ 'type': 'vm',
6683
+ 'fixtureId': createdFixture._id,
6684
+ 'productId': vmTemplate1._id,
6685
+ };
6686
+ const vmData2 = {
6687
+ 'clientId': layoutDoc.clientId,
6688
+ 'storeName': layoutDoc.storeName,
6689
+ 'storeId': layoutDoc.storeId,
6690
+ 'planoId': layoutDoc.planoId,
6691
+ 'floorId': layoutDoc._id,
6692
+ 'type': 'vm',
6693
+ 'fixtureId': createdFixture._id,
6694
+ 'productId': vmTemplate2._id,
6695
+ };
6696
+
6697
+ await Promise.all( [
6698
+ planoMappingService.upsertOne(
6699
+ {
6700
+ fixtureId: createdFixture._id,
6701
+ productId: vmTemplate1._id,
6702
+ },
6703
+ vmData1,
6704
+ ),
6705
+ planoMappingService.upsertOne(
6706
+ {
6707
+ fixtureId: createdFixture._id,
6708
+ productId: vmTemplate2._id,
6709
+ },
6710
+ vmData2,
6711
+ ),
6712
+ ] );
6713
+ }
6714
+ } );
6715
+
6716
+ await Promise.all( [ ...frontFixturePromises, ...leftFixturePromises, ...backFixturePromises, ...rightFixturePromises, ...floorFixturePromises ] );
6717
+
6718
+ const now = Date.now();
6719
+ const elapsedMinutes = ( now - startTime ) / 1000 / 60;
6720
+ console.log( `Store name: ${storeData.storeName}, Iteration ${i + 1}: total elapsed time = ${elapsedMinutes.toFixed( 2 )} minutes` );
6721
+ }
6722
+
6723
+ res.sendSuccess( finalData );
6724
+ } catch ( e ) {
6725
+ logger.error( { functionName: 'updateCrestPlanogram', error: e } );
6726
+ return res.sendError( e.message || 'Internal Server Error', 500 );
6727
+ }
6728
+ }