tango-app-api-analysis-traffic 3.8.9 → 3.8.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 +2 -2
- package/src/controllers/nob.controllers.js +102 -29
- package/src/controllers/revop.controller.js +1493 -71
- package/src/controllers/tangoTrafficV3.controllers.js +27 -26
- package/src/dtos/nob.dtos.js +13 -0
- package/src/dtos/revop.dtos.js +16 -9
- package/src/dtos/validation.dtos.js +11 -0
- package/src/routes/nob.routes.js +5 -1
- package/src/routes/revop.routes.js +7 -6
- package/src/routes/traffic.routes.js +3 -1
- package/src/services/nob.service.js +1 -0
- package/src/services/vmsStoreRequest.service.js +5 -0
- package/src/validations/nob.validations.js +1 -0
- package/src/validations/revop.validation.js +240 -107
|
@@ -3129,29 +3129,30 @@ export const managerTrafficDensityExport = async ( req, res ) => {
|
|
|
3129
3129
|
}
|
|
3130
3130
|
};
|
|
3131
3131
|
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3132
|
+
|
|
3133
|
+
export const getStoreListv2 = async ( req, res ) => {
|
|
3134
|
+
try {
|
|
3135
|
+
let reqestData = req.body;
|
|
3136
|
+
let getClientData = await getClientConfig( reqestData.clientId );
|
|
3137
|
+
if ( !getClientData ) {
|
|
3138
|
+
return res.sendError( 'Invalid Client Id', 400 );
|
|
3139
|
+
}
|
|
3140
|
+
reqestData.featureConfigs = getClientData.featureConfigs;
|
|
3141
|
+
let LamdaURL = 'https://nmnnlq3ie65bljcvxauhxmsrpa0edhfa.lambda-url.ap-south-1.on.aws/';
|
|
3142
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
3143
|
+
if ( resultData ) {
|
|
3144
|
+
if ( resultData.status_code == '200' ) {
|
|
3145
|
+
return res.sendSuccess( resultData );
|
|
3146
|
+
} else {
|
|
3147
|
+
return res.sendError( 'No Content', 204 );
|
|
3148
|
+
}
|
|
3149
|
+
} else {
|
|
3150
|
+
return res.sendError( 'No Content', 204 );
|
|
3151
|
+
}
|
|
3152
|
+
} catch ( error ) {
|
|
3153
|
+
const err = error.message || 'Internal Server Error';
|
|
3154
|
+
logger.error( { error: error, message: req.body, function: 'getStoreListv2' } );
|
|
3155
|
+
return res.sendError( err, 500 );
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
3158
|
+
|
package/src/dtos/nob.dtos.js
CHANGED
|
@@ -21,6 +21,19 @@ export const addBillsSchema = joi.object( {
|
|
|
21
21
|
'string.empty': 'Please enter a valid NOB Count',
|
|
22
22
|
'any.required': 'NOB Count is required',
|
|
23
23
|
} ).allow( null ),
|
|
24
|
+
zonewisenob: joi.array().items(
|
|
25
|
+
joi.object( {
|
|
26
|
+
zoneName: joi.string().required().messages( {
|
|
27
|
+
'string.empty': 'Zone name is required',
|
|
28
|
+
'any.required': 'Zone name is required',
|
|
29
|
+
} ),
|
|
30
|
+
|
|
31
|
+
nobCount: joi.number().required().messages( {
|
|
32
|
+
'number.base': 'Please enter a valid Zone NOB Count',
|
|
33
|
+
'any.required': 'Zone NOB Count is required',
|
|
34
|
+
} ),
|
|
35
|
+
} ),
|
|
36
|
+
).optional(),
|
|
24
37
|
} ),
|
|
25
38
|
).required(),
|
|
26
39
|
|
package/src/dtos/revop.dtos.js
CHANGED
|
@@ -4,7 +4,8 @@ import dayjs from 'dayjs';
|
|
|
4
4
|
export const storeProcessedDataSchema = joi.object( {
|
|
5
5
|
|
|
6
6
|
storeId: joi.string().required(),
|
|
7
|
-
|
|
7
|
+
fromDate: joi.string().required(),
|
|
8
|
+
toDate: joi.string().required(),
|
|
8
9
|
|
|
9
10
|
} );
|
|
10
11
|
|
|
@@ -47,24 +48,19 @@ export const tagTempIdSchema = joi.object( {
|
|
|
47
48
|
storeId: joi.string().required(),
|
|
48
49
|
dateString: joi.string().required().custom( ( value, helpers ) => {
|
|
49
50
|
const inputDate = dayjs( value, 'YYYY-MM-DD', true );
|
|
50
|
-
const today = dayjs();
|
|
51
51
|
|
|
52
52
|
if ( !inputDate.isValid() ) {
|
|
53
53
|
return helpers.error( 'any.invalid' );
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
const diff = today.diff( inputDate, 'day' );
|
|
57
|
-
|
|
58
|
-
if ( diff > 4 ) {
|
|
59
|
-
return helpers.message( 'Tagging is not allowed for a period exceeding 4 days' );
|
|
60
|
-
}
|
|
61
|
-
|
|
62
55
|
return value;
|
|
63
56
|
} ), // yyyy-mm-dd
|
|
64
57
|
tempId: joi.number().required(),
|
|
65
58
|
revopsType: joi.string().required(),
|
|
66
59
|
timeRange: joi.string().required(),
|
|
67
60
|
isChecked: joi.boolean().required().allow( null ),
|
|
61
|
+
mode: joi.string().valid( 'web', 'mobile' ).required().messages( {
|
|
62
|
+
'any.only': 'type must be one of [mobile,web]',
|
|
63
|
+
} ),
|
|
68
64
|
duplicateImage: joi.array().items( joi.object(
|
|
69
65
|
{
|
|
70
66
|
tempId: joi.number().required(),
|
|
@@ -80,6 +76,7 @@ export const tagTempIdSchema = joi.object( {
|
|
|
80
76
|
entryTime: joi.string().required(),
|
|
81
77
|
exitTime: joi.string().required(),
|
|
82
78
|
filePath: joi.string().required(),
|
|
79
|
+
comments: joi.string().optional().allow( '' ),
|
|
83
80
|
|
|
84
81
|
} );
|
|
85
82
|
|
|
@@ -100,3 +97,13 @@ export const getCategorizedImagesValid = {
|
|
|
100
97
|
body: getCategorizedImagesSchema,
|
|
101
98
|
};
|
|
102
99
|
|
|
100
|
+
export const vmsDataMigrationSchema = joi.object( {
|
|
101
|
+
storeId: joi.string().optional(),
|
|
102
|
+
dateString: joi.string().optional(),
|
|
103
|
+
limit: joi.number().optional().default( 100 ),
|
|
104
|
+
} );
|
|
105
|
+
|
|
106
|
+
export const vmsDataMigrationValid = {
|
|
107
|
+
query: vmsDataMigrationSchema,
|
|
108
|
+
};
|
|
109
|
+
|
|
@@ -239,6 +239,17 @@ export const validateCountryHeaderSchemav2 = joi.object( {
|
|
|
239
239
|
export const validateCountryHeaderParamsv2 = {
|
|
240
240
|
body: validateCountryHeaderSchemav2,
|
|
241
241
|
};
|
|
242
|
+
|
|
243
|
+
export const validateGetStoreListSchemav2 = joi.object( {
|
|
244
|
+
clientId: joi.string().required(),
|
|
245
|
+
storeId: joi.array().items( joi.string().required() ).required(),
|
|
246
|
+
downtime: joi.array().items( joi.string().required() ).optional(),
|
|
247
|
+
fromDate: joi.string().required(),
|
|
248
|
+
toDate: joi.string().required(),
|
|
249
|
+
} );
|
|
250
|
+
export const validateGetStoreList = {
|
|
251
|
+
body: validateGetStoreListSchemav2,
|
|
252
|
+
};
|
|
242
253
|
export const getMyProductSchema = joi.object( {
|
|
243
254
|
clientId: joi.string().required(),
|
|
244
255
|
storeId: joi.array().optional().empty(),
|
package/src/routes/nob.routes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { accessVerification, bulkValidate, getAssinedStore, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
3
3
|
import { addBillsValid, getNobDataValid, storeListValid } from '../dtos/nob.dtos.js';
|
|
4
|
-
import { addBills, getNobData, storeList } from '../controllers/nob.controllers.js';
|
|
4
|
+
import { addBills, getNobData, storeList, zonelist, zonetemplate } from '../controllers/nob.controllers.js';
|
|
5
5
|
import { clientValidations, fieldValidation, roleVerification } from '../validations/nob.validations.js';
|
|
6
6
|
|
|
7
7
|
const nobRouter=express.Router();
|
|
@@ -13,4 +13,8 @@ nobRouter.post( '/add-bills', isAllowedSessionHandler, accessVerification( { use
|
|
|
13
13
|
|
|
14
14
|
nobRouter.post( '/get-nob-data', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), validate( getNobDataValid ), clientValidations, getAssinedStore, getNobData );
|
|
15
15
|
|
|
16
|
+
nobRouter.post( '/zonelist', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), zonelist );
|
|
17
|
+
|
|
18
|
+
nobRouter.post( '/zonetemplate', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), getAssinedStore, zonetemplate );
|
|
19
|
+
|
|
16
20
|
export default nobRouter;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages } from '../controllers/revop.controller.js';
|
|
3
|
+
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus } from '../controllers/revop.controller.js';
|
|
4
4
|
import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
5
|
-
import { footfallImagesValid, getCategorizedImagesValid, storeProcessedDataValid, tagTempIdValid } from '../dtos/revop.dtos.js';
|
|
5
|
+
import { footfallImagesValid, getCategorizedImagesValid, storeProcessedDataValid, tagTempIdValid, vmsDataMigrationValid } from '../dtos/revop.dtos.js';
|
|
6
6
|
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig } from '../validations/revop.validation.js';
|
|
7
7
|
|
|
8
8
|
export const revopRouter = express.Router();
|
|
@@ -11,15 +11,16 @@ revopRouter
|
|
|
11
11
|
.get( '/getconfig', isAllowedSessionHandler, getconfig )
|
|
12
12
|
.post( '/tagging', isAllowedSessionHandler, revoptagging )
|
|
13
13
|
.post( '/getrevoptagging', isAllowedSessionHandler, getrevoptagging )
|
|
14
|
+
.post( '/migrate-revop', migrateRevopIndex )
|
|
15
|
+
.post( '/expire-review-status', expireReviewStatus )
|
|
14
16
|
.post( '/revoptaggingcount', isAllowedSessionHandler, revoptaggingcount )
|
|
15
17
|
|
|
16
18
|
// new enhnacemnet (for footfall directory)
|
|
17
19
|
.get( '/store-processed-data', isAllowedSessionHandler, validate( storeProcessedDataValid ), storeProcessedData )
|
|
18
20
|
.get( '/footfall-images', isAllowedSessionHandler, validate( footfallImagesValid ), getTaggingConfig, footFallImages )
|
|
19
|
-
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
.post( '/get-categorized-images', isAllowedSessionHandler, validate( getCategorizedImagesValid ), getCategorizedImages );
|
|
21
|
+
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, tagTempId )
|
|
22
|
+
.post( '/get-categorized-images', isAllowedSessionHandler, validate( getCategorizedImagesValid ), getCategorizedImages )
|
|
23
|
+
.post( '/vms-data-migration', validate( vmsDataMigrationValid ), vmsDataMigration );
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
export default revopRouter;
|
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
funnelV3,
|
|
93
93
|
getStoreMapDataV3,
|
|
94
94
|
managerTrafficDensityExport,
|
|
95
|
+
getStoreListv2,
|
|
95
96
|
} from '../controllers/tangoTrafficV3.controllers.js';
|
|
96
97
|
analysisTrafficRouter
|
|
97
98
|
.get( '/welcome', welcome )
|
|
@@ -175,5 +176,6 @@ analysisTrafficRouter
|
|
|
175
176
|
.post( '/headerCountry_v2', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateCountryHeaderParamsv2 ), getAssinedStore, headerCountryV2 )
|
|
176
177
|
.post( '/checkTodayReportStatus', isAllowedSessionHandler, checkTodayReportStatus )
|
|
177
178
|
.post( '/headerZoneV2', isAllowedSessionHandler, headerZoneV2 )
|
|
178
|
-
.post( '/trafficDensityExport_v2', managerTrafficDensityExport )
|
|
179
|
+
.post( '/trafficDensityExport_v2', managerTrafficDensityExport )
|
|
180
|
+
.post( '/get-store-list-v2', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateGetStoreList ), getStoreListv2 );
|
|
179
181
|
export default analysisTrafficRouter;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import nobBillingModel from 'tango-api-schema/schema/nobBilling.model.js';
|
|
2
2
|
|
|
3
3
|
export async function updateOneNobBilling( query, record ) {
|
|
4
|
+
console.log( '🚀 ~ updateOneNobBilling ~ record:', record );
|
|
4
5
|
return await nobBillingModel.updateOne( query, { $set: record }, { upsert: true } );
|
|
5
6
|
}
|
|
6
7
|
|
|
@@ -121,6 +121,7 @@ export async function fieldValidation( req, res, next ) {
|
|
|
121
121
|
nobDate: nobDateIso,
|
|
122
122
|
nobCount: inputFilter[i]?.nobCount,
|
|
123
123
|
dateString: inputFilter[i]?.nobDate,
|
|
124
|
+
zonewisenob: inputFilter[i]?.zonewisenob?inputFilter[i]?.zonewisenob:[],
|
|
124
125
|
};
|
|
125
126
|
const query ={ storeId: storeData[0]?.storeId, nobDate: inputFilter[i]?.nobDate };
|
|
126
127
|
|
|
@@ -1,12 +1,100 @@
|
|
|
1
1
|
import { getOpenSearchCount, logger } from 'tango-app-api-middleware';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { deleteByQuery, getOpenSearchData } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
3
|
+
import { aggregate } from '../services/clients.services.js';
|
|
4
4
|
|
|
5
5
|
export async function getTaggingConfig( req, res, next ) {
|
|
6
6
|
try {
|
|
7
7
|
const inputData= req.query;
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const clientId = inputData.storeId.split( '-' )[0];
|
|
9
|
+
const configQuery = [
|
|
10
|
+
{
|
|
11
|
+
$match: {
|
|
12
|
+
clientId,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// Convert all effectiveFrom to proper Date
|
|
17
|
+
{
|
|
18
|
+
$addFields: {
|
|
19
|
+
taggingLimitationWithDate: {
|
|
20
|
+
$map: {
|
|
21
|
+
input: '$footfallDirectoryConfigs.taggingLimitation',
|
|
22
|
+
as: 'item',
|
|
23
|
+
in: {
|
|
24
|
+
effectiveFrom: { $toDate: '$$item.effectiveFrom' },
|
|
25
|
+
values: '$$item.values',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Filter items <= input date
|
|
33
|
+
{
|
|
34
|
+
$addFields: {
|
|
35
|
+
matchedLimitation: {
|
|
36
|
+
$filter: {
|
|
37
|
+
input: '$taggingLimitationWithDate',
|
|
38
|
+
as: 'item',
|
|
39
|
+
cond: {
|
|
40
|
+
$lte: [
|
|
41
|
+
'$$item.effectiveFrom',
|
|
42
|
+
{ $toDate: inputData.dateString },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// Sort DESC and pick ONLY top 1 -> latest effective record
|
|
51
|
+
{
|
|
52
|
+
$addFields: {
|
|
53
|
+
effectiveLimitation: {
|
|
54
|
+
$arrayElemAt: [
|
|
55
|
+
{
|
|
56
|
+
$slice: [
|
|
57
|
+
{
|
|
58
|
+
$sortArray: {
|
|
59
|
+
input: '$matchedLimitation',
|
|
60
|
+
sortBy: { effectiveFrom: -1 },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
1,
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
0,
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
$project: {
|
|
74
|
+
config: 1,
|
|
75
|
+
effectiveLimitation: 1,
|
|
76
|
+
footfallDirectoryConfigs: 1,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const getData = await aggregate( configQuery );
|
|
83
|
+
|
|
84
|
+
// Convert "taggingLimitation" array (if present) to "config" object with expected key-value pairs
|
|
85
|
+
let config = {};
|
|
86
|
+
if ( getData && getData?.length > 0 && Array.isArray( getData?.[0]?.effectiveLimitation?.values ) ) {
|
|
87
|
+
for ( const item of getData?.[0]?.footfallDirectoryConfigs?.taggingLimitation?.[0]?.values ) {
|
|
88
|
+
if ( item && item.type && typeof item.value !== 'undefined' && item.unit ) {
|
|
89
|
+
config[item.type] = `${item.value}${item.unit}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// getData[0].footfallDirectoryConfigs.taggingLimitation = [];
|
|
94
|
+
getData[0].footfallDirectoryConfigs.config = config;
|
|
95
|
+
|
|
96
|
+
getData[0].footfallDirectoryConfigs.taggingLimitation = getData?.[0]?.effectiveLimitation?.values;
|
|
97
|
+
req.store = getData[0];
|
|
10
98
|
next();
|
|
11
99
|
} catch ( error ) {
|
|
12
100
|
logger.error( { error: error, message: req.body, function: 'traffic-revop-getTaggingConfig' } );
|
|
@@ -51,41 +139,127 @@ export async function getFootfallCount( req, res, next ) {
|
|
|
51
139
|
export async function mappingConfig( req, res, next ) {
|
|
52
140
|
try {
|
|
53
141
|
const inputData = req.body;
|
|
142
|
+
|
|
54
143
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
term: {
|
|
63
|
-
'storeId.keyword': inputData.storeId,
|
|
64
|
-
},
|
|
144
|
+
const footfallQuery ={
|
|
145
|
+
query: {
|
|
146
|
+
bool: {
|
|
147
|
+
must: [
|
|
148
|
+
{
|
|
149
|
+
term: {
|
|
150
|
+
'store_id.keyword': inputData.storeId,
|
|
65
151
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
term: {
|
|
155
|
+
'date_string': inputData.dateString,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
const footfallOutput = await getOpenSearchData( openSearch.footfall, footfallQuery );
|
|
166
|
+
if ( footfallOutput?.body?.hits?.hits?.length === 0 ) {
|
|
167
|
+
return res.sendError( 'No updated footfall for this date', 400 );
|
|
168
|
+
}
|
|
169
|
+
const getFootfallCount = footfallOutput?.body?.hits?.hits;
|
|
170
|
+
const footfall = getFootfallCount?.[0]?._source?.footfall_count;
|
|
171
|
+
const clientId = inputData?.storeId?.split( '-' )[0];
|
|
172
|
+
const configQuery = [
|
|
173
|
+
{
|
|
174
|
+
$match: {
|
|
175
|
+
clientId: clientId,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// Convert all effectiveFrom to proper Date
|
|
180
|
+
{
|
|
181
|
+
$addFields: {
|
|
182
|
+
taggingLimitationWithDate: {
|
|
183
|
+
$map: {
|
|
184
|
+
input: '$footfallDirectoryConfigs.taggingLimitation',
|
|
185
|
+
as: 'item',
|
|
186
|
+
in: {
|
|
187
|
+
effectiveFrom: { $toDate: '$$item.effectiveFrom' },
|
|
188
|
+
values: '$$item.values',
|
|
70
189
|
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
// Filter items <= input date
|
|
196
|
+
{
|
|
197
|
+
$addFields: {
|
|
198
|
+
matchedLimitation: {
|
|
199
|
+
$filter: {
|
|
200
|
+
input: '$taggingLimitationWithDate',
|
|
201
|
+
as: 'item',
|
|
202
|
+
cond: {
|
|
203
|
+
$lte: [
|
|
204
|
+
'$$item.effectiveFrom',
|
|
205
|
+
{ $toDate: inputData.dateString },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// Sort DESC and pick ONLY top 1 -> latest effective record
|
|
214
|
+
{
|
|
215
|
+
$addFields: {
|
|
216
|
+
effectiveLimitation: {
|
|
217
|
+
$arrayElemAt: [
|
|
71
218
|
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
219
|
+
$slice: [
|
|
220
|
+
{
|
|
221
|
+
$sortArray: {
|
|
222
|
+
input: '$matchedLimitation',
|
|
223
|
+
sortBy: { effectiveFrom: -1 },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
1,
|
|
227
|
+
],
|
|
75
228
|
},
|
|
229
|
+
0,
|
|
76
230
|
],
|
|
77
231
|
},
|
|
78
232
|
},
|
|
79
|
-
}
|
|
233
|
+
},
|
|
80
234
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
235
|
+
{
|
|
236
|
+
$project: {
|
|
237
|
+
config: 1,
|
|
238
|
+
effectiveLimitation: 1,
|
|
239
|
+
footfallDirectoryConfigs: 1,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
const getConfig = await aggregate( configQuery );
|
|
244
|
+
req.client = getConfig[0];
|
|
245
|
+
const taggingLimitation = getConfig?.[0]?.effectiveLimitation?.values;
|
|
246
|
+
|
|
247
|
+
// Find the tagging limitation for the given revopsType
|
|
248
|
+
let matchedLimitation = null;
|
|
249
|
+
if ( Array.isArray( taggingLimitation ) ) {
|
|
250
|
+
matchedLimitation = taggingLimitation.find(
|
|
251
|
+
( l ) => l.type === inputData.revopsType,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if ( matchedLimitation ) {
|
|
256
|
+
// Determine the limit value
|
|
257
|
+
let limitValue = Number( matchedLimitation.value ) || 0;
|
|
258
|
+
let unit = matchedLimitation.unit;
|
|
259
|
+
|
|
260
|
+
// Assuming getData and/or getOpenSearchCount provides the actual tagged count for revopsType
|
|
261
|
+
// Query OpenSearch for current tagged count for this revopsType
|
|
262
|
+
const taggedCountQuery = {
|
|
89
263
|
query: {
|
|
90
264
|
bool: {
|
|
91
265
|
must: [
|
|
@@ -104,92 +278,51 @@ export async function mappingConfig( req, res, next ) {
|
|
|
104
278
|
'revopsType.keyword': inputData.revopsType,
|
|
105
279
|
},
|
|
106
280
|
},
|
|
281
|
+
{
|
|
282
|
+
term: {
|
|
283
|
+
'isParent': false,
|
|
284
|
+
},
|
|
285
|
+
},
|
|
107
286
|
],
|
|
108
287
|
},
|
|
109
288
|
},
|
|
110
289
|
};
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
290
|
+
const taggedData = await getOpenSearchCount( openSearch.revop, taggedCountQuery );
|
|
291
|
+
const taggedValue = ( taggedData?.body?.count || 0 )+ ( inputData.revopsType == 'duplicate'? inputData?.duplicateImage?.length : 1 );
|
|
292
|
+
// If the unit is %, compare percentage of taggedValue/footfall, otherwise compare taggedValue to limitValue directly
|
|
293
|
+
let isLimitExceeded = false;
|
|
294
|
+
if ( unit === '%' ) {
|
|
295
|
+
// footfall may be undefined, treat as 0 (avoid division by zero)
|
|
296
|
+
const totalFootfall = Number( footfall ) || 0;
|
|
297
|
+
const taggedPercent = totalFootfall > 0 ? ( taggedValue / totalFootfall ) * 100 : 0;
|
|
298
|
+
isLimitExceeded = taggedPercent > limitValue;
|
|
299
|
+
logger.info( {
|
|
300
|
+
limitType: 'PERCENT',
|
|
301
|
+
taggedValue,
|
|
302
|
+
totalFootfall,
|
|
303
|
+
taggedPercent,
|
|
304
|
+
limitValue,
|
|
305
|
+
isLimitExceeded,
|
|
306
|
+
forRevopsType: inputData.revopsType,
|
|
307
|
+
} );
|
|
114
308
|
} else {
|
|
115
|
-
|
|
309
|
+
// Non-percent, treat limitValue as an absolute number
|
|
310
|
+
isLimitExceeded = taggedValue > limitValue;
|
|
311
|
+
logger.info( {
|
|
312
|
+
limitType: 'ABSOLUTE',
|
|
313
|
+
taggedValue,
|
|
314
|
+
limitValue,
|
|
315
|
+
isLimitExceeded,
|
|
316
|
+
forRevopsType: inputData.revopsType,
|
|
317
|
+
} );
|
|
116
318
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
next();
|
|
120
|
-
return;
|
|
121
|
-
} else {
|
|
122
|
-
return res.sendError( 'Forbidden to junk mapping', 500 );
|
|
319
|
+
if ( isLimitExceeded ) {
|
|
320
|
+
return res.sendError( `Limit exceed: Only ${limitValue}${unit || ''} items allowed for ${inputData.revopsType}`, 400 );
|
|
123
321
|
}
|
|
322
|
+
return next();
|
|
124
323
|
} else {
|
|
125
|
-
next();
|
|
324
|
+
return next();
|
|
126
325
|
}
|
|
127
|
-
// else if ( inputData.revopsType == 'duplicate' ) {
|
|
128
|
-
// const getFootfallQuery = {
|
|
129
|
-
// query: {
|
|
130
|
-
// terms: {
|
|
131
|
-
// _id: [ inputData?.dateString ],
|
|
132
|
-
// },
|
|
133
|
-
// },
|
|
134
|
-
// _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
135
|
-
// sort: [
|
|
136
|
-
// {
|
|
137
|
-
// date_iso: {
|
|
138
|
-
// order: 'desc',
|
|
139
|
-
// },
|
|
140
|
-
// },
|
|
141
|
-
// ],
|
|
142
|
-
// };
|
|
143
|
-
|
|
144
|
-
// const getFootfall = await getOpenSearchData( openSearch.footfall, getFootfallQuery );
|
|
145
|
-
// const footfall = getFootfall?.body?.hites?.hits?.[0]?._source?.footfall_count;
|
|
146
|
-
// const getQuery = {
|
|
147
|
-
// query: {
|
|
148
|
-
// bool: {
|
|
149
|
-
// must: [
|
|
150
|
-
// {
|
|
151
|
-
// term: {
|
|
152
|
-
// 'storeId.keyword': inputData.storeId,
|
|
153
|
-
// },
|
|
154
|
-
// },
|
|
155
|
-
// {
|
|
156
|
-
// term: {
|
|
157
|
-
// 'dateString': inputData.dateString,
|
|
158
|
-
// },
|
|
159
|
-
// },
|
|
160
|
-
// {
|
|
161
|
-
// term: {
|
|
162
|
-
// 'revopsType.keyword': inputData.revopsType,
|
|
163
|
-
// },
|
|
164
|
-
// },
|
|
165
|
-
// {
|
|
166
|
-
// term: {
|
|
167
|
-
// 'parent.keyword': null,
|
|
168
|
-
// },
|
|
169
|
-
// },
|
|
170
|
-
// ],
|
|
171
|
-
// },
|
|
172
|
-
// },
|
|
173
|
-
// };
|
|
174
|
-
// const getData = await getOpenSearchCount( openSearch.revop, getQuery );
|
|
175
|
-
// logger.info( { getData: getData, footfall: footfall, duplicate: config?.revopTagging?.duplicate } );
|
|
176
|
-
// if ( getData && footfall && config?.revopTagging?.duplicate ) {
|
|
177
|
-
// const data = config?.revopTagging?.duplicate;
|
|
178
|
-
// // Convert "20%" → 0.2 (handle both "20%" and 20)
|
|
179
|
-
// const percentStr = typeof data === 'string' ? data.replace( '%', '' ) : data;
|
|
180
|
-
// logger.info( { percentStr: percentStr } );
|
|
181
|
-
// const percentValue =percentStr / 100;
|
|
182
|
-
|
|
183
|
-
// const result = percentValue * footfall;
|
|
184
|
-
|
|
185
|
-
// logger.info( { result: result, footfall: footfall } );
|
|
186
|
-
// }
|
|
187
|
-
// if ( getData && getData?.body?.count >= Math.round( result ) ) {
|
|
188
|
-
// return res.sendError( `Select up to ${config?.revopTagging?.duplicate} items only`, 400 );
|
|
189
|
-
// } else {
|
|
190
|
-
// next();
|
|
191
|
-
// }
|
|
192
|
-
// }
|
|
193
326
|
} catch ( error ) {
|
|
194
327
|
logger.error( { error: error, message: req.body, function: 'traffic-revop-getTaggingConfig' } );
|
|
195
328
|
next();
|