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

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-13",
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,139 @@ 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
+ if ( planogram.toObject().productResolutionLevel === 'L3' ) {
784
+ const groupedShelves = await ( async () => {
785
+ const shelfProducts = await Promise.all(
786
+ fixtureShelves.map( async ( shelf ) => {
787
+ const productMappings = await planoMappingService.find( { shelfId: shelf._id } );
788
+
789
+ if ( !productMappings.length ) {
790
+ return { ...shelf.toObject(), products: [] };
791
+ }
792
+
793
+ const productIds = productMappings.map( ( mapping ) => mapping.productId );
794
+ const products = await planoProductService.find( { _id: { $in: productIds } } );
795
+ const productMap = new Map( products.map( ( product ) => [ product._id.toString(), product.toObject() ] ) );
796
+
797
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
798
+
799
+ const productDetails = await Promise.all(
800
+ productMappings.map( async ( mapping ) => {
801
+ const productData = productMap.get( mapping.productId.toString() );
802
+ if ( !productData ) {
803
+ return { ...mapping.toObject(), status: '' };
804
+ }
805
+
806
+ const mappingCompliance = await planoComplianceService.findOne( {
807
+ shelfId: mapping.shelfId,
808
+ shelfPosition: mapping.shelfPosition,
809
+ date: currentDate,
810
+ } );
811
+
812
+ const status = mappingCompliance ? mappingCompliance.compliance : 'missing';
813
+
814
+ return {
815
+ ...mapping.toObject(),
816
+ ...productData,
817
+ status,
818
+ };
819
+ } ),
820
+ );
821
+
822
+ return {
823
+ ...shelf.toObject(),
824
+ products: productDetails,
825
+ };
826
+ } ),
827
+ );
828
+
829
+ return shelfProducts.reduce( ( acc, shelf ) => {
830
+ const sectionName = shelf.sectionName || 'Unknown';
831
+ if ( !acc[sectionName] ) {
832
+ acc[sectionName] = [];
833
+ }
834
+ acc[sectionName].push( shelf );
835
+ return acc;
836
+ }, {} );
837
+ } )();
838
+ return res.sendSuccess( { ...fixture.toObject(), categories: groupedShelves } );
839
+ } else if ( planogram.toObject().productResolutionLevel === 'L4' || planogram.toObject().productResolutionLevel === 'L5' ) {
840
+ const shelfProducts = await Promise.all(
841
+ fixtureShelves.map( async ( shelf ) => {
842
+ const productMappings = await planoMappingService.find( { shelfId: shelf._id } );
843
+
844
+ if ( !productMappings.length ) {
845
+ return { ...shelf.toObject(), products: [] };
846
+ }
847
+
848
+ const productIds = productMappings.map( ( mapping ) => mapping.productId );
849
+ const products = await planoProductService.find( { _id: { $in: productIds } } );
850
+ const productMap = new Map( products.map( ( product ) => [ product._id.toString(), product.toObject() ] ) );
851
+
852
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
853
+
854
+ const productDetails = await Promise.all(
855
+ productMappings.map( async ( mapping ) => {
856
+ const productData = productMap.get( mapping.productId.toString() );
857
+ if ( !productData ) {
858
+ return { ...mapping.toObject(), status: '' };
859
+ }
860
+
861
+ const mappingCompliance = await planoComplianceService.findOne( {
862
+ shelfId: mapping.shelfId,
863
+ shelfPosition: mapping.shelfPosition,
864
+ date: currentDate,
865
+ } );
866
+
867
+ const status = mappingCompliance ? mappingCompliance.compliance : 'missing';
868
+
869
+ return {
870
+ ...mapping.toObject(),
871
+ ...productData,
872
+ status,
873
+ };
874
+ } ),
875
+ );
876
+
877
+ return {
878
+ ...shelf.toObject(),
879
+ products: productDetails,
880
+ };
881
+ } ),
882
+ );
883
+
884
+ return res.sendSuccess( { ...fixture.toObject(), shelves: shelfProducts } );
885
+ }
886
+ } catch ( e ) {
887
+ logger.error( { functionName: 'fixtureShelfProductv1', error: e, message: req.body } );
888
+ return res.sendError( e, 500 );
889
+ }
890
+ }
891
+
892
+
633
893
  export async function scan( req, res ) {
634
894
  try {
635
895
  let shelfId;
@@ -731,7 +991,6 @@ export async function scan( req, res ) {
731
991
  query = { ...query, rfId: req.body.rfId };
732
992
 
733
993
  let planoProductDetails = await planoMappingService.findOne( query );
734
- console.log( query, 'query' );
735
994
  // let data = {
736
995
  // ...( 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
996
  // rfId: req.body.rfId,
@@ -764,3 +1023,158 @@ export async function scan( req, res ) {
764
1023
  return res.sendError( e, 500 );
765
1024
  }
766
1025
  }
1026
+
1027
+ export async function scanv1( req, res ) {
1028
+ try {
1029
+ const planogram = await planoService.findOne(
1030
+ { _id: new mongoose.Types.ObjectId( req.body.planoId ) },
1031
+ { storeId: 1, storeName: 1, planoId: '$_id', productResolutionLevel: 1 },
1032
+ );
1033
+
1034
+ if ( !planogram ) return res.sendError( 'No data found', 204 );
1035
+
1036
+ if ( planogram.productResolutionLevel === 'L3' ) {
1037
+ if ( !req.body.floorId ) return res.sendError( 'Floor id is required', 400 );
1038
+
1039
+ if ( !req.body.rfId ) return res.sendError( 'RFID is required', 400 );
1040
+
1041
+ if ( !req.body.fixtureId ) return res.sendError( 'Fixture id is required', 400 );
1042
+
1043
+ if ( !req.body.shelfId && req.body.rfId ) {
1044
+ const shelf = await fixtureShelfService.findOne( { planoId: req.body.planoId, floorId: req.body.floorId, fixtureId: req.body.fixtureId, rfId: req.body.rfId } );
1045
+ if ( !shelf ) return res.sendError( 'No matching shelf for the rfId', 400 );
1046
+ return res.sendSuccess( shelf.toObject()._id );
1047
+ }
1048
+
1049
+ if ( !req.body.shelfId ) return res.sendError( 'Shelf id is required', 400 );
1050
+
1051
+ const mappingQuery = {
1052
+ planoId: req.body.planoId,
1053
+ floorId: req.body.floorId,
1054
+ fixtureId: req.body.fixtureId,
1055
+ shelfId: req.body.shelfId,
1056
+ rfId: req.body.rfId,
1057
+ };
1058
+
1059
+ const productMapping = await planoMappingService.findOne( mappingQuery );
1060
+
1061
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
1062
+
1063
+ if ( !productMapping ) {
1064
+ const shelf = await fixtureShelfService.findOne( { _id: new mongoose.Types.ObjectId( req.body.shelfId ) } );
1065
+
1066
+ if ( !shelf ) {
1067
+ return res.sendError( 'Invalid shelf Id', 400 );
1068
+ }
1069
+ const misplacedQuery = {
1070
+ planoId: req.body.planoId,
1071
+ floorId: req.body.floorId,
1072
+ rfId: req.body.rfId,
1073
+ };
1074
+ const misplacedProductMapping = await planoMappingService.findOne( misplacedQuery );
1075
+
1076
+ if ( !misplacedProductMapping ) {
1077
+ return res.sendError( 'No product found for the RFID', 400 );
1078
+ }
1079
+
1080
+ let misplacedProductDetails = await planoProductService.findOne( { _id: misplacedProductMapping.toObject().productId } );
1081
+
1082
+ if ( shelf.toObject().sectionName === misplacedProductMapping.toObject().category ) {
1083
+ const complianceData = { ...misplacedProductMapping.toObject(), compliance: 'proper' };
1084
+ delete complianceData._id;
1085
+
1086
+ await planoComplianceService.updateOne( { ...misplacedQuery, date: currentDate }, complianceData );
1087
+ return res.sendSuccess( { data: { ...misplacedProductMapping.toObject(), ...( misplacedProductDetails ? misplacedProductDetails?.toObject(): {} ) }, status: true } );
1088
+ } else {
1089
+ const complianceData = { ...misplacedProductMapping.toObject(), compliance: 'misplaced' };
1090
+ delete complianceData._id;
1091
+
1092
+ await planoComplianceService.updateOne( { ...misplacedQuery, date: currentDate }, complianceData );
1093
+ return res.sendSuccess( { data: { ...misplacedProductMapping.toObject(), ...( misplacedProductDetails ? misplacedProductDetails?.toObject(): {} ) }, status: false } );
1094
+ }
1095
+ }
1096
+
1097
+
1098
+ const complianceData = { ...productMapping.toObject(), compliance: 'proper' };
1099
+ delete complianceData._id;
1100
+
1101
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1102
+
1103
+ return res.sendSuccess( { data: productMapping.toObject(), status: true } );
1104
+ } else if ( planogram.productResolutionLevel === 'L4' || planogram.productResolutionLevel === 'L5' ) {
1105
+ if ( !req.body.floorId ) return res.sendError( 'Floor id is required', 400 );
1106
+
1107
+ if ( !req.body.rfId ) return res.sendError( 'RFID is required', 400 );
1108
+
1109
+ if ( !req.body.fixtureId ) return res.sendError( 'Fixture id is required', 400 );
1110
+
1111
+ if ( !req.body.shelfId && req.body.rfId ) {
1112
+ const shelf = await fixtureShelfService.findOne( { planoId: req.body.planoId, floorId: req.body.floorId, fixtureId: req.body.fixtureId, rfId: req.body.rfId } );
1113
+ if ( !shelf ) return res.sendError( 'No matching shelf for the rfId', 400 );
1114
+ return res.sendSuccess( shelf.toObject()._id );
1115
+ }
1116
+
1117
+ if ( !req.body.shelfId ) return res.sendError( 'Shelf id is required', 400 );
1118
+
1119
+ if ( !req.body.shelfPosition ) return res.sendError( 'Shelf position is required', 400 );
1120
+
1121
+ const mappingQuery = {
1122
+ planoId: req.body.planoId,
1123
+ floorId: req.body.floorId,
1124
+ fixtureId: req.body.fixtureId,
1125
+ shelfId: req.body.shelfId,
1126
+ shelfPosition: req.body.shelfPosition,
1127
+ };
1128
+
1129
+ const productMapping = await planoMappingService.findOne( mappingQuery );
1130
+
1131
+ if ( !productMapping ) return res.sendError( 'No product mapped for the spot', 400 );
1132
+
1133
+ const currentDate = new Date( dayjs().format( 'YYYY-MM-DD' ) );
1134
+
1135
+ if ( productMapping.toObject().rfId !== req.body.rfId ) {
1136
+ if ( planogram.productResolutionLevel === 'L4' ) {
1137
+ const complianceData = { ...productMapping.toObject(), compliance: 'misplaced' };
1138
+ delete complianceData._id;
1139
+
1140
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1141
+ return res.sendSuccess( false );
1142
+ } else if ( planogram.productResolutionLevel === 'L5' ) {
1143
+ const misplacedQuery = {
1144
+ planoId: req.body.planoId,
1145
+ floorId: req.body.floorId,
1146
+ fixtureId: req.body.fixtureId,
1147
+ rfId: req.body.rfId,
1148
+ };
1149
+ const misplacedProductMapping = await planoMappingService.findOne( misplacedQuery );
1150
+
1151
+ if ( productMapping.toObject()?.category === misplacedProductMapping.toObject()?.category ) {
1152
+ const complianceData = { ...productMapping.toObject(), compliance: 'proper' };
1153
+ delete complianceData._id;
1154
+
1155
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1156
+ return res.sendSuccess( true );
1157
+ } else {
1158
+ const complianceData = { ...productMapping.toObject(), compliance: 'misplaced' };
1159
+ delete complianceData._id;
1160
+
1161
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1162
+ return res.sendSuccess( false );
1163
+ }
1164
+ }
1165
+ }
1166
+
1167
+ const complianceData = { ...productMapping.toObject(), compliance: 'proper' };
1168
+ delete complianceData._id;
1169
+
1170
+ await planoComplianceService.updateOne( { ...mappingQuery, date: currentDate }, complianceData );
1171
+
1172
+ return res.sendSuccess( true );
1173
+ } else {
1174
+ return res.sendError( 'Incorrect resolution level', 400 );
1175
+ }
1176
+ } catch ( e ) {
1177
+ logger.error( { functionName: 'scanv1', error: e, message: req.body } );
1178
+ return res.sendError( e, 500 );
1179
+ }
1180
+ }
@@ -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
+ }