tango-app-api-store-builder 1.0.0-beta-41 → 1.0.0-beta-43

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.
@@ -0,0 +1,1610 @@
1
+ // import { writeFileSync } from 'fs';
2
+ import xlsx from 'xlsx';
3
+ import { logger } from 'tango-app-api-middleware';
4
+ import * as storeBuilderService from '../service/storeBuilder.service.js';
5
+ import * as storeService from '../service/store.service.js';
6
+ import * as planoService from '../service/planogram.service.js';
7
+ import * as storeFixtureService from '../service/storeFixture.service.js';
8
+ import * as fixtureShelfService from '../service/fixtureShelf.service.js';
9
+ import * as planoProductService from '../service/planoProduct.service.js';
10
+ import * as planoMappingService from '../service/planoMapping.service.js';
11
+ // import * as planoComplianceService from '../service/planoCompliance.service.js';
12
+ // import * as planoTaskComplianceService from '../service/planoTask.service.js';
13
+ // import * as planoQrConversionRequestService from '../service/planoQrConversionRequest.service.js';
14
+ import * as fixtureConfigService from '../service/fixtureConfig.service.js';
15
+ import mongoose from 'mongoose';
16
+
17
+
18
+ export async function getStoreNames( req, res ) {
19
+ try {
20
+ if ( !req.files.file ) {
21
+ return res.sendError( 'Excel file is required', 400 );
22
+ }
23
+
24
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
25
+ const sheetName = 'Layout,Fixture&VM Mapping (1705';
26
+ const raw = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
27
+
28
+ const storeNames = new Set();
29
+ raw.forEach( ( item ) => {
30
+ storeNames.add( item?.['Store ID'] );
31
+ } );
32
+
33
+ const storeNamesArray = Array.from( storeNames );
34
+ return res.sendSuccess( { storeNames: storeNamesArray, length: storeNamesArray.length } );
35
+ } catch ( e ) {
36
+ logger.error( { functionName: 'getStoreNames', error: e } );
37
+ return res.sendError( e.message || 'Internal Server Error', 500 );
38
+ }
39
+ }
40
+
41
+ export async function createFixtureConfig( req, res ) {
42
+ try {
43
+ if ( !req.files.file ) {
44
+ return res.sendError( 'Invalid or missing Excel file', 400 );
45
+ }
46
+
47
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
48
+ const sheetName = 'Fixture Library';
49
+ if ( !workbook.Sheets[sheetName] ) {
50
+ return res.sendError( `Sheet "${sheetName}" not found`, 400 );
51
+ }
52
+
53
+ const inputArray = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
54
+
55
+ const transformedData = inputArray.map( ( item ) => {
56
+ const sectionDetailsRaw = item['Section details'];
57
+ const proportionRaw = item['Proportion'];
58
+
59
+ const availableSections = typeof sectionDetailsRaw === 'string' ?
60
+ sectionDetailsRaw.replace( /[{}]/g, '' ).split( ', ' ).map( ( s ) => s.trim() ) :
61
+ [];
62
+
63
+ const proportions = typeof proportionRaw === 'string' ?
64
+ proportionRaw.replace( /[{}%]/g, '' ).split( ', ' ).map( ( num ) => parseInt( num.trim(), 10 ) ) :
65
+ [];
66
+
67
+ const sectionNames = [ 'Top', 'Mid', 'Bottom' ];
68
+ const sectionKeys = [ 'Top_Section', 'Middle_Section', 'Bottom_Section' ];
69
+
70
+ const sections = availableSections.map( ( section, index ) => {
71
+ const sectionIndex = sectionNames.indexOf( section );
72
+ return {
73
+ sectionId: section,
74
+ sectionName: sectionKeys[sectionIndex],
75
+ sectionShelves: item[sectionKeys[sectionIndex]],
76
+ proportion: proportions[index] !== undefined ? proportions[index] : null,
77
+ };
78
+ } );
79
+
80
+
81
+ return {
82
+ clientId: '11',
83
+ fixtureCategory: item['Fixture Category'],
84
+ fixtureLength: {
85
+ value: typeof item['Fixture Length(ft)'] === 'number' ? item['Fixture Length(ft)'] : 0,
86
+ unit: 'ft',
87
+ },
88
+ shelfCount: typeof item['No. of Shelves'] === 'number' ? item['No. of Shelves'] : undefined,
89
+ productPerShelf: typeof item['No. of Spots on Shelf'] === 'number' ? item['No. of Spots on Shelf'] : undefined,
90
+ fixtureCapacity: typeof item['Capacity on Fixture'] === 'number' ? item['Capacity on Fixture'] : undefined,
91
+ sections,
92
+ fixtureCode: item['Fixture Code '].trim(),
93
+ fixtureConfigType: item['Fixture Type'],
94
+ };
95
+ } );
96
+
97
+ await fixtureConfigService.insertMany( transformedData );
98
+ return res.sendSuccess( { message: 'Data inserted successfully', length: transformedData.length } );
99
+ } catch ( e ) {
100
+ logger.error( { functionName: 'transformDataAPI', error: e } );
101
+ return res.sendError( e.message || 'Internal Server Error', 500 );
102
+ }
103
+ }
104
+
105
+
106
+ export async function createPlano( req, res ) {
107
+ try {
108
+ if ( !req.files.file ) {
109
+ return res.sendError( 'Invalid or missing Excel file', 400 );
110
+ }
111
+
112
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
113
+ const sheetName = 'Layout,Fixture&VM';
114
+ if ( !workbook.Sheets[sheetName] ) {
115
+ return res.sendError( `Sheet "${sheetName}" not found`, 400 );
116
+ }
117
+
118
+ const raw = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
119
+ const storeNames = new Set();
120
+
121
+ raw.forEach( ( item ) => {
122
+ storeNames.add( item?.['Store ID'] );
123
+ } );
124
+
125
+ const storeNamesArray = Array.from( storeNames );
126
+
127
+ for ( const store of storeNamesArray ) {
128
+ const storeData = await storeService.findOne( { storeName: store } );
129
+
130
+ const planoInsertData = {
131
+ storeName: store,
132
+ storeId: storeData?.toObject()?.storeId ? storeData.toObject().storeId : 'nil',
133
+ layoutName: `${store} - Layout`,
134
+ clientId: '11',
135
+ attachments: [],
136
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
137
+ createdByName: 'Bejan',
138
+ createdByEmail: 'bejan@tangotech.co.in',
139
+ status: 'completed',
140
+ floorNumber: 1,
141
+ productResolutionLevel: 'L2',
142
+ scanType: 'qr',
143
+ };
144
+
145
+ await planoService.create( planoInsertData );
146
+
147
+ console.log( planoInsertData );
148
+ }
149
+
150
+ return res.sendSuccess( { message: 'Plano data inserted successfully', length: storeNamesArray.length } );
151
+ } catch ( e ) {
152
+ logger.error( { functionName: 'createPlanoAPI', error: e } );
153
+ return res.sendError( e.message || 'Internal Server Error', 500 );
154
+ }
155
+ }
156
+
157
+ // export async function createFloors(req, res) {
158
+ // try {
159
+ // if (!req.files.file) {
160
+ // return res.sendError('Invalid or missing Excel file', 400);
161
+ // }
162
+
163
+ // const workbook = xlsx.read(req.files.file.data, { type: 'buffer' });
164
+ // const sheetName = 'Layout,Fixture&VM';
165
+ // if (!workbook.Sheets[sheetName]) {
166
+ // return res.sendError(`Sheet "${sheetName}" not found`, 400);
167
+ // }
168
+
169
+ // const rawData = xlsx.utils.sheet_to_json(workbook.Sheets[sheetName]);
170
+
171
+ // const groupedData = {};
172
+
173
+ // rawData.forEach((item) => {
174
+ // const fixtureId = item['Store Fixture ID'];
175
+
176
+ // if (!groupedData[fixtureId]) {
177
+ // groupedData[fixtureId] = {
178
+ // 'Store ID': item['Store ID'],
179
+ // 'Store Fixture ID': fixtureId,
180
+ // 'Fixture ID': item['Fixture ID ( For ref only)'],
181
+ // 'Fixture Category': item['Fixture Category'],
182
+ // 'Fixture Size (feet)': item['Fixture Size (feet)'],
183
+ // 'Fixture Count': item['Fixture Count'],
184
+ // 'Effective Fixture Count': item['Effective Fixture Count'],
185
+ // 'Capacity': item['Capacity'],
186
+ // 'Store Fixture Locator': item['Store Fixture Locator'],
187
+ // 'Wall': item['Wall'],
188
+ // 'Brand-Category': item['Brand-Category'],
189
+ // 'Brand - Sub Category': item['Brand - Sub Category'],
190
+ // 'VM Template ID': item['VM Template ID '],
191
+ // 'categories': [],
192
+ // };
193
+ // }
194
+
195
+ // const categories = groupedData[fixtureId]['categories'];
196
+ // const existingCategory = categories.find((cat) => cat['Zone'] === item['Section Allocation ']);
197
+
198
+ // if (!existingCategory) {
199
+ // categories.push({
200
+ // 'Allocation': item['Shelf Allocation'],
201
+ // 'Zone': item['Section Allocation '],
202
+ // });
203
+ // }
204
+ // });
205
+
206
+ // const raw = Object.values(groupedData);
207
+
208
+ // const constantFixtureLength = 1220;
209
+ // const constantDetailedFixtureLength = 1220;
210
+ // const constantFixtureWidth = 610;
211
+ // const constantDetailedFixtureWidth = 1524;
212
+ // const mmToFeet = 305;
213
+
214
+ // const storeList = await planoService.find({});
215
+
216
+ // await Promise.all(storeList.map(async (store) => {
217
+ // const planoDoc = store.toObject();
218
+ // const leftWalls = raw.filter((entry) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Left');
219
+ // const leftWallCount = leftWalls.length;
220
+
221
+ // const totalLeftDistanceFeet = Math.round((leftWallCount * (constantFixtureLength / mmToFeet)));
222
+ // const totalLeftDetailedDistanceFeet = Math.round((leftWallCount * (constantDetailedFixtureLength / mmToFeet)));
223
+
224
+ // const rightWalls = raw.filter((entry) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Right');
225
+ // const rightWallCount = rightWalls.length;
226
+
227
+ // const totalRightDistanceFeet = Math.round((rightWallCount * (constantFixtureLength / mmToFeet)));
228
+ // const totalRightDetailedDistanceFeet = Math.round((rightWallCount * (constantDetailedFixtureLength / mmToFeet)));
229
+
230
+ // const totalDistanceFeet = Math.max(totalLeftDistanceFeet, totalRightDistanceFeet);
231
+ // const totalDetailedDistanceFeet = Math.max(totalLeftDetailedDistanceFeet, totalRightDetailedDistanceFeet);
232
+
233
+ // const floorFixtures = raw.filter((entry) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Centre');
234
+ // const floorFixtureLength = floorFixtures.length;
235
+ // const maxFixturesPerRow = floorFixtureLength > 4 ? 3 : 2;
236
+ // const totalRows = Math.ceil(floorFixtureLength / maxFixturesPerRow);
237
+
238
+ // const yDistance = Math.round(((totalRows + 6) * (constantFixtureWidth / mmToFeet)));
239
+ // const detailedyDistance = Math.round(((totalRows + 4) * (constantDetailedFixtureWidth / mmToFeet)));
240
+
241
+ // const floorInsertData = {
242
+ // storeName: planoDoc.storeName,
243
+ // storeId: planoDoc.storeId,
244
+ // layoutName: `${planoDoc.storeName} - Layout`,
245
+ // clientId: '11',
246
+ // floorNumber: 1,
247
+ // floorName: 'floor 1',
248
+ // layoutPolygon: [
249
+ // {
250
+ // elementType: 'wall',
251
+ // distance: totalDistanceFeet + 3,
252
+ // unit: 'ft',
253
+ // direction: 'right',
254
+ // angle: 90,
255
+ // elementNumber: 1,
256
+ // detailedDistance: totalDetailedDistanceFeet + 3,
257
+ // },
258
+ // {
259
+ // elementType: 'wall',
260
+ // distance: yDistance,
261
+ // unit: 'ft',
262
+ // direction: 'down',
263
+ // angle: 90,
264
+ // elementNumber: 2,
265
+ // detailedDistance: detailedyDistance,
266
+ // },
267
+ // {
268
+ // elementType: 'wall',
269
+ // distance: totalDistanceFeet + 3,
270
+ // unit: 'ft',
271
+ // direction: 'left',
272
+ // angle: 90,
273
+ // elementNumber: 3,
274
+ // detailedDistance: totalDetailedDistanceFeet + 3,
275
+ // },
276
+ // {
277
+ // elementType: 'wall',
278
+ // distance: Math.round((yDistance * 40) / 100),
279
+ // unit: 'ft',
280
+ // direction: 'up',
281
+ // angle: 90,
282
+ // elementNumber: 4,
283
+ // detailedDistance: Math.round((detailedyDistance * 35) / 100),
284
+ // },
285
+ // {
286
+ // elementType: 'entrance',
287
+ // distance: Math.round((yDistance * 20) / 100),
288
+ // unit: 'ft',
289
+ // direction: 'up',
290
+ // angle: 90,
291
+ // elementNumber: 1,
292
+ // detailedDistance: Math.round((detailedyDistance * 30) / 100),
293
+ // },
294
+ // {
295
+ // elementType: 'wall',
296
+ // distance: Math.round((yDistance * 40) / 100),
297
+ // unit: 'ft',
298
+ // direction: 'up',
299
+ // angle: 90,
300
+ // elementNumber: 5,
301
+ // detailedDistance: Math.round((detailedyDistance * 35) / 100),
302
+ // },
303
+ // ],
304
+ // createdBy: new mongoose.Types.ObjectId('66a78cd82734f4f857cd6db6'),
305
+ // createdByName: 'Bejan',
306
+ // createdByEmail: 'bejan@tangotech.co.in',
307
+ // status: 'completed',
308
+ // planoId: planoDoc._id,
309
+ // };
310
+
311
+ // await storeBuilderService.create(floorInsertData);
312
+
313
+ // console.log(floorInsertData);
314
+ // }));
315
+
316
+ // return res.sendSuccess({ message: 'Floor data inserted successfully' });
317
+ // } catch (e) {
318
+ // logger.error({ functionName: 'addFloorDataAPI', error: e });
319
+ // return res.sendError(e.message || 'Internal Server Error', 500);
320
+ // }
321
+ // }
322
+
323
+ export async function createFloors( req, res ) {
324
+ try {
325
+ if ( !req.files.file ) {
326
+ return res.sendError( 'Invalid or missing Excel file', 400 );
327
+ }
328
+
329
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
330
+ const sheetName = 'Layout,Fixture&VM';
331
+ if ( !workbook.Sheets[sheetName] ) {
332
+ return res.sendError( `Sheet "${sheetName}" not found`, 400 );
333
+ }
334
+
335
+ const rawData = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
336
+
337
+ const groupedData = {};
338
+
339
+ rawData.forEach( ( item ) => {
340
+ const fixtureId = item['Store Fixture ID'];
341
+
342
+ if ( !groupedData[fixtureId] ) {
343
+ groupedData[fixtureId] = {
344
+ 'Store ID': item['Store ID'],
345
+ 'Store Fixture ID': fixtureId,
346
+ 'Fixture ID': item['Fixture ID ( For ref only)'],
347
+ 'Fixture Category': item['Fixture Category'],
348
+ 'Fixture Size (feet)': item['Fixture Size (feet)'],
349
+ 'Fixture Count': item['Fixture Count'],
350
+ 'Effective Fixture Count': item['Effective Fixture Count'],
351
+ 'Capacity': item['Capacity'],
352
+ 'Store Fixture Locator': item['Store Fixture Locator'],
353
+ 'Wall': item['Wall'],
354
+ 'Brand-Category': item['Brand-Category'],
355
+ 'Brand - Sub Category': item['Brand - Sub Category'],
356
+ 'VM Template ID': item['VM Template ID '],
357
+ 'categories': [],
358
+ };
359
+ }
360
+
361
+ const categories = groupedData[fixtureId]['categories'];
362
+ const existingCategory = categories.find( ( cat ) => cat['Zone'] === item['Section Allocation '] );
363
+
364
+ if ( !existingCategory ) {
365
+ categories.push( {
366
+ 'Allocation': item['Shelf Allocation'],
367
+ 'Zone': item['Section Allocation '],
368
+ } );
369
+ }
370
+ } );
371
+
372
+ const raw = Object.values( groupedData );
373
+
374
+ const constantFixtureLength = 1220;
375
+ const constantDetailedFixtureLength = 1220;
376
+
377
+ const constantFixtureWidth = 610;
378
+ const constantDetailedFixtureWidth = 1524;
379
+
380
+ const mmToFeet = 305;
381
+
382
+ const storeList = await planoService.find( {} );
383
+
384
+ function roundToTwo( num ) {
385
+ return Math.round( num * 100 ) / 100;
386
+ }
387
+
388
+ await Promise.all( storeList.map( async ( store ) => {
389
+ const planoDoc = store.toObject();
390
+ const leftFixtures = raw.filter( ( entry ) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Left' );
391
+ const rightFixtures = raw.filter( ( entry ) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Right' );
392
+ const floorFixtures = raw.filter( ( entry ) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Centre' );
393
+ const backFixtures = raw.filter( ( entry ) => entry['Store ID'] === planoDoc.storeName && entry.Wall === 'Back' );
394
+
395
+ const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
396
+ const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
397
+
398
+ const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
399
+ const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
400
+
401
+ const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
402
+ const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
403
+
404
+ const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
405
+ const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
406
+
407
+ const maxFixturesPerRow = floorFixtures.length > 4 ? 3 : 2;
408
+ const totalRows = Math.ceil( floorFixtures.length / maxFixturesPerRow );
409
+ const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( maxFixturesPerRow * ( constantFixtureLength / mmToFeet ) ) ) : 0;
410
+ const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( maxFixturesPerRow * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
411
+
412
+ const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( totalRows * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
413
+ const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( totalRows * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
414
+
415
+ const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
416
+ const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
417
+
418
+ const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
419
+ const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
420
+
421
+ const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
422
+ const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
423
+
424
+ const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet );
425
+ const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet );
426
+
427
+ const finalXDistance = maxXDistance;
428
+ const finalXDetailedDistance = maxXDetailedDistance;
429
+
430
+
431
+ const finalYDistance = maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : maxYDistance;
432
+ const finalYDetailedDistance = maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : maxYDetailedDistance;
433
+
434
+ const floorInsertData = {
435
+ storeName: planoDoc.storeName,
436
+ storeId: planoDoc.storeId,
437
+ layoutName: `${planoDoc.storeName} - Layout`,
438
+ clientId: '11',
439
+ floorNumber: 1,
440
+ floorName: 'floor 1',
441
+ layoutPolygon: [
442
+ {
443
+ elementType: 'wall',
444
+ distance: finalXDistance,
445
+ unit: 'ft',
446
+ direction: 'right',
447
+ angle: 90,
448
+ elementNumber: 1,
449
+ detailedDistance: finalXDetailedDistance,
450
+ },
451
+ {
452
+ elementType: 'wall',
453
+ distance: finalYDistance,
454
+ unit: 'ft',
455
+ direction: 'down',
456
+ angle: 90,
457
+ elementNumber: 2,
458
+ detailedDistance: finalYDetailedDistance,
459
+ },
460
+ {
461
+ elementType: 'wall',
462
+ distance: finalXDistance,
463
+ unit: 'ft',
464
+ direction: 'left',
465
+ angle: 90,
466
+ elementNumber: 3,
467
+ detailedDistance: finalXDetailedDistance,
468
+ },
469
+ {
470
+ elementType: 'wall',
471
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
472
+ unit: 'ft',
473
+ direction: 'up',
474
+ angle: 90,
475
+ elementNumber: 4,
476
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
477
+ },
478
+ {
479
+ elementType: 'entrance',
480
+ distance: roundToTwo( ( ( finalYDistance * 20 ) / 100 ) ),
481
+ unit: 'ft',
482
+ direction: 'up',
483
+ angle: 90,
484
+ elementNumber: 1,
485
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 30 ) / 100 ) ),
486
+ },
487
+ {
488
+ elementType: 'wall',
489
+ distance: roundToTwo( ( ( finalYDistance * 40 ) / 100 ) ),
490
+ unit: 'ft',
491
+ direction: 'up',
492
+ angle: 90,
493
+ elementNumber: 5,
494
+ detailedDistance: roundToTwo( ( ( finalYDetailedDistance * 35 ) / 100 ) ),
495
+ },
496
+ ],
497
+ createdBy: new mongoose.Types.ObjectId( '66a78cd82734f4f857cd6db6' ),
498
+ createdByName: 'Bejan',
499
+ createdByEmail: 'bejan@tangotech.co.in',
500
+ status: 'completed',
501
+ planoId: planoDoc._id,
502
+ };
503
+
504
+ await storeBuilderService.create( floorInsertData );
505
+
506
+ // console.log( floorInsertData );
507
+ } ) );
508
+
509
+ return res.sendSuccess( { message: 'Floor data inserted successfully' } );
510
+ } catch ( e ) {
511
+ logger.error( { functionName: 'addFloorDataAPI', error: e } );
512
+ return res.sendError( e.message || 'Internal Server Error', 500 );
513
+ }
514
+ }
515
+
516
+ // export async function createFixturesShelves( req, res ) {
517
+ // try {
518
+ // if ( !req.files.file ) {
519
+ // return res.sendError( 'Invalid or missing Excel file', 400 );
520
+ // }
521
+
522
+ // const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
523
+ // const sheetName = 'Layout,Fixture&VM';
524
+ // if ( !workbook.Sheets[sheetName] ) {
525
+ // return res.sendError( `Sheet "${sheetName}" not found`, 400 );
526
+ // }
527
+
528
+ // const rawData = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
529
+
530
+ // const groupedData = {};
531
+
532
+ // rawData.forEach( ( item ) => {
533
+ // const fixtureId = item['Store Fixture ID'];
534
+
535
+ // if ( !groupedData[fixtureId] ) {
536
+ // groupedData[fixtureId] = {
537
+ // 'Store ID': item['Store ID'],
538
+ // 'Store Fixture ID': fixtureId,
539
+ // 'Fixture ID': item['Fixture ID ( For ref only)'],
540
+ // 'Fixture Category': item['Fixture Category'],
541
+ // 'Fixture Size (feet)': item['Fixture Size (feet)'],
542
+ // 'Fixture Count': item['Fixture Count'],
543
+ // 'Effective Fixture Count': item['Effective Fixture Count'],
544
+ // 'Capacity': item['Capacity'],
545
+ // 'Store Fixture Locator': item['Store Fixture Locator'],
546
+ // 'Wall': item['Wall'],
547
+ // 'Brand-Category': item['Brand-Category'],
548
+ // 'Brand - Sub Category': item['Brand - Sub Category'],
549
+ // 'VM Template ID': item['VM Template ID '],
550
+ // 'categories': [],
551
+ // };
552
+ // }
553
+
554
+ // const categories = groupedData[fixtureId]['categories'];
555
+ // const existingCategory = categories.find( ( cat ) => cat['Zone'] === item['Section Allocation '] );
556
+
557
+ // if ( !existingCategory ) {
558
+ // categories.push( {
559
+ // 'Allocation': item['Shelf Allocation'],
560
+ // 'Zone': item['Section Allocation '],
561
+ // } );
562
+ // }
563
+ // } );
564
+
565
+ // const raw = Object.values( groupedData );
566
+
567
+ // const constantFixtureLength = 1220;
568
+ // const constantDetailedFixtureLength = 1220;
569
+ // const constantDetailedFloorFixtureLength = 1524;
570
+
571
+
572
+ // const constantFixtureWidth = 610;
573
+ // const constantDetailedFixtureWidth = 1524;
574
+ // const constantDetailedFloorFixtureWidth = 1220;
575
+
576
+
577
+ // const mmToFeet = 305;
578
+ // const layoutList = await storeBuilderService.find( {} );
579
+
580
+ // for ( let i = 0; i < layoutList.length; i++ ) {
581
+ // const layout = layoutList[i];
582
+
583
+ // const layoutDoc = layout.toObject();
584
+
585
+ // const leftFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Left' );
586
+ // const leftWallCount = leftFixtures.length;
587
+
588
+ // const rightFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Right' );
589
+ // const rightWallCount = rightFixtures.length;
590
+
591
+
592
+ // const floorFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Centre' );
593
+ // const floorFixtureCount = floorFixtures.length;
594
+
595
+ // const maxFixturesPerRow = floorFixtureCount > 4 ? 3 : 2;
596
+
597
+ // const totalRows = Math.ceil( floorFixtureCount / maxFixturesPerRow );
598
+ // const centerRow = Math.floor( totalRows / 2 );
599
+
600
+ // const totalLeftDistanceFeet = Math.round( ( leftWallCount * ( constantFixtureLength / mmToFeet ) ) );
601
+ // const totalLeftDetailedDistanceFeet = Math.round( ( leftWallCount * ( constantDetailedFixtureLength / mmToFeet ) ) );
602
+
603
+ // const totalRightDistanceFeet = Math.round( ( rightWallCount * ( constantFixtureLength / mmToFeet ) ) );
604
+ // const totalRightDetailedDistanceFeet = Math.round( ( rightWallCount * ( constantDetailedFixtureLength / mmToFeet ) ) );
605
+
606
+ // const totalCentreDistanceFeet = Math.round( ( ( totalRows + 6 ) * ( constantFixtureWidth / mmToFeet ) ) );
607
+ // const totalCentreDetailedDistanceFeet = Math.round( ( ( totalRows + 4 ) * ( constantDetailedFixtureWidth / mmToFeet ) ) );
608
+
609
+ // const totalDistanceFeetX = Math.max( totalLeftDistanceFeet, totalRightDistanceFeet );
610
+ // const totalDetailedDistanceFeetX = Math.max( totalLeftDetailedDistanceFeet, totalRightDetailedDistanceFeet );
611
+
612
+ // const totalDistanceFeetY = totalCentreDistanceFeet;
613
+ // const totalDetailedDistanceFeetY = totalCentreDetailedDistanceFeet;
614
+
615
+ // const startingX = ( totalDistanceFeetX / 2 ) - ( Math.floor( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) );
616
+ // const startingY = ( totalDistanceFeetY / 2 ) - ( centerRow * ( constantFixtureWidth / mmToFeet ) );
617
+
618
+ // const detailedStartingX = ( totalDetailedDistanceFeetX / 2 ) - ( Math.floor( maxFixturesPerRow / 2 ) * ( constantDetailedFloorFixtureLength / mmToFeet ) );
619
+ // const detailedStartingY = ( totalDetailedDistanceFeetY / 2 ) - ( centerRow * ( constantDetailedFloorFixtureWidth / mmToFeet ) );
620
+
621
+ // let fixtureCounter = 1;
622
+
623
+ // for ( let index = 0; index < leftFixtures.length; index++ ) {
624
+ // const fixture = leftFixtures[index];
625
+
626
+ // const fixtureData = {
627
+ // 'clientId': layoutDoc.clientId,
628
+ // 'storeName': layoutDoc.storeName,
629
+ // 'storeId': layoutDoc.storeId,
630
+ // 'planoId': layoutDoc.planoId,
631
+ // 'floorId': layoutDoc._id,
632
+ // 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
633
+ // 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
634
+ // 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
635
+ // 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
636
+ // 'fixtureCode': fixture?.['Fixture ID'],
637
+ // 'fixtureCapacity': fixture?.['Capacity'],
638
+ // 'fixtureType': 'wall',
639
+ // 'fixtureHeight': {
640
+ // 'value': 0,
641
+ // 'unit': 'mm',
642
+ // },
643
+ // 'fixtureLength': {
644
+ // 'value': constantFixtureLength,
645
+ // 'unit': 'mm',
646
+ // },
647
+ // 'fixtureWidth': {
648
+ // 'value': constantFixtureWidth,
649
+ // 'unit': 'mm',
650
+ // },
651
+ // 'associatedElementType': 'wall',
652
+ // 'associatedElementNumber': 1,
653
+ // 'relativePosition': {
654
+ // 'x': Math.round( ( index * ( constantFixtureLength / mmToFeet ) ) ),
655
+ // 'y': 0,
656
+ // 'unit': 'ft',
657
+ // },
658
+ // 'fixtureNumber': fixtureCounter++,
659
+ // 'detailedFixtureLength': {
660
+ // 'value': constantDetailedFixtureLength,
661
+ // 'unit': 'mm',
662
+ // },
663
+ // 'detailedFixtureWidth': {
664
+ // 'value': constantDetailedFixtureWidth,
665
+ // 'unit': 'mm',
666
+ // },
667
+ // 'relativeDetailedPosition': {
668
+ // 'x': Math.round( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
669
+ // 'y': 0,
670
+ // 'unit': 'ft',
671
+ // },
672
+ // 'productResolutionLevel': 'L2',
673
+ // };
674
+
675
+ // const createdFixture = await storeFixtureService.create( fixtureData );
676
+
677
+ // // console.log( 'Fixture Data', fixtureData );
678
+
679
+
680
+ // const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
681
+
682
+ // for ( let i = 0; i < vms?.length; i++ ) {
683
+ // const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
684
+
685
+ // if ( vmTemplate ) {
686
+ // const vmData = {
687
+ // 'clientId': layoutDoc.clientId,
688
+ // 'storeName': layoutDoc.storeName,
689
+ // 'storeId': layoutDoc.storeId,
690
+ // 'planoId': layoutDoc.planoId,
691
+ // 'floorId': layoutDoc._id,
692
+ // 'type': 'vm',
693
+ // 'fixtureId': createdFixture._id,
694
+ // 'productId': vmTemplate._id,
695
+ // };
696
+
697
+ // await planoMappingService.create( vmData );
698
+ // }
699
+ // }
700
+
701
+
702
+ // const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
703
+
704
+ // if ( fixtureConfig ) {
705
+ // let shelfIndex = 0;
706
+
707
+ // for ( const section of fixtureConfig.sections ) {
708
+ // const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
709
+
710
+ // for ( let j = 0; j < section.sectionShelves; j++ ) {
711
+ // if ( shelfIndex >= fixtureConfig.shelfCount ) break;
712
+
713
+
714
+ // const shelfData = {
715
+ // 'clientId': fixtureConfig.clientId,
716
+ // 'storeName': layoutDoc.storeName,
717
+ // 'storeId': layoutDoc.storeId,
718
+ // 'planoId': layoutDoc.planoId,
719
+ // 'floorId': layoutDoc._id,
720
+ // 'fixtureId': createdFixture._id,
721
+ // 'shelfNumber': shelfIndex + 1,
722
+ // 'shelfOrder': 'LTR',
723
+ // 'shelfCapacity': fixtureConfig.productPerShelf,
724
+ // 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
725
+ // 'sectionZone': section.sectionId,
726
+ // };
727
+
728
+
729
+ // const createdShelf = await fixtureShelfService.create( shelfData );
730
+
731
+ // // console.log( 'Shelf Data:', createdShelf );
732
+
733
+ // shelfIndex++;
734
+ // }
735
+ // }
736
+ // }
737
+ // }
738
+
739
+ // for ( let index = 0; index < rightFixtures.length; index++ ) {
740
+ // const fixture = rightFixtures[index];
741
+
742
+ // const fixtureData = {
743
+ // 'clientId': layoutDoc.clientId,
744
+ // 'storeName': layoutDoc.storeName,
745
+ // 'storeId': layoutDoc.storeId,
746
+ // 'planoId': layoutDoc.planoId,
747
+ // 'floorId': layoutDoc._id,
748
+ // 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
749
+ // 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
750
+ // 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
751
+ // 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
752
+ // 'fixtureCode': fixture?.['Fixture ID'],
753
+ // 'fixtureCapacity': fixture?.['Capacity'],
754
+ // 'fixtureType': 'wall',
755
+ // 'fixtureHeight': {
756
+ // 'value': 0,
757
+ // 'unit': 'mm',
758
+ // },
759
+ // 'fixtureLength': {
760
+ // 'value': constantFixtureLength,
761
+ // 'unit': 'mm',
762
+ // },
763
+ // 'fixtureWidth': {
764
+ // 'value': constantFixtureWidth,
765
+ // 'unit': 'mm',
766
+ // },
767
+ // 'associatedElementType': 'wall',
768
+ // 'associatedElementNumber': 3,
769
+ // 'relativePosition': {
770
+ // 'x': Math.round( ( index * ( constantFixtureLength / mmToFeet ) ) ),
771
+ // 'y': Math.round( ( ( ( totalRows + 6 ) * ( constantFixtureWidth / mmToFeet ) ) - ( constantFixtureWidth / mmToFeet ) ) ),
772
+ // 'unit': 'ft',
773
+ // },
774
+ // 'fixtureNumber': fixtureCounter++,
775
+ // 'detailedFixtureLength': {
776
+ // 'value': constantDetailedFixtureLength,
777
+ // 'unit': 'mm',
778
+ // },
779
+ // 'detailedFixtureWidth': {
780
+ // 'value': constantDetailedFixtureWidth,
781
+ // 'unit': 'mm',
782
+ // },
783
+ // 'relativeDetailedPosition': {
784
+ // 'x': Math.round( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
785
+ // 'y': Math.round( ( ( ( totalRows + 4 ) * ( constantDetailedFixtureWidth / mmToFeet ) ) - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
786
+ // 'unit': 'ft',
787
+ // },
788
+ // 'productResolutionLevel': 'L2',
789
+ // };
790
+
791
+ // const createdFixture = await storeFixtureService.create( fixtureData );
792
+
793
+ // // console.log( 'Fixture Data', fixtureData );
794
+
795
+ // const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
796
+
797
+ // for ( let i = 0; i < vms?.length; i++ ) {
798
+ // const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
799
+
800
+ // if ( vmTemplate ) {
801
+ // const vmData = {
802
+ // 'clientId': layoutDoc.clientId,
803
+ // 'storeName': layoutDoc.storeName,
804
+ // 'storeId': layoutDoc.storeId,
805
+ // 'planoId': layoutDoc.planoId,
806
+ // 'floorId': layoutDoc._id,
807
+ // 'type': 'vm',
808
+ // 'fixtureId': createdFixture._id,
809
+ // 'productId': vmTemplate._id,
810
+ // };
811
+
812
+ // await planoMappingService.create( vmData );
813
+ // }
814
+ // }
815
+
816
+ // const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
817
+
818
+ // if ( fixtureConfig ) {
819
+ // let shelfIndex = 0;
820
+
821
+ // for ( const section of fixtureConfig.sections ) {
822
+ // const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
823
+ // for ( let j = 0; j < section.sectionShelves; j++ ) {
824
+ // if ( shelfIndex >= fixtureConfig.shelfCount ) break;
825
+
826
+ // const shelfData = {
827
+ // 'clientId': fixtureConfig.clientId,
828
+ // 'storeName': layoutDoc.storeName,
829
+ // 'storeId': layoutDoc.storeId,
830
+ // 'planoId': layoutDoc.planoId,
831
+ // 'floorId': layoutDoc._id,
832
+ // 'fixtureId': createdFixture._id,
833
+ // 'shelfNumber': shelfIndex + 1,
834
+ // 'shelfOrder': 'LTR',
835
+ // 'shelfCapacity': fixtureConfig.productPerShelf,
836
+ // 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
837
+ // 'sectionZone': section.sectionId,
838
+ // };
839
+
840
+ // const createdShelf = await fixtureShelfService.create( shelfData );
841
+
842
+ // // console.log( 'Shelf Data:', createdShelf );
843
+
844
+ // shelfIndex++;
845
+ // }
846
+ // }
847
+ // }
848
+ // }
849
+
850
+ // for ( let index = 0; index < floorFixtures.length; index++ ) {
851
+ // const fixture = floorFixtures[index];
852
+
853
+ // const rowIndex = Math.floor( index / maxFixturesPerRow );
854
+ // const colIndex = index % maxFixturesPerRow;
855
+
856
+ // const xPos = Math.round( startingX + colIndex * ( constantFixtureLength / mmToFeet ) );
857
+ // const yPos = Math.round( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) );
858
+
859
+ // const detailedXPos = Math.round( detailedStartingX + colIndex * ( constantDetailedFloorFixtureLength / mmToFeet ) );
860
+ // const detailedYPos = Math.round( detailedStartingY + rowIndex * ( constantDetailedFloorFixtureWidth / mmToFeet ) );
861
+
862
+ // const fixtureData = {
863
+ // 'clientId': layoutDoc.clientId,
864
+ // 'storeName': layoutDoc.storeName,
865
+ // 'storeId': layoutDoc.storeId,
866
+ // 'planoId': layoutDoc.planoId,
867
+ // 'floorId': layoutDoc._id,
868
+ // 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
869
+ // 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
870
+ // 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
871
+ // 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
872
+ // 'fixtureCode': fixture?.['Fixture ID'],
873
+ // 'fixtureCapacity': fixture?.['Capacity'],
874
+ // 'fixtureType': 'floor',
875
+ // 'fixtureHeight': {
876
+ // 'value': 0,
877
+ // 'unit': 'mm',
878
+ // },
879
+ // 'fixtureLength': {
880
+ // 'value': constantFixtureLength,
881
+ // 'unit': 'mm',
882
+ // },
883
+ // 'fixtureWidth': {
884
+ // 'value': constantFixtureWidth,
885
+ // 'unit': 'mm',
886
+ // },
887
+ // 'relativePosition': {
888
+ // 'x': xPos,
889
+ // 'y': yPos,
890
+ // 'unit': 'ft',
891
+ // },
892
+ // 'fixtureNumber': fixtureCounter++,
893
+ // 'detailedFixtureLength': {
894
+ // 'value': constantDetailedFloorFixtureLength,
895
+ // 'unit': 'mm',
896
+ // },
897
+ // 'detailedFixtureWidth': {
898
+ // 'value': constantDetailedFloorFixtureWidth,
899
+ // 'unit': 'mm',
900
+ // },
901
+ // 'relativeDetailedPosition': {
902
+ // 'x': detailedXPos,
903
+ // 'y': detailedYPos,
904
+ // 'unit': 'ft',
905
+ // },
906
+ // 'productResolutionLevel': 'L2',
907
+ // };
908
+
909
+ // const createdFixture = await storeFixtureService.create( fixtureData );
910
+ // // console.log( 'Fixture Data', fixtureData );
911
+
912
+ // const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
913
+
914
+ // for ( let i = 0; i < vms?.length; i++ ) {
915
+ // const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
916
+
917
+ // if ( vmTemplate ) {
918
+ // const vmData = {
919
+ // 'clientId': layoutDoc.clientId,
920
+ // 'storeName': layoutDoc.storeName,
921
+ // 'storeId': layoutDoc.storeId,
922
+ // 'planoId': layoutDoc.planoId,
923
+ // 'floorId': layoutDoc._id,
924
+ // 'type': 'vm',
925
+ // 'fixtureId': createdFixture._id,
926
+ // 'productId': vmTemplate._id,
927
+ // };
928
+
929
+ // await planoMappingService.create( vmData );
930
+ // }
931
+ // }
932
+
933
+
934
+ // const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
935
+
936
+ // if ( fixtureConfig ) {
937
+ // let shelfIndex = 0;
938
+
939
+ // for ( const section of fixtureConfig.sections ) {
940
+ // const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
941
+ // for ( let j = 0; j < section.sectionShelves; j++ ) {
942
+ // if ( shelfIndex >= fixtureConfig.shelfCount ) break;
943
+
944
+ // const shelfData = {
945
+ // 'clientId': fixtureConfig.clientId,
946
+ // 'storeName': layoutDoc.storeName,
947
+ // 'storeId': layoutDoc.storeId,
948
+ // 'planoId': layoutDoc.planoId,
949
+ // 'floorId': layoutDoc._id,
950
+ // 'fixtureId': createdFixture._id,
951
+ // 'shelfNumber': shelfIndex + 1,
952
+ // 'shelfOrder': 'LTR',
953
+ // 'shelfCapacity': fixtureConfig.productPerShelf,
954
+ // 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
955
+ // 'sectionZone': section.sectionId,
956
+ // };
957
+
958
+ // const createdShelf = await fixtureShelfService.create( shelfData );
959
+
960
+ // // console.log( 'Shelf Data:', createdShelf );
961
+
962
+ // shelfIndex++;
963
+ // }
964
+ // }
965
+ // }
966
+ // }
967
+ // }
968
+
969
+
970
+ // return res.sendSuccess( 'Updated successfully' );
971
+ // } catch ( e ) {
972
+ // logger.error( { functionName: 'createFixturesShelves', error: e } );
973
+ // return res.sendError( e.message || 'Internal Server Error', 500 );
974
+ // }
975
+ // }
976
+
977
+ export async function createFixturesShelves( req, res ) {
978
+ try {
979
+ if ( !req.files.file ) {
980
+ return res.sendError( 'Invalid or missing Excel file', 400 );
981
+ }
982
+
983
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
984
+ const sheetName = 'Layout,Fixture&VM';
985
+ if ( !workbook.Sheets[sheetName] ) {
986
+ return res.sendError( `Sheet "${sheetName}" not found`, 400 );
987
+ }
988
+
989
+ const rawData = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
990
+
991
+ const groupedData = {};
992
+
993
+ rawData.forEach( ( item ) => {
994
+ const fixtureId = item['Store Fixture ID'];
995
+
996
+ if ( !groupedData[fixtureId] ) {
997
+ groupedData[fixtureId] = {
998
+ 'Store ID': item['Store ID'],
999
+ 'Store Fixture ID': fixtureId,
1000
+ 'Fixture ID': item['Fixture ID ( For ref only)'],
1001
+ 'Fixture Category': item['Fixture Category'],
1002
+ 'Fixture Size (feet)': item['Fixture Size (feet)'],
1003
+ 'Fixture Count': item['Fixture Count'],
1004
+ 'Effective Fixture Count': item['Effective Fixture Count'],
1005
+ 'Capacity': item['Capacity'],
1006
+ 'Store Fixture Locator': item['Store Fixture Locator'],
1007
+ 'Wall': item['Wall'],
1008
+ 'Brand-Category': item['Brand-Category'],
1009
+ 'Brand - Sub Category': item['Brand - Sub Category'],
1010
+ 'VM Template ID': item['VM Template ID '],
1011
+ 'categories': [],
1012
+ };
1013
+ }
1014
+
1015
+ const categories = groupedData[fixtureId]['categories'];
1016
+ const existingCategory = categories.find( ( cat ) => cat['Zone'] === item['Section Allocation '] );
1017
+
1018
+ if ( !existingCategory ) {
1019
+ categories.push( {
1020
+ 'Allocation': item['Shelf Allocation'],
1021
+ 'Zone': item['Section Allocation '],
1022
+ } );
1023
+ }
1024
+ } );
1025
+
1026
+ const raw = Object.values( groupedData );
1027
+
1028
+ const constantFixtureLength = 1220;
1029
+ const constantDetailedFixtureLength = 1220;
1030
+
1031
+
1032
+ const constantFixtureWidth = 610;
1033
+ const constantDetailedFixtureWidth = 1524;
1034
+
1035
+
1036
+ const mmToFeet = 305;
1037
+ const layoutList = await storeBuilderService.find( {} );
1038
+
1039
+ function roundToTwo( num ) {
1040
+ return Math.round( num * 100 ) / 100;
1041
+ }
1042
+
1043
+ for ( let i = 0; i < layoutList.length; i++ ) {
1044
+ const layout = layoutList[i];
1045
+
1046
+ const layoutDoc = layout.toObject();
1047
+
1048
+ const leftFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Left' );
1049
+ const rightFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Right' );
1050
+ const floorFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Centre' );
1051
+ const backFixtures = raw.filter( ( entry ) => entry['Store ID'] === layoutDoc.storeName && entry.Wall === 'Back' );
1052
+
1053
+ const leftXDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
1054
+ const leftXDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( leftFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
1055
+
1056
+ const leftYDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantFixtureWidth / mmToFeet ) ) ) : 0;
1057
+ const leftYDetailedDistanceFeet = leftFixtures.length ? roundToTwo( ( ( constantDetailedFixtureWidth / mmToFeet ) ) ) : 0;
1058
+
1059
+ const rightXDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantFixtureLength / mmToFeet ) ) ) : 0;
1060
+ const rightXDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( rightFixtures.length * ( constantDetailedFixtureLength / mmToFeet ) ) ) : 0;
1061
+
1062
+ const rightYDistanceFeet = rightFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
1063
+ const rightYDetailedDistanceFeet = rightFixtures.length ? roundToTwo( ( constantDetailedFixtureWidth / mmToFeet ) ): 0;
1064
+
1065
+ const maxFixturesPerRow = floorFixtures.length > 4 ? 3 : 2;
1066
+ const totalRows = Math.ceil( floorFixtures.length / maxFixturesPerRow );
1067
+ const floorXDistanceFeet = floorFixtures.length ? roundToTwo( ( maxFixturesPerRow * ( constantFixtureLength / mmToFeet ) ) ) : 0;
1068
+ const floorXDetailedDistanceFeet = floorFixtures.length ? roundToTwo( ( maxFixturesPerRow * ( constantDetailedFixtureLength / mmToFeet ) ) ): 0;
1069
+
1070
+ const floorYDistanceFeet = floorFixtures.length ? roundToTwo( ( totalRows * ( constantFixtureWidth/ mmToFeet ) ) ): 0;
1071
+ const floorYDetailedDistanceFeet = floorFixtures.length ? roundToTwo( totalRows * ( constantDetailedFixtureWidth/mmToFeet ) ): 0;
1072
+
1073
+ const backXDistanceFeet = backFixtures.length ? roundToTwo( ( constantFixtureWidth / mmToFeet ) ) : 0;
1074
+ const backXDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( constantDetailedFixtureLength / mmToFeet ) ) : 0;
1075
+
1076
+ const backYDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantFixtureLength / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantFixtureWidth )/mmToFeet ) ) ) : 0;
1077
+ const backYDetailedDistanceFeet = backFixtures.length ? roundToTwo( ( ( backFixtures.length * ( constantDetailedFixtureWidth / mmToFeet ) ) + ( ( ( leftFixtures.length ? 1 : 0 ) + ( rightFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth )/mmToFeet ) ) ): 0;
1078
+
1079
+ const maxXDistance = Math.max( leftXDistanceFeet, rightXDistanceFeet, floorXDistanceFeet );
1080
+ const maxXDetailedDistance = Math.max( leftXDetailedDistanceFeet, rightXDetailedDistanceFeet, floorXDetailedDistanceFeet );
1081
+
1082
+ const maxYDistance = Math.max( floorYDistanceFeet, backYDistanceFeet );
1083
+ const maxYDetailedDistance = Math.max( floorYDetailedDistanceFeet, backYDetailedDistanceFeet );
1084
+
1085
+ const finalXDistance = maxXDistance;
1086
+ const finalXDetailedDistance = maxXDetailedDistance;
1087
+
1088
+ const finalYDistance = maxYDistance < ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) ? ( ( leftYDistanceFeet + rightYDistanceFeet + floorYDistanceFeet ) + ( ( 2 * constantFixtureWidth )/mmToFeet ) ) : maxYDistance;
1089
+ const finalYDetailedDistance = maxYDetailedDistance < ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) ? ( ( leftYDetailedDistanceFeet + rightYDetailedDistanceFeet + floorYDetailedDistanceFeet ) + ( ( 2 * constantDetailedFixtureWidth )/mmToFeet ) ) : maxYDetailedDistance;
1090
+
1091
+
1092
+ let fixtureCounter = 1;
1093
+
1094
+ for ( let index = 0; index < leftFixtures.length; index++ ) {
1095
+ const fixture = leftFixtures[index];
1096
+
1097
+ const fixtureData = {
1098
+ 'clientId': layoutDoc.clientId,
1099
+ 'storeName': layoutDoc.storeName,
1100
+ 'storeId': layoutDoc.storeId,
1101
+ 'planoId': layoutDoc.planoId,
1102
+ 'floorId': layoutDoc._id,
1103
+ 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1104
+ 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
1105
+ 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1106
+ 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
1107
+ 'fixtureCode': fixture?.['Fixture ID'],
1108
+ 'fixtureCapacity': fixture?.['Capacity'],
1109
+ 'fixtureType': 'wall',
1110
+ 'fixtureHeight': {
1111
+ 'value': 0,
1112
+ 'unit': 'mm',
1113
+ },
1114
+ 'fixtureLength': {
1115
+ 'value': constantFixtureLength,
1116
+ 'unit': 'mm',
1117
+ },
1118
+ 'fixtureWidth': {
1119
+ 'value': constantFixtureWidth,
1120
+ 'unit': 'mm',
1121
+ },
1122
+ 'associatedElementType': 'wall',
1123
+ 'associatedElementNumber': 1,
1124
+ 'relativePosition': {
1125
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
1126
+ 'y': 0,
1127
+ 'unit': 'ft',
1128
+ },
1129
+ 'fixtureNumber': fixtureCounter++,
1130
+ 'detailedFixtureLength': {
1131
+ 'value': constantDetailedFixtureLength,
1132
+ 'unit': 'mm',
1133
+ },
1134
+ 'detailedFixtureWidth': {
1135
+ 'value': constantDetailedFixtureWidth,
1136
+ 'unit': 'mm',
1137
+ },
1138
+ 'relativeDetailedPosition': {
1139
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
1140
+ 'y': 0,
1141
+ 'unit': 'ft',
1142
+ },
1143
+ 'productResolutionLevel': 'L2',
1144
+ };
1145
+
1146
+ const createdFixture = await storeFixtureService.create( fixtureData );
1147
+
1148
+ // console.log( 'Fixture Data', fixtureData );
1149
+
1150
+
1151
+ const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
1152
+
1153
+ for ( let i = 0; i < vms?.length; i++ ) {
1154
+ const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
1155
+
1156
+ if ( vmTemplate ) {
1157
+ const vmData = {
1158
+ 'clientId': layoutDoc.clientId,
1159
+ 'storeName': layoutDoc.storeName,
1160
+ 'storeId': layoutDoc.storeId,
1161
+ 'planoId': layoutDoc.planoId,
1162
+ 'floorId': layoutDoc._id,
1163
+ 'type': 'vm',
1164
+ 'fixtureId': createdFixture._id,
1165
+ 'productId': vmTemplate._id,
1166
+ };
1167
+
1168
+ await planoMappingService.create( vmData );
1169
+ }
1170
+ }
1171
+
1172
+
1173
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
1174
+
1175
+ if ( fixtureConfig ) {
1176
+ let shelfIndex = 0;
1177
+
1178
+ for ( const section of fixtureConfig.sections ) {
1179
+ const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
1180
+
1181
+ for ( let j = 0; j < section.sectionShelves; j++ ) {
1182
+ if ( shelfIndex >= fixtureConfig.shelfCount ) break;
1183
+
1184
+
1185
+ const shelfData = {
1186
+ 'clientId': fixtureConfig.clientId,
1187
+ 'storeName': layoutDoc.storeName,
1188
+ 'storeId': layoutDoc.storeId,
1189
+ 'planoId': layoutDoc.planoId,
1190
+ 'floorId': layoutDoc._id,
1191
+ 'fixtureId': createdFixture._id,
1192
+ 'shelfNumber': shelfIndex + 1,
1193
+ 'shelfOrder': 'LTR',
1194
+ 'shelfCapacity': fixtureConfig.productPerShelf,
1195
+ 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
1196
+ 'sectionZone': section.sectionId,
1197
+ };
1198
+
1199
+
1200
+ await fixtureShelfService.create( shelfData );
1201
+
1202
+ // console.log( 'Shelf Data:', createdShelf );
1203
+
1204
+ shelfIndex++;
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+
1210
+ for ( let index = 0; index < backFixtures.length; index++ ) {
1211
+ const fixture = rightFixtures[index];
1212
+
1213
+ const fixtureData = {
1214
+ 'clientId': layoutDoc.clientId,
1215
+ 'storeName': layoutDoc.storeName,
1216
+ 'storeId': layoutDoc.storeId,
1217
+ 'planoId': layoutDoc.planoId,
1218
+ 'floorId': layoutDoc._id,
1219
+ 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1220
+ 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
1221
+ 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1222
+ 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
1223
+ 'fixtureCode': fixture?.['Fixture ID'],
1224
+ 'fixtureCapacity': fixture?.['Capacity'],
1225
+ 'fixtureType': 'wall',
1226
+ 'fixtureHeight': {
1227
+ 'value': 0,
1228
+ 'unit': 'mm',
1229
+ },
1230
+ 'fixtureLength': {
1231
+ 'value': constantFixtureWidth,
1232
+ 'unit': 'mm',
1233
+ },
1234
+ 'fixtureWidth': {
1235
+ 'value': constantFixtureLength,
1236
+ 'unit': 'mm',
1237
+ },
1238
+ 'associatedElementType': 'wall',
1239
+ 'associatedElementNumber': 2,
1240
+ 'relativePosition': {
1241
+ 'x': roundToTwo( ( finalXDistance - ( constantFixtureWidth/mmToFeet ) ) ),
1242
+ 'y': roundToTwo( ( ( index * ( ( constantFixtureLength/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantFixtureWidth/mmToFeet ) ) ),
1243
+ 'unit': 'ft',
1244
+ },
1245
+ 'fixtureNumber': fixtureCounter++,
1246
+ 'detailedFixtureLength': {
1247
+ 'value': constantDetailedFixtureLength,
1248
+ 'unit': 'mm',
1249
+ },
1250
+ 'detailedFixtureWidth': {
1251
+ 'value': constantDetailedFixtureWidth,
1252
+ 'unit': 'mm',
1253
+ },
1254
+ 'relativeDetailedPosition': {
1255
+ 'x': roundToTwo( ( finalXDistance - ( constantDetailedFixtureLength/mmToFeet ) ) ),
1256
+ 'y': roundToTwo( ( ( index * ( ( constantDetailedFixtureWidth/mmToFeet ) ) ) + ( ( leftFixtures.length ? 1 : 0 ) * constantDetailedFixtureWidth/mmToFeet ) ) ),
1257
+ 'unit': 'ft',
1258
+ },
1259
+ 'productResolutionLevel': 'L2',
1260
+ };
1261
+
1262
+ const createdFixture = await storeFixtureService.create( fixtureData );
1263
+
1264
+ // console.log( 'Fixture Data', fixtureData );
1265
+
1266
+ const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
1267
+
1268
+ for ( let i = 0; i < vms?.length; i++ ) {
1269
+ const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
1270
+
1271
+ if ( vmTemplate ) {
1272
+ const vmData = {
1273
+ 'clientId': layoutDoc.clientId,
1274
+ 'storeName': layoutDoc.storeName,
1275
+ 'storeId': layoutDoc.storeId,
1276
+ 'planoId': layoutDoc.planoId,
1277
+ 'floorId': layoutDoc._id,
1278
+ 'type': 'vm',
1279
+ 'fixtureId': createdFixture._id,
1280
+ 'productId': vmTemplate._id,
1281
+ };
1282
+
1283
+ await planoMappingService.create( vmData );
1284
+ }
1285
+ }
1286
+
1287
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
1288
+
1289
+ if ( fixtureConfig ) {
1290
+ let shelfIndex = 0;
1291
+
1292
+ for ( const section of fixtureConfig.sections ) {
1293
+ const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
1294
+ for ( let j = 0; j < section.sectionShelves; j++ ) {
1295
+ if ( shelfIndex >= fixtureConfig.shelfCount ) break;
1296
+
1297
+ const shelfData = {
1298
+ 'clientId': fixtureConfig.clientId,
1299
+ 'storeName': layoutDoc.storeName,
1300
+ 'storeId': layoutDoc.storeId,
1301
+ 'planoId': layoutDoc.planoId,
1302
+ 'floorId': layoutDoc._id,
1303
+ 'fixtureId': createdFixture._id,
1304
+ 'shelfNumber': shelfIndex + 1,
1305
+ 'shelfOrder': 'LTR',
1306
+ 'shelfCapacity': fixtureConfig.productPerShelf,
1307
+ 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
1308
+ 'sectionZone': section.sectionId,
1309
+ };
1310
+
1311
+ const createdShelf = await fixtureShelfService.create( shelfData );
1312
+
1313
+ // console.log( 'Shelf Data:', createdShelf );
1314
+
1315
+ shelfIndex++;
1316
+ }
1317
+ }
1318
+ }
1319
+ }
1320
+
1321
+ for ( let index = 0; index < rightFixtures.length; index++ ) {
1322
+ const fixture = rightFixtures[index];
1323
+
1324
+ const fixtureData = {
1325
+ 'clientId': layoutDoc.clientId,
1326
+ 'storeName': layoutDoc.storeName,
1327
+ 'storeId': layoutDoc.storeId,
1328
+ 'planoId': layoutDoc.planoId,
1329
+ 'floorId': layoutDoc._id,
1330
+ 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1331
+ 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
1332
+ 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1333
+ 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
1334
+ 'fixtureCode': fixture?.['Fixture ID'],
1335
+ 'fixtureCapacity': fixture?.['Capacity'],
1336
+ 'fixtureType': 'wall',
1337
+ 'fixtureHeight': {
1338
+ 'value': 0,
1339
+ 'unit': 'mm',
1340
+ },
1341
+ 'fixtureLength': {
1342
+ 'value': constantFixtureLength,
1343
+ 'unit': 'mm',
1344
+ },
1345
+ 'fixtureWidth': {
1346
+ 'value': constantFixtureWidth,
1347
+ 'unit': 'mm',
1348
+ },
1349
+ 'associatedElementType': 'wall',
1350
+ 'associatedElementNumber': 3,
1351
+ 'relativePosition': {
1352
+ 'x': roundToTwo( ( index * ( constantFixtureLength / mmToFeet ) ) ),
1353
+ 'y': roundToTwo( ( finalYDistance - ( constantFixtureWidth / mmToFeet ) ) ),
1354
+ 'unit': 'ft',
1355
+ },
1356
+ 'fixtureNumber': fixtureCounter++,
1357
+ 'detailedFixtureLength': {
1358
+ 'value': constantDetailedFixtureLength,
1359
+ 'unit': 'mm',
1360
+ },
1361
+ 'detailedFixtureWidth': {
1362
+ 'value': constantDetailedFixtureWidth,
1363
+ 'unit': 'mm',
1364
+ },
1365
+ 'relativeDetailedPosition': {
1366
+ 'x': roundToTwo( ( index * ( constantDetailedFixtureLength / mmToFeet ) ) ),
1367
+ 'y': roundToTwo( ( finalYDetailedDistance - ( constantDetailedFixtureWidth / mmToFeet ) ) ),
1368
+ 'unit': 'ft',
1369
+ },
1370
+ 'productResolutionLevel': 'L2',
1371
+ };
1372
+
1373
+ const createdFixture = await storeFixtureService.create( fixtureData );
1374
+
1375
+ // console.log( 'Fixture Data', fixtureData );
1376
+
1377
+ const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
1378
+
1379
+ for ( let i = 0; i < vms?.length; i++ ) {
1380
+ const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
1381
+
1382
+ if ( vmTemplate ) {
1383
+ const vmData = {
1384
+ 'clientId': layoutDoc.clientId,
1385
+ 'storeName': layoutDoc.storeName,
1386
+ 'storeId': layoutDoc.storeId,
1387
+ 'planoId': layoutDoc.planoId,
1388
+ 'floorId': layoutDoc._id,
1389
+ 'type': 'vm',
1390
+ 'fixtureId': createdFixture._id,
1391
+ 'productId': vmTemplate._id,
1392
+ };
1393
+
1394
+ await planoMappingService.create( vmData );
1395
+ }
1396
+ }
1397
+
1398
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
1399
+
1400
+ if ( fixtureConfig ) {
1401
+ let shelfIndex = 0;
1402
+
1403
+ for ( const section of fixtureConfig.sections ) {
1404
+ const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
1405
+ for ( let j = 0; j < section.sectionShelves; j++ ) {
1406
+ if ( shelfIndex >= fixtureConfig.shelfCount ) break;
1407
+
1408
+ const shelfData = {
1409
+ 'clientId': fixtureConfig.clientId,
1410
+ 'storeName': layoutDoc.storeName,
1411
+ 'storeId': layoutDoc.storeId,
1412
+ 'planoId': layoutDoc.planoId,
1413
+ 'floorId': layoutDoc._id,
1414
+ 'fixtureId': createdFixture._id,
1415
+ 'shelfNumber': shelfIndex + 1,
1416
+ 'shelfOrder': 'LTR',
1417
+ 'shelfCapacity': fixtureConfig.productPerShelf,
1418
+ 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
1419
+ 'sectionZone': section.sectionId,
1420
+ };
1421
+
1422
+ await fixtureShelfService.create( shelfData );
1423
+
1424
+ // console.log( 'Shelf Data:', createdShelf );
1425
+
1426
+ shelfIndex++;
1427
+ }
1428
+ }
1429
+ }
1430
+ }
1431
+
1432
+ for ( let index = 0; index < floorFixtures.length; index++ ) {
1433
+ const fixture = floorFixtures[index];
1434
+
1435
+ const centerRow = Math.floor( totalRows / 2 );
1436
+
1437
+ const startingX = ( finalXDistance / 2 ) - ( Math.floor( maxFixturesPerRow / 2 ) * ( constantFixtureLength / mmToFeet ) );
1438
+ const detailedStartingX = ( finalXDetailedDistance / 2 ) - ( Math.floor( maxFixturesPerRow / 2 ) * ( constantDetailedFixtureLength / mmToFeet ) );
1439
+
1440
+ const startingY = ( finalYDistance / 2 ) - ( centerRow * ( constantFixtureWidth / mmToFeet ) );
1441
+ const detailedStartingY = ( finalYDetailedDistance / 2 ) - ( centerRow * ( constantDetailedFixtureWidth / mmToFeet ) );
1442
+
1443
+ const rowIndex = Math.floor( index / maxFixturesPerRow );
1444
+ const colIndex = index % maxFixturesPerRow;
1445
+
1446
+ const xPos = roundToTwo( ( startingX + colIndex * ( constantFixtureLength / mmToFeet ) ) );
1447
+ const yPos = roundToTwo( ( startingY + rowIndex * ( constantFixtureWidth / mmToFeet ) ) );
1448
+
1449
+ const detailedXPos = roundToTwo( ( detailedStartingX + colIndex * ( constantDetailedFixtureLength / mmToFeet ) ) );
1450
+ const detailedYPos = roundToTwo( ( detailedStartingY + rowIndex * ( constantDetailedFixtureWidth / mmToFeet ) ) );
1451
+
1452
+ const fixtureData = {
1453
+ 'clientId': layoutDoc.clientId,
1454
+ 'storeName': layoutDoc.storeName,
1455
+ 'storeId': layoutDoc.storeId,
1456
+ 'planoId': layoutDoc.planoId,
1457
+ 'floorId': layoutDoc._id,
1458
+ 'fixtureName': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1459
+ 'fixtureCategory': fixture?.['Fixture Category'] ? fixture?.['Fixture Category'] : 'nil',
1460
+ 'fixtureBrandCategory': fixture?.['Brand-Category'] ? fixture?.['Brand-Category'] : 'nil',
1461
+ 'fixtureBrandSubCategory': fixture?.['Brand - Sub Category'] ? fixture?.['Brand - Sub Category'] : 'nil',
1462
+ 'fixtureCode': fixture?.['Fixture ID'],
1463
+ 'fixtureCapacity': fixture?.['Capacity'],
1464
+ 'fixtureType': 'floor',
1465
+ 'fixtureHeight': {
1466
+ 'value': 0,
1467
+ 'unit': 'mm',
1468
+ },
1469
+ 'fixtureLength': {
1470
+ 'value': constantFixtureLength,
1471
+ 'unit': 'mm',
1472
+ },
1473
+ 'fixtureWidth': {
1474
+ 'value': constantFixtureWidth,
1475
+ 'unit': 'mm',
1476
+ },
1477
+ 'relativePosition': {
1478
+ 'x': xPos,
1479
+ 'y': yPos,
1480
+ 'unit': 'ft',
1481
+ },
1482
+ 'fixtureNumber': fixtureCounter++,
1483
+ 'detailedFixtureLength': {
1484
+ 'value': constantDetailedFixtureLength,
1485
+ 'unit': 'mm',
1486
+ },
1487
+ 'detailedFixtureWidth': {
1488
+ 'value': constantDetailedFixtureWidth,
1489
+ 'unit': 'mm',
1490
+ },
1491
+ 'relativeDetailedPosition': {
1492
+ 'x': detailedXPos,
1493
+ 'y': detailedYPos,
1494
+ 'unit': 'ft',
1495
+ },
1496
+ 'productResolutionLevel': 'L2',
1497
+ };
1498
+
1499
+ const createdFixture = await storeFixtureService.create( fixtureData );
1500
+ // console.log( 'Fixture Data', fixtureData );
1501
+
1502
+ const vms = typeof fixture?.['VM Template ID'] === 'string' ? fixture?.['VM Template ID']?.split( ', ' ).map( ( item ) => item.trim() ) : [];
1503
+
1504
+ for ( let i = 0; i < vms?.length; i++ ) {
1505
+ const vmTemplate = await planoProductService.findOne( { productId: vms[i] } );
1506
+
1507
+ if ( vmTemplate ) {
1508
+ const vmData = {
1509
+ 'clientId': layoutDoc.clientId,
1510
+ 'storeName': layoutDoc.storeName,
1511
+ 'storeId': layoutDoc.storeId,
1512
+ 'planoId': layoutDoc.planoId,
1513
+ 'floorId': layoutDoc._id,
1514
+ 'type': 'vm',
1515
+ 'fixtureId': createdFixture._id,
1516
+ 'productId': vmTemplate._id,
1517
+ };
1518
+
1519
+ await planoMappingService.create( vmData );
1520
+ }
1521
+ }
1522
+
1523
+
1524
+ const fixtureConfig = await fixtureConfigService.findOne( { fixtureCode: fixture?.['Fixture ID'] } );
1525
+
1526
+ if ( fixtureConfig ) {
1527
+ let shelfIndex = 0;
1528
+
1529
+ for ( const section of fixtureConfig.sections ) {
1530
+ const storeCategory = fixture.categories.find( ( cat ) => cat.Zone === section.sectionId );
1531
+ for ( let j = 0; j < section.sectionShelves; j++ ) {
1532
+ if ( shelfIndex >= fixtureConfig.shelfCount ) break;
1533
+
1534
+ const shelfData = {
1535
+ 'clientId': fixtureConfig.clientId,
1536
+ 'storeName': layoutDoc.storeName,
1537
+ 'storeId': layoutDoc.storeId,
1538
+ 'planoId': layoutDoc.planoId,
1539
+ 'floorId': layoutDoc._id,
1540
+ 'fixtureId': createdFixture._id,
1541
+ 'shelfNumber': shelfIndex + 1,
1542
+ 'shelfOrder': 'LTR',
1543
+ 'shelfCapacity': fixtureConfig.productPerShelf,
1544
+ 'sectionName': storeCategory ? storeCategory?.['Allocation'] : 'Unknown',
1545
+ 'sectionZone': section.sectionId,
1546
+ };
1547
+
1548
+ await fixtureShelfService.create( shelfData );
1549
+
1550
+ // console.log( 'Shelf Data:', createdShelf );
1551
+
1552
+ shelfIndex++;
1553
+ }
1554
+ }
1555
+ }
1556
+ }
1557
+ }
1558
+
1559
+
1560
+ return res.sendSuccess( 'Updated successfully' );
1561
+ } catch ( e ) {
1562
+ logger.error( { functionName: 'createFixturesShelves', error: e } );
1563
+ return res.sendError( e.message || 'Internal Server Error', 500 );
1564
+ }
1565
+ }
1566
+
1567
+ export async function createVmData( req, res ) {
1568
+ try {
1569
+ if ( !req.files.file ) {
1570
+ return res.sendError( 'Invalid or missing Excel file', 400 );
1571
+ }
1572
+
1573
+ const workbook = xlsx.read( req.files.file.data, { type: 'buffer' } );
1574
+ const sheetName = 'VM Library';
1575
+ if ( !workbook.Sheets[sheetName] ) {
1576
+ return res.sendError( `Sheet "${sheetName}" not found`, 400 );
1577
+ }
1578
+
1579
+ const inputArray = xlsx.utils.sheet_to_json( workbook.Sheets[sheetName] );
1580
+
1581
+
1582
+ const transformedData = inputArray.map( ( item ) => {
1583
+ console.log( item );
1584
+
1585
+ return {
1586
+ 'clientId': '11',
1587
+ 'productId': item['VM ID'],
1588
+ 'type': 'vm',
1589
+ 'productName': item['VM Categories '],
1590
+ 'productHeight': {
1591
+ 'value': typeof item?.['VM Height mm'] === 'number' ? item?.['VM Height mm'] : 0,
1592
+ 'unit': 'mm',
1593
+ },
1594
+ 'productWidth': {
1595
+ 'value': typeof item?.['VM Width mm'] === 'number' ? item?.['VM Width mm'] : 0,
1596
+ 'unit': 'mm',
1597
+ },
1598
+ 'startYPosition': typeof item?.['StartPosition '] === 'number' ? item?.['StartPosition '] : 0,
1599
+ 'endYPosition': typeof item?.['End Position'] === 'number' ? item?.['End Position'] : 0,
1600
+ 'xZone': item?.['Start Zone '],
1601
+ };
1602
+ } );
1603
+
1604
+ await planoProductService.insertMany( transformedData );
1605
+ return res.sendSuccess( { message: 'Data inserted successfully', length: transformedData.length } );
1606
+ } catch ( e ) {
1607
+ logger.error( { functionName: 'transformDataAPI', error: e } );
1608
+ return res.sendError( e.message || 'Internal Server Error', 500 );
1609
+ }
1610
+ }