tango-app-api-store-builder 1.0.0-beta-11 → 1.0.0-beta-12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-store-builder",
3
- "version": "1.0.0-beta-11",
3
+ "version": "1.0.0-beta-12",
4
4
  "description": "storeBuilder",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "joi": "^17.13.3",
24
24
  "mongodb": "^6.12.0",
25
25
  "nodemon": "^3.1.9",
26
- "tango-api-schema": "^2.2.28",
26
+ "tango-api-schema": "^2.2.31",
27
27
  "tango-app-api-middleware": "^3.1.48",
28
28
  "winston": "^3.17.0",
29
29
  "winston-daily-rotate-file": "^5.0.0"
@@ -375,6 +375,33 @@ export async function getStoreDetails( req, res ) {
375
375
  }
376
376
 
377
377
  export async function storeLayout( req, res ) {
378
+ try {
379
+ const planoIds = req.body.id.map( ( id ) => new mongoose.Types.ObjectId( id ) );
380
+
381
+ const planograms = await planoService.find( { _id: { $in: planoIds } }, { storeId: 1, storeName: 1, planoId: '$_id' } );
382
+
383
+ if ( !planograms?.length ) {
384
+ return res.sendError( 'No data found', 204 );
385
+ }
386
+
387
+ const storeLayout = await Promise.all(
388
+ planograms.map( async ( planogram ) => {
389
+ const floors = await storeBuilderService.find( { planoId: planogram._id }, { floorName: 1, layoutPolygon: 1 } );
390
+ return {
391
+ ...planogram.toObject(),
392
+ floors,
393
+ };
394
+ } ),
395
+ );
396
+
397
+ return res.sendSuccess( storeLayout );
398
+ } catch ( e ) {
399
+ logger.error( { functionName: 'storeLayoutv1', error: e, message: req.body } );
400
+ return res.sendError( e, 500 );
401
+ }
402
+ }
403
+
404
+ export async function storeFixtures( req, res ) {
378
405
  try {
379
406
  let idList = req.body.id.map( ( item ) => new mongoose.Types.ObjectId( item ) );
380
407
  let query = { _id: { $in: req.body.id } };
@@ -502,6 +529,106 @@ export async function storeLayout( req, res ) {
502
529
  }
503
530
  }
504
531
 
532
+ export async function storeFixturesv1( req, res ) {
533
+ try {
534
+ const planoIds = req.body.id.map( ( id ) => new mongoose.Types.ObjectId( id ) );
535
+
536
+ const planograms = await planoService.find(
537
+ { _id: { $in: planoIds } },
538
+ { storeId: 1, storeName: 1, planoId: '$_id', productResolutionLevel: 1 },
539
+ );
540
+
541
+ if ( !planograms?.length ) return res.sendError( 'No data found', 204 );
542
+
543
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
544
+
545
+ const storeLayout = await Promise.all(
546
+ planograms.map( async ( planogram ) => {
547
+ const floors = await storeBuilderService.find(
548
+ { planoId: planogram._id },
549
+ { floorName: 1, layoutPolygon: 1, planoId: 1 },
550
+ );
551
+
552
+ const floorsWithFixtures = await Promise.all(
553
+ floors.map( async ( floor ) => {
554
+ const layoutPolygonWithFixtures = await Promise.all(
555
+ floor.layoutPolygon.map( async ( element ) => {
556
+ const fixtures = await storeFixtureService.find( {
557
+ floorId: floor._id,
558
+ associatedElementType: element.elementType,
559
+ associatedElementNumber: element.elementNumber,
560
+ fixtureType: 'wall',
561
+ } );
562
+
563
+ const fixturesWithStatus = await Promise.all(
564
+ fixtures.map( async ( fixture ) => {
565
+ const productCount = await planoMappingService.count( { fixtureId: fixture._id } );
566
+
567
+ const complianceCount = await planoComplianceService.count( {
568
+ fixtureId: fixture._id,
569
+ compliance: 'proper',
570
+ date: currentDate,
571
+ } );
572
+
573
+ return {
574
+ ...fixture.toObject(),
575
+ status: productCount === 0 ? '' : complianceCount === productCount ? 'complete' : 'incomplete',
576
+ };
577
+ } ),
578
+ );
579
+
580
+ return {
581
+ ...element,
582
+ fixtures: fixturesWithStatus,
583
+ };
584
+ } ),
585
+ );
586
+
587
+ const centerFixtures = await storeFixtureService.find( {
588
+ floorId: floor._id,
589
+ fixtureType: 'floor',
590
+ } );
591
+
592
+ const centerFixturesWithStatus = await Promise.all(
593
+ centerFixtures.map( async ( fixture ) => {
594
+ const productCount = await planoMappingService.count( { fixtureId: fixture._id } );
595
+
596
+ const complianceCount = await planoComplianceService.count( {
597
+ fixtureId: fixture._id,
598
+ compliance: 'proper',
599
+ date: currentDate,
600
+ } );
601
+
602
+ return {
603
+ ...fixture.toObject(),
604
+ status: productCount === 0 ? '' : complianceCount === productCount ? 'complete' : 'incomplete',
605
+ };
606
+ } ),
607
+ );
608
+
609
+ return {
610
+ ...floor.toObject(),
611
+ layoutPolygon: layoutPolygonWithFixtures,
612
+ centerFixture: centerFixturesWithStatus,
613
+ };
614
+ } ),
615
+ );
616
+
617
+ return {
618
+ ...planogram.toObject(),
619
+ floors: floorsWithFixtures,
620
+ };
621
+ } ),
622
+ );
623
+
624
+ return res.sendSuccess( storeLayout );
625
+ } catch ( e ) {
626
+ logger.error( { functionName: 'storeFixturesv1', error: e, message: req.body } );
627
+ return res.sendError( e, 500 );
628
+ }
629
+ }
630
+
631
+
505
632
  export async function deleteStoreLayout( req, res ) {
506
633
  try {
507
634
  let getDetails = await planoService.findOne( { _id: req.params.id } );
@@ -630,6 +757,81 @@ export async function fixtureShelfProduct( req, res ) {
630
757
  }
631
758
  }
632
759
 
760
+ export async function fixtureShelfProductv1( req, res ) {
761
+ try {
762
+ const { planoId, fixtureId } = req.body;
763
+
764
+ const [ planogram, fixture ] = await Promise.all( [
765
+ planoService.findOne(
766
+ { _id: new mongoose.Types.ObjectId( planoId ) },
767
+ { storeId: 1, storeName: 1, planoId: '$_id', productResolutionLevel: 1 },
768
+ ),
769
+ storeFixtureService.findOne( {
770
+ _id: new mongoose.Types.ObjectId( fixtureId ),
771
+ } ),
772
+ ] );
773
+
774
+ if ( !planogram ) return res.sendError( 'Planogram not found', 204 );
775
+ if ( !fixture ) return res.sendError( 'Fixture not found', 204 );
776
+
777
+ const fixtureShelves = await fixtureShelfService.find( {
778
+ fixtureId: new mongoose.Types.ObjectId( fixtureId ),
779
+ } );
780
+
781
+ if ( !fixtureShelves.length ) return res.sendError( 'No shelves found for the fixture', 204 );
782
+
783
+ const shelfProducts = await Promise.all(
784
+ fixtureShelves.map( async ( shelf ) => {
785
+ const productMappings = await planoMappingService.find( { shelfId: shelf._id } );
786
+
787
+ if ( !productMappings.length ) {
788
+ return { ...shelf.toObject(), products: [] };
789
+ }
790
+
791
+ const productIds = productMappings.map( ( mapping ) => mapping.productId );
792
+ const products = await planoProductService.find( { _id: { $in: productIds } } );
793
+ const productMap = new Map( products.map( ( product ) => [ product._id.toString(), product.toObject() ] ) );
794
+
795
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
796
+
797
+ const productDetails = await Promise.all(
798
+ productMappings.map( async ( mapping ) => {
799
+ const productData = productMap.get( mapping.productId.toString() );
800
+ if ( !productData ) {
801
+ return { ...mapping.toObject(), status: '' };
802
+ }
803
+
804
+ const mappingCompliance = await planoComplianceService.findOne( {
805
+ shelfId: mapping.shelfId,
806
+ shelfPosition: mapping.shelfPosition,
807
+ date: currentDate,
808
+ } );
809
+
810
+ const status = mappingCompliance ? mappingCompliance.compliance : 'missing';
811
+
812
+ return {
813
+ ...mapping.toObject(),
814
+ ...productData,
815
+ status,
816
+ };
817
+ } ),
818
+ );
819
+
820
+ return {
821
+ ...shelf.toObject(),
822
+ products: productDetails,
823
+ };
824
+ } ),
825
+ );
826
+
827
+ return res.sendSuccess( { ...fixture.toObject(), shelves: shelfProducts } );
828
+ } catch ( e ) {
829
+ logger.error( { functionName: 'fixtureShelfProductv1', error: e, message: req.body } );
830
+ return res.sendError( e, 500 );
831
+ }
832
+ }
833
+
834
+
633
835
  export async function scan( req, res ) {
634
836
  try {
635
837
  let shelfId;
@@ -731,7 +933,6 @@ export async function scan( req, res ) {
731
933
  query = { ...query, rfId: req.body.rfId };
732
934
 
733
935
  let planoProductDetails = await planoMappingService.findOne( query );
734
- console.log( query, 'query' );
735
936
  // let data = {
736
937
  // ...( planoProductDetails ) ? { ...planoProductDetails._doc } : { planoId: req.body?.planoId, floorId: req.body?.floorId, fixtureId: req.body?.fixtureId, shelfId: shelfId, clientId: planoDetails.clientId, storeName: planoDetails.storeName, storeId: planoDetails.storeId, shelfPosition: req.body?.shelfPosition },
737
938
  // rfId: req.body.rfId,
@@ -764,3 +965,90 @@ export async function scan( req, res ) {
764
965
  return res.sendError( e, 500 );
765
966
  }
766
967
  }
968
+
969
+ export async function scanv1( req, res ) {
970
+ try {
971
+ const planogram = await planoService.findOne(
972
+ { _id: new mongoose.Types.ObjectId( req.body.planoId ) },
973
+ { storeId: 1, storeName: 1, planoId: '$_id', productResolutionLevel: 1 },
974
+ );
975
+
976
+ if ( !planogram ) return res.sendError( 'No data found', 204 );
977
+
978
+ if ( planogram.productResolutionLevel === 'L4' || planogram.productResolutionLevel === 'L5' ) {
979
+ if ( !req.body.floorId ) return res.sendError( 'Floor id is required', 400 );
980
+
981
+ if ( !req.body.rfId ) return res.sendError( 'RFID is required', 400 );
982
+
983
+ if ( !req.body.fixtureId ) return res.sendError( 'Fixture id is required', 400 );
984
+
985
+ if ( !req.body.shelfId && req.body.rfId ) {
986
+ const shelf = await fixtureShelfService.findOne( { planoId: req.body.planoId, floorId: req.body.floorId, fixtureId: req.body.fixtureId, rfId: req.body.rfId } );
987
+ if ( !shelf ) return res.sendError( 'No matching shelf for the rfId', 400 );
988
+ return res.sendSuccess( shelf.toObject()._id );
989
+ }
990
+
991
+ if ( !req.body.shelfId ) return res.sendError( 'Shelf id is required', 400 );
992
+
993
+ if ( !req.body.shelfPosition ) return res.sendError( 'Shelf position is required', 400 );
994
+
995
+ const mappingQuery = {
996
+ planoId: req.body.planoId,
997
+ floorId: req.body.floorId,
998
+ fixtureId: req.body.fixtureId,
999
+ shelfId: req.body.shelfId,
1000
+ shelfPosition: req.body.shelfPosition,
1001
+ };
1002
+
1003
+ const productMapping = await planoMappingService.findOne( mappingQuery );
1004
+
1005
+ if ( !productMapping ) return res.sendError( 'No product mapped for the spot', 400 );
1006
+
1007
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
1008
+
1009
+ if ( productMapping.toObject().rfId !== req.body.rfId ) {
1010
+ if ( planogram.productResolutionLevel === 'L4' ) {
1011
+ const complianceData = { ...productMapping.toObject(), compliance: 'misplaced' };
1012
+ delete complianceData._id;
1013
+
1014
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1015
+ return res.sendSuccess( false );
1016
+ } else if ( planogram.productResolutionLevel === 'L5' ) {
1017
+ const misplacedQuery = {
1018
+ planoId: req.body.planoId,
1019
+ floorId: req.body.floorId,
1020
+ fixtureId: req.body.fixtureId,
1021
+ rfId: req.body.rfId,
1022
+ };
1023
+ const misplacedProductMapping = await planoMappingService.findOne( misplacedQuery );
1024
+
1025
+ if ( productMapping.toObject()?.category === misplacedProductMapping.toObject()?.category ) {
1026
+ const complianceData = { ...productMapping.toObject(), compliance: 'proper' };
1027
+ delete complianceData._id;
1028
+
1029
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1030
+ return res.sendSuccess( true );
1031
+ } else {
1032
+ const complianceData = { ...productMapping.toObject(), compliance: 'misplaced' };
1033
+ delete complianceData._id;
1034
+
1035
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1036
+ return res.sendSuccess( false );
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ const complianceData = { ...productMapping.toObject(), compliance: 'proper' };
1042
+ delete complianceData._id;
1043
+
1044
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1045
+
1046
+ return res.sendSuccess( true );
1047
+ } else {
1048
+ return res.sendError( 'Incorrect resolution level', 400 );
1049
+ }
1050
+ } catch ( e ) {
1051
+ logger.error( { functionName: 'scanv1', error: e, message: req.body } );
1052
+ return res.sendError( e, 500 );
1053
+ }
1054
+ }
@@ -52,6 +52,16 @@ export const storeList = {
52
52
  body: storeListSchema,
53
53
  };
54
54
 
55
+ export const fixtureShelfProductSchema = joi.object( {
56
+ fixtureId: joi.string().required(),
57
+ floorId: joi.string().required(),
58
+ planoId: joi.string().required(),
59
+ } );
60
+
61
+ export const fixtureShelfProduct = {
62
+ body: fixtureShelfProductSchema,
63
+ };
64
+
55
65
  export const storeDetailSchema = joi.object( {
56
66
  storeId: joi.string().required(),
57
67
  clientId: joi.string().required(),
@@ -12,13 +12,17 @@ storeBuilderRouter
12
12
  .post( '/updateFloor', isAllowedSessionHandler, validate( validateDtos.updateFloor ), storeBuilderController.updateFloor )
13
13
  .post( '/uploadBulkStore', isAllowedSessionHandler, storeBuilderController.uploadBulkStore )
14
14
  .post( '/uploadFile', isAllowedSessionHandler, storeBuilderController.uploadFile )
15
- .post( '/storeLayout', isAllowedSessionHandler, validate( validateDtos.storeList ), storeBuilderController.storeLayout )
15
+ // .post( '/storeLayout', validate( validateDtos.storeList ), storeBuilderController.storeFixtures )
16
16
  .post( '/storeDetails', isAllowedSessionHandler, validate( validateDtos.storeDetails ), storeBuilderController.getStoreDetails )
17
17
  .delete( '/deleteStoreLayout/:id', isAllowedSessionHandler, validate( validateDtos.deleteStoreLayout ), storeBuilderController.deleteStoreLayout )
18
18
  .post( '/removeFile', isAllowedSessionHandler, storeBuilderController.deleteFile )
19
19
  .post( '/deleteFloor', isAllowedSessionHandler, storeBuilderController.deleteFloor )
20
20
  .post( '/updateStatus', isAllowedSessionHandler, validate( validateDtos.updateStatus ), storeBuilderController.updateStatus )
21
- .post( '/FixtureShelfDetails', isAllowedSessionHandler, storeBuilderController.fixtureShelfProduct )
22
- .post( '/scan', isAllowedSessionHandler, storeBuilderController.scan );
21
+ // .post( '/FixtureShelfDetails', storeBuilderController.fixtureShelfProduct )
22
+ // .post( '/scan', storeBuilderController.scan )
23
+ .post( '/storeLayout', validate( validateDtos.storeList ), storeBuilderController.storeLayout )
24
+ .post( '/storeFixtures', validate( validateDtos.storeList ), storeBuilderController.storeFixturesv1 )
25
+ .post( '/FixtureShelfDetails', validate( validateDtos.fixtureShelfProduct ), storeBuilderController.fixtureShelfProductv1 )
26
+ .post( '/scan', storeBuilderController.scanv1 );
23
27
 
24
28
 
@@ -18,3 +18,7 @@ export async function aggregate( query ) {
18
18
  export async function updateOne( query, record ) {
19
19
  return model.planoMappingModel.updateOne( query, { $set: record } );
20
20
  }
21
+
22
+ export async function count( query ) {
23
+ return model.planoMappingModel.countDocuments( query );
24
+ }