tango-app-api-infra 3.7.1-beta.5 → 3.7.1-beta.7

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,11 +1,11 @@
1
1
  {
2
2
  "name": "tango-app-api-infra",
3
- "version": "3.7.1-beta.5",
3
+ "version": "3.7.1-beta.7",
4
4
  "description": "infra",
5
- "main": "app.js",
5
+ "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "start": "nodemon --exec \"eslint --fix . && node app.js\""
8
+ "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
9
  },
10
10
  "engines": {
11
11
  "node": ">=18.10.0"
@@ -1,14 +1,14 @@
1
- import { logger } from 'tango-app-api-middleware';
2
- import { bulkUpdate, getOpenSearchData, insertWithId } from 'tango-app-api-middleware/src/utils/openSearch.js';
1
+ import { download, logger } from 'tango-app-api-middleware';
2
+ import { bulkUpdate, getOpenSearchCount, getOpenSearchData, insertWithId } from 'tango-app-api-middleware/src/utils/openSearch.js';
3
3
  import { findOneStore } from '../services/store.service.js';
4
4
  // import { updateOneCamera } from '../services/camera.service.js';
5
+ import dayjs from 'dayjs';
5
6
 
6
7
  export async function createTicket( req, res ) {
7
8
  try {
8
9
  const openSearch = JSON.parse( process.env.OPENSEARCH );
9
10
  const inputData = req.body;
10
11
  const getStoreName = await findOneStore( { storeId: inputData.storeId }, { storeName: 1, _id: 0 } );
11
-
12
12
  inputData.ticketId = 'TE_FDT_' + new Date().valueOf();
13
13
  inputData.clientId = inputData?.storeId?.split( '-' )[0];
14
14
  inputData.storeName =getStoreName?.storeName;
@@ -208,34 +208,175 @@ export async function ticketList( req, res ) {
208
208
  // process.exit();
209
209
  const openSearch = JSON.parse( process.env.OPENSEARCH );
210
210
  const inputData = req.query;
211
- const skip=( inputData.offset - 1 ) *inputData.limit;
211
+ const limit = inputData.limit || 10;
212
+ const skip= inputData.offset == 0? 0:( inputData.offset - 1 ) *limit || 0;
212
213
  inputData.clientId = inputData.clientId.split( ',' ); // convert strig to array
214
+ logger.info( { inputData: inputData, limit: limit, skip: skip } );
215
+ const getCount= {
216
+ query: {
217
+ bool: {
218
+ filter: [
219
+ { terms: { 'clientId.keyword': Array.isArray( inputData.clientId ) ?
220
+ inputData.clientId :
221
+ inputData.clientId } },
222
+ {
223
+ range: {
224
+ dateString: {
225
+ gte: inputData.fromDate,
226
+ lte: inputData.toDate,
227
+ format: 'yyyy-MM-dd',
228
+ },
229
+ },
230
+ },
231
+ ],
232
+ },
233
+ },
234
+ };
235
+
236
+ const geteDataCount = await getOpenSearchCount( openSearch.footfallDirectory, getCount );
237
+ if ( !geteDataCount || geteDataCount?.body?.count == 0 ) {
238
+ return res.sendError( 'No data found', 204 );
239
+ }
213
240
  const getQuery = {
214
- size: inputData.limit,
241
+ size: limit,
215
242
  from: skip,
216
243
  query: {
217
244
  bool: {
218
245
  filter: [
219
- { terms: { 'clientId.keyword': inputData.clientId } },
246
+ { terms: { 'clientId.keyword': Array.isArray( inputData.clientId ) ?
247
+ inputData.clientId :
248
+ inputData.clientId } },
249
+ {
250
+ range: {
251
+ dateString: {
252
+ gte: inputData.fromDate,
253
+ lte: inputData.toDate,
254
+ format: 'yyyy-MM-dd',
255
+ },
256
+ },
257
+ },
258
+ ],
259
+ },
260
+ },
261
+ _source: [ 'storeName', 'storeId', 'ticketId', 'createdAt', 'footfallCount', 'duplicateCount', 'employeeCount', 'houseKeepingCount', 'status', 'dateString' ],
262
+ };
263
+
264
+ const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
265
+ const response = getData?.body?.hits?.hits;
266
+ logger.info( { response: response, body: getData?.body, getData: getData, geteDataCount: geteDataCount } );
267
+
268
+
269
+ if ( inputData.isExport=== true ) {
270
+ const temp = [];
271
+ for ( const item of response ) {
272
+ temp.push( {
273
+ 'Store Name': item.storeName,
274
+ 'Store ID': item.storeId,
275
+ 'Ticket ID': item.ticketId,
276
+ 'Ticket raised on': dayjs( item.createdAt ).format( 'dd MMM, yyyy' ),
277
+ 'Total Footfalls': item.footfallCount,
278
+ 'Duplicates': item.duplicateCount,
279
+ 'Employee/Staff': item.employeeCount,
280
+ 'HouseKeeping': item.houseKeepingCount,
281
+ 'Revised Footfalls': item.footfallCount-( item.duplicateCount+item.employeeCount+item.houseKeepingCount ),
282
+ 'Status': item.status,
283
+ } );
284
+ }
285
+ await download( temp, res );
286
+ return;
287
+ }
288
+
289
+ return res.sendSuccess( { result: response, count: geteDataCount?.body?.count } );
290
+ } catch ( error ) {
291
+ const err = error.message || 'Internal Server Error';
292
+ logger.error( { error: error, messgage: req.query } );
293
+ return res.sendSuccess( err, 500 );
294
+ }
295
+ }
296
+
297
+ export async function getTickets( req, res ) {
298
+ try {
299
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
300
+ const inputData = req.query;
301
+ const limit = inputData.limit || 10;
302
+ const skip= inputData.offset == 0? 0:( inputData.offset - 1 ) *limit || 0;
303
+ inputData.storeId = inputData.storeId.split( ',' ); // convert strig to array
304
+ logger.info( { inputData: inputData, limit: limit, skip: skip } );
305
+ const getCount= {
306
+ query: {
307
+ bool: {
308
+ filter: [
309
+ { terms: { 'storeId.keyword': Array.isArray( inputData.storeId ) ?
310
+ inputData.storeId :
311
+ inputData.storeId } },
312
+ {
313
+ range: {
314
+ dateString: {
315
+ gte: inputData.fromDate,
316
+ lte: inputData.toDate,
317
+ format: 'yyyy-MM-dd',
318
+ },
319
+ },
320
+ },
321
+ ],
322
+ },
323
+ },
324
+ };
325
+
326
+ const geteDataCount = await getOpenSearchCount( openSearch.footfallDirectory, getCount );
327
+ if ( !geteDataCount || geteDataCount?.body?.count == 0 ) {
328
+ return res.sendError( 'No data found', 204 );
329
+ }
330
+ const getQuery = {
331
+ size: limit,
332
+ from: skip,
333
+ query: {
334
+ bool: {
335
+ filter: [
336
+ { terms: { 'storeId.keyword': Array.isArray( inputData.storeId ) ?
337
+ inputData.storeId :
338
+ inputData.storeId } },
220
339
  {
221
340
  range: {
222
341
  dateString: {
223
342
  gte: inputData.fromDate,
224
343
  lte: inputData.toDate,
344
+ format: 'yyyy-MM-dd',
225
345
  },
226
346
  },
227
347
  },
228
348
  ],
229
349
  },
230
350
  },
351
+ // _source: [ 'storeName', 'storeId', 'ticketId', 'createdAt', 'footfallCount', 'duplicateCount', 'employeeCount', 'houseKeepingCount', 'status', 'dateString' ],
231
352
  };
232
353
 
233
354
  const getData = await getOpenSearchData( openSearch.footfallDirectory, getQuery );
234
355
  const response = getData?.body?.hits?.hits;
235
- logger.info( { response: response, body: getData?.body } );
356
+ logger.info( { response: response, body: getData?.body, getData: getData, geteDataCount: geteDataCount } );
236
357
 
237
358
 
238
- return res.sendSuccess( { result: response } );
359
+ if ( inputData.isExport=== true ) {
360
+ const temp = [];
361
+ for ( const item of response ) {
362
+ temp.push( {
363
+ 'Store Name': item.storeName,
364
+ 'Store ID': item.storeId,
365
+ 'Ticket ID': item.ticketId,
366
+ 'Ticket raised on': dayjs( item.createdAt ).format( 'dd MMM, yyyy' ),
367
+ 'Total Footfalls': item.footfallCount,
368
+ 'Duplicates': item.duplicateCount,
369
+ 'Employee/Staff': item.employeeCount,
370
+ 'HouseKeeping': item.houseKeepingCount,
371
+ 'Revised Footfalls': item.footfallCount-( item.duplicateCount+item.employeeCount+item.houseKeepingCount ),
372
+ 'Status': item.status,
373
+ } );
374
+ }
375
+ await download( temp, res );
376
+ return;
377
+ }
378
+
379
+ return res.sendSuccess( { result: response, count: geteDataCount?.body?.count } );
239
380
  } catch ( error ) {
240
381
  const err = error.message || 'Internal Server Error';
241
382
  logger.error( { error: error, messgage: req.query } );
@@ -1,5 +1,5 @@
1
1
  import j2s from 'joi-to-swagger';
2
- import { createTicketSchema, ticketSummarySchema } from '../dtos/footfallDirectory.dtos.js';
2
+ import { createTicketSchema, getTicketsSchema, ticketListSchema, ticketSummarySchema } from '../dtos/footfallDirectory.dtos.js';
3
3
 
4
4
  export const footfallDirectoryDocs = {
5
5
 
@@ -69,21 +69,80 @@ export const footfallDirectoryDocs = {
69
69
  {
70
70
  in: 'query',
71
71
  name: 'clientId',
72
- scema: j2s( ticketSummarySchema ).swagger,
73
- required: false,
72
+ scema: j2s( ticketListSchema ).swagger,
73
+ required: true,
74
74
  },
75
75
  {
76
76
  in: 'query',
77
77
  name: 'fromDate',
78
- scema: j2s( ticketSummarySchema ).swagger,
79
- required: false,
78
+ scema: j2s( ticketListSchema ).swagger,
79
+ required: true,
80
80
  },
81
81
  {
82
82
  in: 'query',
83
83
  name: 'toDate',
84
- scema: j2s( ticketSummarySchema ).swagger,
84
+ scema: j2s( ticketListSchema ).swagger,
85
+ required: true,
86
+ },
87
+ {
88
+ in: 'query',
89
+ name: 'searchvalue',
90
+ scema: j2s( ticketListSchema ).swagger,
91
+ required: false,
92
+ },
93
+ {
94
+ in: 'query',
95
+ name: 'limit',
96
+ scema: j2s( ticketListSchema ).swagger,
85
97
  required: false,
86
98
  },
99
+ {
100
+ in: 'query',
101
+ name: 'offset',
102
+ scema: j2s( ticketListSchema ).swagger,
103
+ required: false,
104
+ },
105
+ {
106
+ in: 'query',
107
+ name: 'isExport',
108
+ scema: j2s( ticketListSchema ).swagger,
109
+ required: false,
110
+ },
111
+ ],
112
+ responses: {
113
+ 200: { description: 'Successful' },
114
+ 401: { description: 'Unauthorized User' },
115
+ 422: { description: 'Field Error' },
116
+ 500: { description: 'Server Error' },
117
+ 204: { description: 'Not Found' },
118
+ },
119
+ },
120
+ },
121
+
122
+ '/v3/footfall-directory-tagging/get-tickets': {
123
+ get: {
124
+ tags: [ 'Footfall Directory Ticket' ],
125
+ description: 'To get tickets Details by selction stores and date range',
126
+ operationId: 'get-tickets',
127
+ parameters: [
128
+ {
129
+ in: 'query',
130
+ name: 'storeId',
131
+ scema: j2s( getTicketsSchema ).swagger,
132
+ required: true,
133
+ },
134
+ {
135
+ in: 'query',
136
+ name: 'fromDate',
137
+ scema: j2s( getTicketsSchema ).swagger,
138
+ required: true,
139
+ },
140
+ {
141
+ in: 'query',
142
+ name: 'toDate',
143
+ scema: j2s( getTicketsSchema ).swagger,
144
+ required: true,
145
+ },
87
146
  ],
88
147
  responses: {
89
148
  200: { description: 'Successful' },
@@ -124,6 +124,7 @@ export const ticketListSchema = Joi.object().keys( {
124
124
  searchValue: Joi.string().optional().allow( '' ),
125
125
  limit: Joi.number().optional(),
126
126
  offset: Joi.number().optional(),
127
+ isExport: Joi.boolean().optional(),
127
128
  fromDate: Joi.string()
128
129
  .pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
129
130
  .required()
@@ -179,5 +180,66 @@ export const ticketListSchema = Joi.object().keys( {
179
180
  } );
180
181
 
181
182
  export const ticketListValid = {
182
- query: ticketSummarySchema,
183
+ query: ticketListSchema,
184
+ };
185
+
186
+ export const getTicketsSchema = Joi.object().keys( {
187
+ storeId: Joi.string().required(),
188
+
189
+ fromDate: Joi.string()
190
+ .pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
191
+ .required()
192
+ .messages( {
193
+ 'string.pattern.name': `'fromDate' must be in the format YYYY-MM-DD (e.g., 2025-07-19).`,
194
+ 'string.empty': `'fromDate' is required.`,
195
+ } )
196
+ .custom( ( value, helpers ) => {
197
+ const from = dayjs( value );
198
+ if ( !from.isValid() ) {
199
+ return helpers.error( 'any.invalid', { message: 'Invalid fromDate' } );
200
+ }
201
+ return value;
202
+ } ),
203
+
204
+ toDate: Joi.string()
205
+ .pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
206
+ .required()
207
+ .messages( {
208
+ 'string.pattern.name': `'toDate' must be in the format YYYY-MM-DD (e.g., 2025-07-19).`,
209
+ 'string.empty': `'toDate' is required.`,
210
+ } )
211
+ .custom( ( value, helpers ) => {
212
+ const to = dayjs( value );
213
+ const today = dayjs();
214
+
215
+ if ( !to.isValid() ) {
216
+ return helpers.error( 'any.invalid', { message: 'Invalid toDate' } );
217
+ }
218
+ if ( to.isAfter( today, 'day' ) ) {
219
+ return helpers.error( 'any.invalid', { message: 'toDate cannot be in the future' } );
220
+ }
221
+
222
+ return value;
223
+ } ),
224
+ } ).custom( ( value, helpers ) => {
225
+ const from = dayjs( value.fromDate );
226
+ const to = dayjs( value.toDate );
227
+
228
+ if ( !from.isValid() || !to.isValid() ) {
229
+ return helpers.error( 'any.invalid', { message: 'Invalid dates' } );
230
+ }
231
+
232
+ if ( from.isAfter( to ) ) {
233
+ return helpers.error( 'any.invalid', { message: 'fromDate cannot be after toDate' } );
234
+ }
235
+
236
+ if ( to.diff( from, 'day' ) > 90 ) {
237
+ return helpers.error( 'any.invalid', { message: 'Date range cannot exceed 90 days' } );
238
+ }
239
+
240
+ return value;
241
+ } );
242
+
243
+ export const getTicketsValid = {
244
+ query: getTicketsSchema,
183
245
  };
@@ -1,7 +1,7 @@
1
1
  import express from 'express';
2
2
  import { isExist } from '../validations/footfallDirectory.validation.js';
3
- import { createTicket, ticketList, ticketSummary } from '../controllers/footfallDirectory.controllers.js';
4
- import { createTicketValid, ticketListValid, ticketSummaryValid } from '../dtos/footfallDirectory.dtos.js';
3
+ import { createTicket, getTickets, ticketList, ticketSummary } from '../controllers/footfallDirectory.controllers.js';
4
+ import { createTicketValid, getTicketsValid, ticketListValid, ticketSummaryValid } from '../dtos/footfallDirectory.dtos.js';
5
5
  import { bulkValidate, validate } from 'tango-app-api-middleware';
6
6
 
7
7
  export const footfallDirectoryRouter = express.Router();
@@ -10,4 +10,8 @@ footfallDirectoryRouter.post( '/create-ticket', validate( createTicketValid ), i
10
10
  footfallDirectoryRouter.get( '/ticket-summary', bulkValidate( ticketSummaryValid ), ticketSummary );
11
11
 
12
12
  footfallDirectoryRouter.get( '/ticket-list', bulkValidate( ticketListValid ), ticketList );
13
+ footfallDirectoryRouter.get( '/get-tickets', bulkValidate( getTicketsValid ), getTickets );
14
+
15
+ // footfallDirectoryRouter.put( '/update-status', bulkValidate( updateStatus ), getTickets );
16
+
13
17
  // footfallDirectoryRouter.get( '/ticket-list', ticketList );
package/app.js DELETED
@@ -1,49 +0,0 @@
1
- import express from 'express';
2
- import { infraRouter, internalInfraRouter, userInfraRouter, storeInfraRouter, dataMismatchTicketRouter, clientInfraRouter, employeeTrainigRouter } from './index.js';
3
-
4
- import dotenv from 'dotenv';
5
- import { logger } from 'tango-app-api-middleware';
6
- import { connectdb } from './config/database/database.js';
7
- import responseMiddleware from './config/response/response.js';
8
- import errorMiddleware from './config/response/error.js';
9
- import pkg from 'body-parser';
10
- import cors from 'cors';
11
- import { swaggerConfig } from './config/swagger/swagger.js';
12
- import swagger from 'swagger-ui-express';
13
- import { footfallDirectoryRouter } from './src/routes/footfallDirectory.routes.js';
14
-
15
- const { json, urlencoded } = pkg;
16
- const env=dotenv.config();
17
-
18
- const app = express();
19
- const PORT = process.env.PORT || 3000;
20
- app.use( cors() );
21
-
22
- app.use( json( { limit: '500mb' } ) );
23
- app.use(
24
- urlencoded( {
25
- extended: true,
26
- } ),
27
- );
28
- app.use( responseMiddleware );
29
- app.use( errorMiddleware );
30
-
31
- if ( env.error ) {
32
- logger.error( '.env not found' );
33
- process.exit( 1 );
34
- }
35
- app.use( '/api-docs', swagger.serve, swagger.setup( swaggerConfig ) );
36
-
37
- app.use( '/v3/infra', infraRouter );
38
- app.use( '/v3/userinfra', userInfraRouter );
39
- app.use( '/v3/internalinfra', internalInfraRouter );
40
- app.use( '/v3/storeInfra', storeInfraRouter );
41
- app.use( '/v3/datamismatchticket', dataMismatchTicketRouter );
42
- app.use( '/v3/clientinfra', clientInfraRouter );
43
- app.use( '/v3/employeetrainig', employeeTrainigRouter );
44
- app.use( '/v3/footfall-directory-tagging', footfallDirectoryRouter );
45
-
46
- app.listen( PORT, () => {
47
- logger.info( `server is running on port= ${PORT} ` );
48
- connectdb();
49
- } );