@@ -45,22 +45,23 @@ def process_rep_data(rep_data)
4545
4646 if rep_data [ 'address_changed' ]
4747 api_response = get_best_address_candidate ( rep_data [ 'address' ] )
48-
49- # don't update the record if there is not a valid address with non-zero lat and long at this point
48+ # If address validation fails, log and continue to update non-address fields (email/phone)
5049 if api_response . nil?
51- return
50+ log_error ( "Address validation failed for Rep: #{ rep_data [ 'id' ] } . Proceeding to update non-address fields." )
5251 else
5352 address_validation_api_response = api_response
5453 end
5554 end
5655
57- update_rep_record ( rep_data , address_validation_api_response )
56+ updated_flags = update_rep_record ( rep_data , address_validation_api_response )
5857 rescue => e
5958 log_error ( "Update failed for Rep id: #{ rep_data } : #{ e . message } " )
6059 return
6160 end
6261
63- update_flagged_records ( rep_data )
62+ # Update flagged records only for fields that were actually updated
63+ updated_flags [ 'representative_id' ] = rep_data [ 'id' ] if updated_flags . is_a? ( Hash )
64+ update_flagged_records ( updated_flags || { } )
6465 end
6566
6667 def record_can_be_updated? ( rep_data )
@@ -73,16 +74,18 @@ def record_can_be_updated?(rep_data)
7374 def build_validation_address ( address )
7475 validation_model = VAProfile ::Models ::ValidationAddress
7576
77+ cleaned = Veteran ::AddressPreprocessor . clean ( address )
78+
7679 validation_model . new (
77- address_pou : address [ 'address_pou' ] ,
78- address_line1 : address [ 'address_line1' ] ,
79- address_line2 : address [ 'address_line2' ] ,
80- address_line3 : address [ 'address_line3' ] ,
81- city : address [ 'city' ] ,
82- state_code : address [ 'state' ] [ 'state_code' ] ,
83- zip_code : address [ 'zip_code5' ] ,
84- zip_code_suffix : address [ 'zip_code4' ] ,
85- country_code_iso3 : address [ 'country_code_iso3' ]
80+ address_pou : cleaned [ 'address_pou' ] || address [ 'address_pou' ] ,
81+ address_line1 : cleaned [ 'address_line1' ] ,
82+ address_line2 : cleaned [ 'address_line2' ] ,
83+ address_line3 : cleaned [ 'address_line3' ] ,
84+ city : cleaned [ 'city' ] || address [ 'city' ] ,
85+ state_code : cleaned . dig ( 'state' , 'state_code' ) || address . dig ( 'state' , 'state_code' ) ,
86+ zip_code : cleaned [ 'zip_code5' ] || address [ 'zip_code5' ] ,
87+ zip_code_suffix : cleaned [ 'zip_code4' ] || address [ 'zip_code4' ] ,
88+ country_code_iso3 : cleaned [ 'country_code_iso3' ] || address [ 'country_code_iso3' ]
8689 )
8790 end
8891
@@ -106,25 +109,56 @@ def address_valid?(response)
106109 # @param rep_data [Hash] Original rep_data containing the address and other details.
107110 # @param api_response [Hash] The response from the address validation service.
108111 def update_rep_record ( rep_data , api_response )
109- record =
110- Veteran ::Service ::Representative . find_by ( representative_id : rep_data [ 'id' ] )
111- if record . nil?
112- raise StandardError , 'Representative not found.'
113- else
114- address_attributes = rep_data [ 'address_changed' ] ? build_address_attributes ( rep_data , api_response ) : { }
115- email_attributes = rep_data [ 'email_changed' ] ? build_email_attributes ( rep_data ) : { }
116- phone_attributes = rep_data [ 'phone_number_changed' ] ? build_phone_attributes ( rep_data ) : { }
117- record . update ( merge_attributes ( address_attributes , email_attributes , phone_attributes ) )
118- end
112+ record = Veteran ::Service ::Representative . find_by ( representative_id : rep_data [ 'id' ] )
113+ raise StandardError , 'Representative not found.' if record . nil?
114+
115+ attributes , flags = build_rep_update_payload ( rep_data , api_response )
116+ record . update ( attributes )
117+ flags
119118 end
120119
121- # Updates flags for the representative's records based on changes in address, email, or phone number.
122- # @param rep_data [Hash] The representative data including the id and flags for changes.
123- def update_flagged_records ( rep_data )
124- representative_id = rep_data [ 'id' ]
125- update_flags ( representative_id , 'address' ) if rep_data [ 'address_changed' ]
126- update_flags ( representative_id , 'email' ) if rep_data [ 'email_changed' ]
127- update_flags ( representative_id , 'phone_number' ) if rep_data [ 'phone_number_changed' ]
120+ # Build attributes hash and flags indicating which fields changed
121+ # @return [Array<Hash, Hash>] first element is attributes, second is flags
122+ def build_rep_update_payload ( rep_data , api_response )
123+ flags = { }
124+
125+ address_attrs = if rep_data [ 'address_changed' ] && api_response . present?
126+ flags [ 'address' ] = true
127+ build_address_attributes ( rep_data , api_response )
128+ else
129+ { }
130+ end
131+
132+ email_attrs = if rep_data [ 'email_changed' ]
133+ flags [ 'email' ] = true
134+ build_email_attributes ( rep_data )
135+ else
136+ { }
137+ end
138+
139+ phone_attrs = if rep_data [ 'phone_number_changed' ]
140+ flags [ 'phone_number' ] = true
141+ build_phone_attributes ( rep_data )
142+ else
143+ { }
144+ end
145+
146+ [ merge_attributes ( address_attrs , email_attrs , phone_attrs ) , flags ]
147+ end
148+
149+ # Updates flags for the representative's records based on which fields were actually updated.
150+ # @param updated_flags [Hash] Hash with keys 'address', 'email', 'phone_number' set to true when updated.
151+ def update_flagged_records ( updated_flags )
152+ return unless updated_flags . is_a? ( Hash )
153+
154+ representative_id = updated_flags [ 'representative_id' ]
155+
156+ # If representative_id is missing, we can't update flags
157+ return if representative_id . blank?
158+
159+ update_flags ( representative_id , 'address' ) if updated_flags [ 'address' ]
160+ update_flags ( representative_id , 'email' ) if updated_flags [ 'email' ]
161+ update_flags ( representative_id , 'phone_number' ) if updated_flags [ 'phone_number' ]
128162 end
129163
130164 # Updates the flags for a representative's contact data indicating a change.
@@ -160,51 +194,35 @@ def merge_attributes(address, email, phone)
160194
161195 # Builds the attributes for the record update from the address, geocode, and metadata.
162196 # @param address [Hash] Address details from the validation response.
163- # @param geocode [Hash] Geocode details from the validation response.
164- # @param meta [Hash] Metadata about the address from the validation response.
165197 # @return [Hash] The attributes to update the record with.
166- def build_address ( address , geocode , meta )
167- {
168- address_type : meta [ 'address_type' ] ,
169- address_line1 : address [ 'address_line1' ] ,
170- address_line2 : address [ 'address_line2' ] ,
171- address_line3 : address [ 'address_line3' ] ,
172- city : address [ 'city' ] ,
173- province : address [ 'state_province' ] [ 'name' ] ,
174- state_code : address [ 'state_province' ] [ 'code' ] ,
175- zip_code : address [ 'zip_code5' ] ,
176- zip_suffix : address [ 'zip_code4' ] ,
177- country_code_iso3 : address [ 'country' ] [ 'iso3_code' ] ,
178- country_name : address [ 'country' ] [ 'name' ] ,
179- county_name : address . dig ( 'county' , 'name' ) ,
180- county_code : address . dig ( 'county' , 'county_fips_code' ) ,
181- lat : geocode [ 'latitude' ] ,
182- long : geocode [ 'longitude' ] ,
183- location : "POINT(#{ geocode [ 'longitude' ] } #{ geocode [ 'latitude' ] } )"
184- }
185- end
186-
187198 def build_v3_address ( address )
188199 {
189200 address_type : address [ 'address_type' ] ,
190201 address_line1 : address [ 'address_line1' ] ,
191202 address_line2 : address [ 'address_line2' ] ,
192203 address_line3 : address [ 'address_line3' ] ,
193204 city : address [ 'city_name' ] ,
194- province : address [ 'state' ] [ 'state_name' ] ,
195- state_code : address [ 'state' ] [ 'state_code' ] ,
205+ province : address . dig ( 'state' , 'state_name' ) ,
206+ state_code : address . dig ( 'state' , 'state_code' ) ,
196207 zip_code : address [ 'zip_code5' ] ,
197208 zip_suffix : address [ 'zip_code4' ] ,
198- country_code_iso3 : address [ 'country' ] [ 'iso3_code' ] ,
199- country_name : address [ 'country' ] [ 'country_name' ] ,
209+ country_code_iso3 : address . dig ( 'country' , 'iso3_code' ) ,
210+ country_name : address . dig ( 'country' , 'country_name' ) ,
200211 county_name : address . dig ( 'county' , 'county_name' ) ,
201212 county_code : address . dig ( 'county' , 'county_code' ) ,
202- lat : address [ 'geocode' ] [ 'latitude' ] ,
203- long : address [ 'geocode' ] [ 'longitude' ] ,
204- location : "POINT( #{ address [ 'geocode' ] [ 'longitude' ] } #{ address [ 'geocode' ] [ 'latitude' ] } )"
213+ lat : address . dig ( 'geocode' , 'latitude' ) ,
214+ long : address . dig ( 'geocode' , 'longitude' ) ,
215+ location : build_point ( address . dig ( 'geocode' , 'longitude' ) , address . dig ( 'geocode' , 'latitude' ) )
205216 }
206217 end
207218
219+ # Build a WKT point string if both coordinates are present
220+ def build_point ( longitude , latitude )
221+ return nil if longitude . blank? || latitude . blank?
222+
223+ "POINT(#{ longitude } #{ latitude } )"
224+ end
225+
208226 # Logs an error to Datadog and adds an error to the array that will be logged to slack.
209227 # @param error [Exception] The error string to be logged.
210228 def log_error ( error , send_to_slack : false )
@@ -252,9 +270,7 @@ def modified_validation(address, retry_count)
252270 # @param response [Hash, Nil] the response from the address validation service
253271 # @return [Boolean]
254272 def retriable? ( response )
255- return true if response . blank?
256-
257- !address_valid? ( response ) || lat_long_zero? ( response )
273+ response . blank? || !address_valid? ( response ) || lat_long_zero? ( response )
258274 end
259275
260276 # Retry address validation
@@ -297,21 +313,16 @@ def get_best_address_candidate(rep_address)
297313 raise
298314 end
299315
300- return nil unless address_valid? ( original_response )
301-
302- # retry validation if we get zero as the coordinates - this should indicate some warning with validation that
303- # is typically seen with addresses that mix street addresses with P.O. Boxes
304- if lat_long_zero? ( original_response )
316+ # If the original response is blank, invalid, or has zero coords, attempt retry logic which will
317+ # try modified address lines (including preprocessor-extracted PO Box) before giving up.
318+ if retriable? ( original_response )
305319 retry_response = retry_validation ( rep_address )
306320
307- if retriable? ( retry_response )
308- nil
309- else
310- retry_response
311- end
312- else
313- original_response
321+ return retriable? ( retry_response ) ? nil : retry_response
314322 end
323+
324+ # If we get here the original response was valid and not retriable (e.g. has non-zero coords)
325+ original_response
315326 end
316327
317328 # Determine if the backend exception represents a candidate address not found scenario
0 commit comments