@@ -360,19 +360,17 @@ function normalizeMatchFilter(filter, field){
360360 */
361361/** @namespace filter.numDocs */
362362async function getPredictions ( filter , user ) {
363-
364363 try {
364+ logger . debug ( "Starting getPredictions" , { filter, userAgency : user ?. agency , userRole : user ?. userRole } )
365+
365366 let first = filter . first || 0
366367 let max_fetch_rows = filter . rows || configuration . getConfig ( "defaultMaxPredictions" , 1000 )
367368
368-
369369 if ( user === undefined || user . agency === undefined || user . userRole === undefined ) {
370+ logger . warn ( "Missing user information - returning empty result" )
370371 return [ ]
371372 }
372373
373- logger . debug ( "Entering getPredictions" )
374- // await updatePredictionTable()
375-
376374 let attributes = {
377375 offset : first ,
378376 limit : max_fetch_rows ,
@@ -382,108 +380,101 @@ async function getPredictions (filter, user) {
382380 } ] ,
383381 }
384382
385- // filter to allowed notice types
386383 let types = configuration . getConfig ( "VisibleNoticeTypes" , [ 'Solicitation' , 'Combined Synopsis/Solicitation' , 'RFQ' ] )
384+ logger . debug ( "Filtering by notice types" , { types } )
387385
388386 attributes . where = {
389387 noticeType : {
390388 [ Op . in ] : types
391389 }
392390 }
393391
394- // filter out rows
395392 if ( filter . globalFilter ) {
393+ logger . debug ( "Applying global filter" , { searchText : filter . globalFilter . toLowerCase ( ) } )
396394 attributes . where . searchText = { [ Op . like ] : `%${ filter . globalFilter . toLowerCase ( ) } %` }
397395 }
396+
398397 for ( let f of [ 'office' , 'agency' , 'title' , 'solNum' , 'reviewRec' , 'id' ] ) {
399398 normalizeMatchFilter ( filter , f )
400399 }
401400
402-
403- // process PrimeNG filters: filter.filters = { field: { value: 'x', matchMode: 'equals' } }
404401 if ( filter . filters ) {
402+ logger . debug ( "Processing PrimeNG filters" , { filters : filter . filters } )
405403 for ( let f in filter . filters ) {
406404 if ( filter . filters . hasOwnProperty ( f ) && filter . filters [ f ] . matchMode === 'equals' ) {
407405 attributes . where [ f ] = { [ Op . eq ] : filter . filters [ f ] . value }
406+ logger . debug ( `Applied filter for field ${ f } ` , { value : filter . filters [ f ] . value } )
408407 }
409408 }
410409 }
411410
412-
413- try {
414- let agency = ( filter && filter . filters && filter . filters . agency && filter . filters . agency . value ) || "no agency"
415- logger . log ( "debug" , `Getting predictions for agency ${ agency } . Remaining filters in meta data` , { tag : 'getPredictions' , filter : filter } )
416- } catch ( e ) {
417- logger . log ( "error" , "error logging prediction search filter" , { error : e } )
418- }
419-
420- // process dates
421-
422- // make sure anything we return is past the date cuttoff - unless we are asking for a specific record!
423- if ( ! filter . ignoreDateCutoff ) {
411+ if ( ! filter . ignoreDateCutoff ) {
412+ logger . debug ( "Applying date cutoff filters" )
424413 if ( ( ! filter . filters ) || ( ! filter . filters . hasOwnProperty ( 'solNum' ) ) ) {
425414 if ( configuration . getConfig ( "minPredictionCutoffDate" ) ) {
426- attributes . where . date = { [ Op . gt ] : configuration . getConfig ( "minPredictionCutoffDate" ) }
415+ const cutoffDate = configuration . getConfig ( "minPredictionCutoffDate" )
416+ logger . debug ( "Using minPredictionCutoffDate" , { cutoffDate } )
417+ attributes . where . date = { [ Op . gt ] : cutoffDate }
427418 } else if ( configuration . getConfig ( "predictionCutoffDays" ) ) {
428419 const numDays = configuration . getConfig ( "predictionCutoffDays" )
429420 const today = new Date ( )
430421 let cutoff = new Date ( )
431422 cutoff . setDate ( today . getDate ( ) - numDays )
423+ logger . debug ( "Using predictionCutoffDays" , { numDays, cutoffDate : cutoff } )
432424 attributes . where . date = { [ Op . gt ] : cutoff }
433425 }
434426 }
435427 }
436428
437-
438-
439429 if ( filter . startDate ) {
440- // double check they aren't asking for data from before the cutoff
441430 const start = Date . parse ( filter . startDate )
442431 const cutoff = Date . parse ( configuration . getConfig ( "minPredictionCutoffDate" , '1990-01-01' ) )
432+ logger . debug ( "Processing start date filter" , { startDate : filter . startDate , cutoffDate : cutoff } )
443433 if ( start > cutoff ) {
444434 attributes . where . date = { [ Op . gt ] : filter . startDate }
445435 }
446436 }
447437
448438 if ( filter . endDate ) {
439+ logger . debug ( "Processing end date filter" , { endDate : filter . endDate } )
449440 attributes . where . date = ( attributes . where . date ) ?
450441 Object . assign ( attributes . where . date , { [ Op . lt ] : filter . endDate } ) :
451442 { [ Op . lt ] : filter . endDate }
452443 }
453444
454- // finally, put in an agency filter if this user isn't an admin
455- // want to do it last so it overrides any possible agency setting in the supplied filter
456- if ( ! authRoutes . isGSAAdmin ( user . agency , user . userRole ) ) {
457- attributes . where . agency = {
458- [ Op . eq ] : ( user && user . agency ) ? user . agency : ''
459- }
445+ // Agency access control - check both agency and office fields
446+ if ( ! authRoutes . isGSAAdmin ( user . agency , user . userRole ) ) {
447+ logger . debug ( "Restricting to user's agency and office" , { agency : user . agency } )
448+ attributes . where [ Op . or ] = [
449+ { agency : { [ Op . eq ] : user . agency } } ,
450+ { office : { [ Op . eq ] : user . agency } }
451+ ]
452+ } else {
453+ logger . debug ( "GSA Admin detected - no agency restriction applied" )
460454 }
461455
462- // set order
456+ // Set order
463457 attributes . order = [ ]
464458 if ( filter . sortField !== 'unsorted' && filter . sortField ) {
465- let direction = 'ASC' ;
466- if ( filter . sortOrder && filter . sortOrder < 0 ) {
467- direction = 'DESC'
468- }
459+ let direction = filter . sortOrder && filter . sortOrder < 0 ? 'DESC' : 'ASC'
460+ logger . debug ( "Applying sort" , { field : filter . sortField , direction } )
469461 attributes . order . push ( [ filter . sortField , direction ] )
470462 }
471-
472- // always end with id sort to keep the newest first (all else being equal)
473463 attributes . order . push ( [ 'id' , 'DESC' ] )
474464
475- attributes . raw = true // return as plan data not Sequelize object
465+ attributes . raw = true
476466 attributes . nest = true
477- // Debugging Queries:
478- //attributes.logging = console.log
479467
480- // Removing where checks if values are not provided. where column = {} leads to sequelize issues
481468 attributes . where = removeEmptyFrom ( attributes . where )
482-
483- // noinspection JSUnresolvedFunction
484- let preds = await Solicitation . findAndCountAll ( attributes )
485-
469+ logger . debug ( "Final query attributes" , { attributes } )
486470
471+ let preds = await Solicitation . findAndCountAll ( attributes )
472+ logger . debug ( "Query complete" , {
473+ rowCount : preds . count ,
474+ firstRow : first ,
475+ maxRows : max_fetch_rows ,
476+ returnedRows : preds . rows . length
477+ } )
487478
488479 return {
489480 predictions : preds . rows ,
@@ -492,7 +483,12 @@ async function getPredictions (filter, user) {
492483 totalCount : preds . count
493484 }
494485 } catch ( e ) {
495- logger . log ( "error" , "Error in getPredictions" , { tag : "getPredictions" , error : e , "error-message" : e . message , stack : e . stack } )
486+ logger . error ( "Error in getPredictions" , {
487+ error : e . message ,
488+ stack : e . stack ,
489+ filter,
490+ userAgency : user ?. agency
491+ } )
496492 return {
497493 predictions : [ ] ,
498494 first : 0 ,
0 commit comments