@@ -415,14 +415,18 @@ def get(self, id: int) -> Agreement:
415415 return agreement
416416
417417 def get_list (
418- self , agreement_classes : list [Type [Agreement ]], data : dict [str , Any ]
418+ self ,
419+ agreement_classes : list [Type [Agreement ]],
420+ data : dict [str , Any ],
421+ include_procurement : bool = False ,
419422 ) -> tuple [list [Agreement ], dict [str , Any ]]:
420423 """
421424 Get list of agreements with optional filtering and pagination.
422425
423426 Args:
424427 agreement_classes: List of Agreement subclasses to query (e.g., ContractAgreement, GrantAgreement)
425428 data: Dictionary containing filter parameters including limit and offset
429+ include_procurement: When True, eager-load BLIs/trackers and compute procurement metrics
426430
427431 Returns:
428432 Tuple of (paginated agreements list, metadata dict with count/limit/offset)
@@ -433,7 +437,7 @@ def get_list(
433437 # Collect all agreements across types using existing resource helpers
434438 all_results = []
435439 for agreement_cls in agreement_classes :
436- agreements = _get_agreements (self .db_session , agreement_cls , data )
440+ agreements = _get_agreements (self .db_session , agreement_cls , data , include_procurement )
437441 all_results .extend (agreements )
438442
439443 # Filter by award_type (computed property, must be done post-query)
@@ -454,10 +458,15 @@ def get_list(
454458 # Calculate aggregate totals before pagination (for summary cards)
455459 totals = _compute_agreement_totals (all_results )
456460
457- # Calculate procurement overview and step summary before pagination
458- overview_fiscal_year = filters .fiscal_year [0 ] if filters .fiscal_year and len (filters .fiscal_year ) == 1 else None
459- procurement_overview = _compute_procurement_overview (all_results , overview_fiscal_year )
460- procurement_step_summary = _compute_procurement_step_summary (all_results , overview_fiscal_year )
461+ # Calculate procurement overview and step summary before pagination (only when requested)
462+ procurement_overview = None
463+ procurement_step_summary = None
464+ if include_procurement :
465+ overview_fiscal_year = (
466+ filters .fiscal_year [0 ] if filters .fiscal_year and len (filters .fiscal_year ) == 1 else None
467+ )
468+ procurement_overview = _compute_procurement_overview (all_results , overview_fiscal_year )
469+ procurement_step_summary = _compute_procurement_step_summary (all_results , overview_fiscal_year )
461470
462471 # Apply pagination slicing
463472 if filters .limit is not None and filters .offset is not None :
@@ -836,6 +845,13 @@ def _validate_special_topics(db_session: Session, special_topics: List[SpecialTo
836845 raise ValidationError ({"special_topics" : [f"Special Topic IDs do not exist: { invalid_special_topic_ids } " ]})
837846
838847
848+ def _percent (value , total ):
849+ """Return ``value / total`` as a rounded whole-number percentage, or 0.0 when *total* is zero."""
850+ if total == 0 :
851+ return 0.0
852+ return float (round ((float (value ) / float (total )) * 100 ))
853+
854+
839855def _compute_agreement_totals (all_results : list [Agreement ]) -> dict [str , Any ]:
840856 """Compute aggregate totals across all filtered agreements for summary cards."""
841857 totals = {
@@ -917,11 +933,6 @@ def _compute_procurement_overview(all_results: list[Agreement], fiscal_year: int
917933 # This means per-status percentages may not sum to 100%.
918934 total_agreements = len (all_results )
919935
920- def _percent (value , total ):
921- if total == 0 :
922- return 0.0
923- return float (round ((float (value ) / float (total )) * 100 ))
924-
925936 status_data = []
926937 for status in tracked_statuses :
927938 amount = amount_by_status [status ]
@@ -988,11 +999,6 @@ def _compute_procurement_step_summary(all_results: list[Agreement], fiscal_year:
988999
9891000 total_agreement_count = sum (agreements_by_step .values ())
9901001
991- def _percent (value , total ):
992- if total == 0 :
993- return 0.0
994- return float (round ((float (value ) / float (total )) * 100 ))
995-
9961002 step_data = []
9971003 for step in range (1 , 7 ):
9981004 step_data .append (
@@ -1010,8 +1016,13 @@ def _percent(value, total):
10101016 }
10111017
10121018
1013- def _get_agreements (session : Session , agreement_cls : Type [Agreement ], data : dict [str , Any ]) -> Sequence [Agreement ]:
1014- query = _build_base_query (agreement_cls )
1019+ def _get_agreements (
1020+ session : Session ,
1021+ agreement_cls : Type [Agreement ],
1022+ data : dict [str , Any ],
1023+ include_procurement : bool = False ,
1024+ ) -> Sequence [Agreement ]:
1025+ query = _build_base_query (agreement_cls , include_procurement )
10151026 query = _apply_filters (query , agreement_cls , data )
10161027
10171028 logger .debug (f"query: { query } " )
@@ -1020,18 +1031,16 @@ def _get_agreements(session: Session, agreement_cls: Type[Agreement], data: dict
10201031 return _filter_by_ownership (all_results , data .get ("only_my" , []))
10211032
10221033
1023- def _build_base_query (agreement_cls : Type [Agreement ]) -> Select [tuple [Agreement ]]:
1024- return (
1025- select (agreement_cls )
1026- .distinct ()
1027- .join (BudgetLineItem , isouter = True )
1028- .join (CAN , isouter = True )
1029- .options (
1034+ def _build_base_query (agreement_cls : Type [Agreement ], include_procurement : bool = False ) -> Select [tuple [Agreement ]]:
1035+ query = select (agreement_cls ).distinct ().join (BudgetLineItem , isouter = True ).join (CAN , isouter = True )
1036+
1037+ if include_procurement :
1038+ query = query .options (
10301039 selectinload (agreement_cls .budget_line_items ),
10311040 selectinload (agreement_cls .procurement_trackers ),
10321041 )
1033- . order_by ( agreement_cls . id )
1034- )
1042+
1043+ return query . order_by ( agreement_cls . id )
10351044
10361045
10371046def _apply_filters (query : Select [Agreement ], agreement_cls : Type [Agreement ], data : dict [str , Any ]) -> Select [Agreement ]:
0 commit comments