Coverage for src/usaspending/models/idv.py: 79%

66 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-03 17:15 -0700

1"""IDV (Indefinite Delivery Vehicle) award model for USASpending data.""" 

2 

3from __future__ import annotations 

4from typing import Dict, Any, Optional 

5from functools import cached_property 

6 

7from .award import Award 

8from .location import Location 

9from ..utils.formatter import to_float 

10 

11class IDV(Award): 

12 """Indefinite Delivery Vehicle (IDV) award type. 

13  

14 IDVs are contract vehicles that provide for an indefinite quantity of supplies  

15 or services during a fixed period of time. They establish broad parameters and  

16 terms for ordering supplies/services, with specific orders placed against them  

17 via delivery orders or task orders. 

18  

19 Common IDV types include: 

20 - GWAC (Government-Wide Acquisition Contract) 

21 - IDC (Indefinite Delivery Contract)  

22 - FSS (Federal Supply Schedule) 

23 - BOA (Basic Ordering Agreement) 

24 - BPA (Blanket Purchase Agreement) 

25  

26 IDVs serve as parent contracts that streamline procurement by pre-negotiating 

27 terms, conditions, and pricing for future orders. They reduce administrative  

28 costs and enable faster acquisition of recurring needs. 

29  

30 Example: 

31 >>> # Find all IDVs for an agency 

32 >>> idvs = client.awards.search().idvs().for_agency("NASA").all() 

33 >>> for idv in idvs: 

34 ... print(f"{idv.piid}: {idv.recipient_name} - ${idv.total_obligation:,.2f}") 

35 """ 

36 

37 TYPE_FIELDS = [ 

38 "piid", 

39 "base_and_all_options", 

40 "contract_award_type", 

41 "naics_code", 

42 "naics_description", 

43 "naics_hierarchy", 

44 "psc_code", 

45 "psc_description", 

46 "psc_hierarchy", 

47 "latest_transaction_contract_data", 

48 ] 

49 

50 SEARCH_FIELDS = Award.SEARCH_FIELDS + [ 

51 "Start Date", 

52 "Award Amount", 

53 "Total Outlays", 

54 "Contract Award Type", 

55 "Last Date to Order", 

56 "NAICS", 

57 "PSC", 

58 ] 

59 

60 @property 

61 def piid(self) -> Optional[str]: 

62 """ 

63 Procurement Instrument Identifier - A unique identifier assigned to a federal 

64 contract, purchase order, basic ordering agreement, basic agreement, and 

65 blanket purchase agreement. It is used to track the contract, and any 

66 modifications or transactions related to it. After October 2017, it is 

67 between 13 and 17 digits, both letters and numbers. 

68 """ 

69 return self._lazy_get("piid") 

70 

71 @property 

72 def base_and_all_options(self) -> Optional[float]: 

73 """ 

74 For IDVs, this is the mutually agreed upon total contract value including all options (if any) 

75 AND the estimated value of all potential orders. For modifications enter the CHANGE, positive 

76 or negative of these values, if any. 

77 """ 

78 return to_float(self._lazy_get("base_and_all_options", default=None)) 

79 

80 @property 

81 def base_exercised_options(self) -> Optional[float]: 

82 """ 

83 The contract value for the base contract and any options that have been exercised. 

84 """ 

85 return to_float(self._lazy_get("base_exercised_options", default=None)) 

86 

87 @property 

88 def contract_award_type(self) -> Optional[str]: 

89 """Contract award type description.""" 

90 return self._lazy_get("contract_award_type", "Contract Award Type", "type_description") 

91 

92 @property 

93 def naics_code(self) -> Optional[str]: 

94 """NAICS industry classification code.""" 

95 naics_data = self._lazy_get("naics", "NAICS") 

96 if isinstance(naics_data, dict): 

97 return naics_data.get("code") 

98 if self.naics_hierarchy and isinstance(self.naics_hierarchy.get("base_code"), dict): 

99 return self.naics_hierarchy["base_code"].get("code") 

100 return None 

101 

102 @property 

103 def naics_description(self) -> Optional[str]: 

104 """NAICS industry classification description.""" 

105 naics_data = self._lazy_get("naics", "NAICS") 

106 if isinstance(naics_data, dict): 

107 return naics_data.get("description") 

108 return None 

109 

110 @property 

111 def psc_code(self) -> Optional[str]: 

112 """Product/Service Code (PSC) for contracts.""" 

113 psc_data = self._lazy_get("psc", "PSC") 

114 if isinstance(psc_data, dict): 

115 return psc_data.get("code") 

116 if self.psc_hierarchy and isinstance(self.psc_hierarchy.get("base_code"), dict): 

117 return self.psc_hierarchy["base_code"].get("code") 

118 return None 

119 

120 @property 

121 def psc_description(self) -> Optional[str]: 

122 """Product/Service Code (PSC) description.""" 

123 psc_data = self._lazy_get("psc", "PSC") 

124 if isinstance(psc_data, dict): 

125 return psc_data.get("description") 

126 return None 

127 

128 @cached_property 

129 def psc_hierarchy(self) -> Optional[Dict[str, Any]]: 

130 """Product/Service Code (PSC) hierarchy information.""" 

131 return self._lazy_get("psc_hierarchy") 

132 

133 @cached_property 

134 def naics_hierarchy(self) -> Optional[Dict[str, Any]]: 

135 """North American Industry Classification System (NAICS) hierarchy.""" 

136 return self._lazy_get("naics_hierarchy") 

137 

138 @cached_property 

139 def latest_transaction_contract_data(self) -> Optional[Dict[str, Any]]: 

140 """Latest contract transaction data with procurement-specific details.""" 

141 return self._lazy_get("latest_transaction_contract_data") 

142 

143 @cached_property 

144 def place_of_performance(self) -> Optional[Location]: 

145 """Award place of performance location. 

146 

147 Note: IDVs typically have null or empty place_of_performance data. 

148 """ 

149 data = self._lazy_get( 

150 "place_of_performance", "Primary Place of Performance", default=None 

151 ) 

152 if not isinstance(data, dict) or not data: 

153 return None 

154 

155 # Check if all values in the dict are None/null (common for IDV awards) 

156 if all(v is None for v in data.values()): 

157 return None 

158 

159 return Location(data, self._client)