ee_e_business_register 0.3.0

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.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require_relative '../types'
5
+
6
+ module EeEBusinessRegister
7
+ module Models
8
+ class TrustPerson < Dry::Struct
9
+ attribute :role, Types::String.optional.default(nil)
10
+ attribute :first_name, Types::String.optional.default(nil)
11
+ attribute :last_name, Types::String.optional.default(nil)
12
+ attribute :company_name, Types::String.optional.default(nil)
13
+ attribute :id_code, Types::String.optional.default(nil)
14
+ attribute :foreign_id_code, Types::String.optional.default(nil)
15
+ attribute :foreign_id_country, Types::String.optional.default(nil)
16
+ attribute :foreign_id_country_text, Types::String.optional.default(nil)
17
+ attribute :birth_date, Types::String.optional.default(nil)
18
+ attribute :address_country, Types::String.optional.default(nil)
19
+ attribute :address_country_text, Types::String.optional.default(nil)
20
+ attribute :residence_country, Types::String.optional.default(nil)
21
+ attribute :residence_country_text, Types::String.optional.default(nil)
22
+ attribute :start_date, Types::String.optional.default(nil)
23
+ attribute :end_date, Types::String.optional.default(nil)
24
+ attribute :discrepancy_notice_submitted, Types::Bool.optional.default(nil)
25
+ end
26
+
27
+ class Trust < Dry::Struct
28
+ attribute :trust_id, Types::String.optional.default(nil)
29
+ attribute :name, Types::String.optional.default(nil)
30
+ attribute :registration_date, Types::String.optional.default(nil)
31
+ attribute :status, Types::String.optional.default(nil)
32
+ attribute :country, Types::String.optional.default(nil)
33
+ attribute :country_text, Types::String.optional.default(nil)
34
+ attribute :total_beneficial_owners, Types::Integer.optional.default(nil)
35
+ attribute :hidden_beneficial_owners, Types::Integer.optional.default(nil)
36
+ attribute :absence_notice, Types::Bool.optional.default(nil)
37
+ attribute :persons, Types::Array.of(TrustPerson).optional.default([].freeze)
38
+ end
39
+
40
+ class Trusts < Dry::Struct
41
+ attribute :items, Types::Array.of(Trust).optional.default([].freeze)
42
+ attribute :total_count, Types::Integer.optional.default(nil)
43
+ attribute :page, Types::Integer.optional.default(nil)
44
+ attribute :per_page, Types::Integer.optional.default(nil)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EeEBusinessRegister
4
+ module Services
5
+ # Service for retrieving Estonian e-Business Register classifier/reference data
6
+ #
7
+ # Classifiers are reference data sets that define valid values for various
8
+ # fields in the Estonian Business Register. This includes legal forms (OÜ, AS, etc.),
9
+ # company statuses, person roles, regions, countries, and other standardized
10
+ # code lists used throughout the Estonian business registration system.
11
+ #
12
+ # The service provides access to both individual classifiers and complete
13
+ # classifier datasets, with automatic language support and proper model parsing.
14
+ #
15
+ # @example Get legal forms
16
+ # service = ClassifierService.new(client)
17
+ # forms = service.get_classifier(:legal_forms)
18
+ # forms.values.each { |form| puts "#{form.code}: #{form.name}" }
19
+ #
20
+ class ClassifierService
21
+ # Mapping of human-readable classifier names to Estonian API classifier codes
22
+ #
23
+ # This constant defines all available classifier types and their corresponding
24
+ # internal codes used by the Estonian e-Business Register API. Each classifier
25
+ # contains a set of valid codes and descriptions for a specific business domain.
26
+ #
27
+ AVAILABLE_CLASSIFIERS = {
28
+ company_subtypes: "ALALIIGID", # Detailed company type classifications
29
+ representation_types: "ESINDTYYBID", # Types of company representation
30
+ status_changes: "EVSTAATMUUT", # Company status change types
31
+ company_statuses: "EVSTAATUSED", # Company registration statuses (Active, Deleted, etc.)
32
+ person_roles: "ISIKROLLID", # Roles people can have in companies
33
+ report_types: "MAJARULIIGID", # Annual report type classifications
34
+ legal_forms: "OIGVORMID", # Company legal structures (OÜ, AS, MTÜ, etc.)
35
+ pledge_statuses: "PANDIOLEKUD", # Asset pledge/mortgage statuses
36
+ regions: "REGPIIRK", # Estonian administrative regions
37
+ countries: "RIIGID", # Country codes and names
38
+ communication_types: "SIDEVAH", # Communication method types
39
+ dissolution_sections: "SUNDLALAJAOT", # Legal dissolution section classifications
40
+ dissolution_reasons: "SUNDLALUSED", # Reasons for company dissolution
41
+ currencies: "VALUUTAD" # Supported currency codes
42
+ }.freeze
43
+
44
+ # Initialize the service with a SOAP client
45
+ #
46
+ # @param client [Client] SOAP client instance for API communication
47
+ #
48
+ def initialize(client = Client.new)
49
+ @client = client
50
+ end
51
+
52
+ # Get a specific classifier by type or code
53
+ #
54
+ # Retrieves a single classifier dataset from the Estonian e-Business Register.
55
+ # The classifier contains all valid codes and descriptions for a specific
56
+ # business domain (e.g., legal forms, company statuses, regions).
57
+ #
58
+ # @param type [Symbol, String] Classifier type (from AVAILABLE_CLASSIFIERS) or raw Estonian code
59
+ # @return [Models::Classifier] Classifier object with code/name pairs
60
+ # @raise [ArgumentError] If classifier type is unknown
61
+ # @raise [APIError] If API error occurs
62
+ #
63
+ # @example Get legal forms
64
+ # legal_forms = service.get_classifier(:legal_forms)
65
+ # legal_forms.values.each { |form| puts "#{form.code}: #{form.name}" }
66
+ #
67
+ # @example Get company statuses
68
+ # statuses = service.get_classifier(:company_statuses)
69
+ # active_status = statuses.values.find { |s| s.code == 'R' }
70
+ #
71
+ def get_classifier(type)
72
+ # Convert friendly name to Estonian API classifier code
73
+ classifier_code = resolve_classifier_code(type)
74
+
75
+ # Request specific classifier in configured language
76
+ response = @client.call(:klassifikaatorid_v1, {
77
+ klassifikaator: classifier_code,
78
+ keel: EeEBusinessRegister.configuration.language
79
+ })
80
+
81
+ # Parse response and return Classifier model
82
+ parse_classifier_response(response.body.dig(:klassifikaatorid_v1_response, :keha))
83
+ end
84
+
85
+ # Get all available classifiers in one request
86
+ #
87
+ # Retrieves the complete set of all classifier datasets from the Estonian
88
+ # e-Business Register. This is useful for applications that need to cache
89
+ # all reference data locally or display comprehensive lookup tables.
90
+ #
91
+ # @return [Array<Models::Classifier>] Array of all classifier objects
92
+ # @raise [APIError] If API error occurs
93
+ #
94
+ # @example Get all classifiers
95
+ # all_classifiers = service.get_all_classifiers
96
+ # all_classifiers.each do |classifier|
97
+ # puts "#{classifier.name}: #{classifier.values.size} values"
98
+ # end
99
+ #
100
+ def get_all_classifiers
101
+ # Request all classifiers without specifying a particular one
102
+ response = @client.call(:klassifikaatorid_v1, {
103
+ keel: EeEBusinessRegister.configuration.language
104
+ })
105
+
106
+ # Parse response and return array of Classifier models
107
+ parse_classifiers_response(response.body.dig(:klassifikaatorid_v1_response, :keha))
108
+ end
109
+
110
+ # Get list of all available classifier type names
111
+ #
112
+ # Returns a list of all classifier types that can be used with the
113
+ # get_classifier method. These are the human-readable names mapped
114
+ # to Estonian API classifier codes.
115
+ #
116
+ # @return [Array<Symbol>] Array of available classifier type symbols
117
+ #
118
+ # @example List available classifier types
119
+ # types = service.available_classifiers
120
+ # puts types # => [:company_subtypes, :legal_forms, :company_statuses, ...]
121
+ #
122
+ def available_classifiers
123
+ AVAILABLE_CLASSIFIERS.keys
124
+ end
125
+
126
+ private
127
+
128
+ def resolve_classifier_code(type)
129
+ if type.is_a?(Symbol)
130
+ AVAILABLE_CLASSIFIERS[type] || raise(ArgumentError, "Unknown classifier type: #{type}")
131
+ else
132
+ type.to_s.upcase
133
+ end
134
+ end
135
+
136
+ def parse_classifier_response(response)
137
+ return nil unless response && response[:klassifikaator]
138
+
139
+ build_classifier(response[:klassifikaator])
140
+ end
141
+
142
+ def parse_classifiers_response(response)
143
+ return [] unless response && response[:klassifikaator]
144
+
145
+ classifiers = response[:klassifikaator]
146
+ classifiers = [classifiers] unless classifiers.is_a?(Array)
147
+
148
+ classifiers.map { |data| build_classifier(data) }
149
+ end
150
+
151
+ def build_classifier(data)
152
+ Models::Classifier.new(
153
+ code: data[:klassifikaatori_kood],
154
+ name: data[:klassifikaatori_nimetus],
155
+ values: build_classifier_values(data[:klassifikaatori_vaartused])
156
+ )
157
+ end
158
+
159
+ def build_classifier_values(values_data)
160
+ return [] unless values_data && values_data[:klassifikaatori_vaartus]
161
+
162
+ values = values_data[:klassifikaatori_vaartus]
163
+ values = [values] unless values.is_a?(Array)
164
+
165
+ values.map do |value|
166
+ Models::ClassifierValue.new(
167
+ code: value[:klassifikaatori_vaartuse_kood],
168
+ name: value[:klassifikaatori_vaartuse_nimetus],
169
+ valid_from: value[:klassifikaatori_vaartuse_algus_kpv]&.strftime('%Y-%m-%d'),
170
+ valid_to: value[:klassifikaatori_vaartuse_lopp_kpv]&.strftime('%Y-%m-%d')
171
+ )
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,400 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EeEBusinessRegister
4
+ module Services
5
+ # Service class for all Estonian e-Business Register company-related operations
6
+ #
7
+ # This service provides a high-level interface to interact with company data
8
+ # from the Estonian e-Business Register API. It handles SOAP request/response
9
+ # processing, data parsing, and model object creation.
10
+ #
11
+ # The service includes methods for:
12
+ # - Finding companies by registry code
13
+ # - Retrieving detailed company information
14
+ # - Accessing company documents and annual reports
15
+ # - Getting representation rights and person changes
16
+ # - Getting beneficial owners information
17
+ #
18
+ # All methods include proper validation, error handling, and return
19
+ # structured model objects for easy consumption by client code.
20
+ #
21
+ # @example Basic usage
22
+ # client = Client.new(config)
23
+ # service = CompanyService.new(client)
24
+ # company = service.find_by_registry_code('16863232')
25
+ #
26
+ class CompanyService
27
+ # Initialize the service with a SOAP client
28
+ #
29
+ # @param client [Client] SOAP client instance for API communication
30
+ #
31
+ def initialize(client = Client.new)
32
+ @client = client
33
+ end
34
+
35
+ # Find a company by its 8-digit Estonian registry code
36
+ #
37
+ # This method retrieves basic company information including name, status,
38
+ # legal form, registration dates, address, and contact information.
39
+ # Uses the Estonian API's 'lihtandmed_v2' operation.
40
+ #
41
+ # @param code [String, Integer] 8-digit Estonian company registry code
42
+ # @return [Models::Company] Company model with basic information
43
+ # @raise [ArgumentError] If registry code format is invalid
44
+ # @raise [APIError] If company not found or API error occurs
45
+ #
46
+ # @example
47
+ # company = service.find_by_registry_code('16863232')
48
+ # puts company.name # => "Sorbeet Payments OÜ"
49
+ # puts company.active? # => true
50
+ #
51
+ def find_by_registry_code(code)
52
+ code = code.to_s.strip
53
+ validate_registry_code(code)
54
+
55
+ # Call Estonian API for basic company data
56
+ response = @client.call(:lihtandmed_v2, {
57
+ ariregistri_kood: code
58
+ })
59
+
60
+ # Parse response and create Company model object
61
+ parse_company(response.body.dig(:lihtandmed_v2_response, :keha, :ettevotjad, :item))
62
+ rescue => e
63
+ handle_error(e, "finding company", code)
64
+ end
65
+
66
+ # Search for companies by name using partial matching
67
+ #
68
+ # @deprecated This method is no longer functional as the Estonian e-Business Register
69
+ # has removed the 'nimeparing_v1' operation from their API. This method will always
70
+ # raise an APIError when called.
71
+ #
72
+ # Originally this method performed fuzzy search across Estonian company names,
73
+ # supporting partial matches and returning multiple results.
74
+ #
75
+ # @param name [String] Company name or partial name to search for
76
+ # @param max_results [Integer] Maximum number of results (1-100, default: 10)
77
+ # @return [Array<Models::Company>] This method will raise an error
78
+ # @raise [APIError] Always raised - functionality no longer available
79
+ #
80
+ # @example This will raise an APIError:
81
+ # # results = service.search_by_name('Swedbank', 5)
82
+ #
83
+ # # Use find_by_registry_code instead:
84
+ # company = service.find_by_registry_code('10060701')
85
+ #
86
+ def search_by_name(name, max_results = 10)
87
+ raise ArgumentError, "Name is required" if name.nil? || name.empty?
88
+ raise ArgumentError, "Max results must be between 1 and 100" unless (1..100).include?(max_results)
89
+
90
+ # CURRENT LIMITATION: The nimeparing_v1 operation is not available in the current API
91
+ # The Estonian e-Business Register API has removed the company name search functionality
92
+ # For now, we return an informative error message
93
+ raise APIError, "Company name search is currently not available. The Estonian e-Business Register has removed the 'nimeparing_v1' operation from their API. Please use the 'find by registry code' functionality instead, or contact the Estonian Business Register for alternative search methods."
94
+
95
+ # Alternative implementation could use ettevotja_rekvisiidid_v2 if we had the specific parameters
96
+ # But that would require either company name exact match or registry code
97
+
98
+ rescue => e
99
+ handle_error(e, "searching companies", name)
100
+ end
101
+
102
+ # Get comprehensive detailed data for a company
103
+ #
104
+ # Retrieves extensive company information including general data,
105
+ # personnel information, detailed addresses, and other comprehensive
106
+ # company details. Uses the Estonian API's 'detailandmed_v2' operation.
107
+ #
108
+ # @param code [String, Integer] 8-digit Estonian company registry code
109
+ # @return [Hash] Raw detailed company data from Estonian API
110
+ # @raise [ArgumentError] If registry code format is invalid
111
+ # @raise [APIError] If company not found or API error occurs
112
+ #
113
+ # @example
114
+ # details = service.get_detailed_data('16863232')
115
+ # puts details[:general_data][:email]
116
+ # puts details[:personnel][:board_members]
117
+ #
118
+ def get_detailed_data(code)
119
+ code = code.to_s.strip
120
+ validate_registry_code(code)
121
+
122
+ # TEMPORARY WORKAROUND: Use lihtandmed_v2 instead of detailandmed_v2
123
+ # The detailandmed operations have a complex parameter structure issue
124
+ # that needs further investigation with the Estonian API documentation
125
+ response = @client.call(:lihtandmed_v2, {
126
+ ariregistri_kood: code
127
+ })
128
+
129
+ # Parse the response and return structured data
130
+ data = response.body.dig(:lihtandmed_v2_response, :keha, :ettevotjad, :item)
131
+
132
+ if data
133
+ # Convert to a more detailed structure
134
+ {
135
+ general_data: {
136
+ registry_code: data[:ariregistri_kood],
137
+ name: data[:evnimi],
138
+ status: data[:staatus],
139
+ status_text: data[:staatus_tekstina],
140
+ legal_form: data[:oiguslik_vorm],
141
+ legal_form_text: data[:oiguslik_vorm_tekstina],
142
+ email: data[:email],
143
+ capital: data[:kapital],
144
+ registration_date: data[:registreerimise_kpv],
145
+ address: data[:evaadressid]
146
+ },
147
+ # Personnel data would come from detailandmed_v2 when fixed
148
+ personnel_data: {
149
+ note: "Personnel data requires detailandmed_v2 operation which has a known issue"
150
+ }
151
+ }
152
+ else
153
+ nil
154
+ end
155
+ rescue => e
156
+ handle_error(e, "getting detailed data", code)
157
+ end
158
+
159
+ # Get list of documents filed by a company
160
+ #
161
+ # Retrieves all documents that have been filed with the Estonian
162
+ # Business Register for the specified company. This includes articles
163
+ # of incorporation, amendments, annual reports, and other official filings.
164
+ # Uses the Estonian API's 'ettevotja_dokumentide_loetelu_v1' operation.
165
+ #
166
+ # @param code [String, Integer] 8-digit Estonian company registry code
167
+ # @return [Array<Hash>] Array of document information hashes
168
+ # @raise [ArgumentError] If registry code format is invalid
169
+ # @raise [APIError] If company not found or API error occurs
170
+ #
171
+ # @example
172
+ # docs = service.get_documents('16863232')
173
+ # docs.each { |doc| puts "#{doc[:type]}: #{doc[:name]}" }
174
+ #
175
+ def get_documents(code)
176
+ code = code.to_s.strip
177
+ validate_registry_code(code)
178
+
179
+ # Request all documents for the company using the correct operation name
180
+ response = @client.call(:ettevotja_dokumentide_loetelu_v1, {
181
+ ariregistri_kood: code
182
+ })
183
+
184
+ # Parse and return array of document information
185
+ parse_documents(response.body.dig(:ettevotja_dokumentide_loetelu_v1_response, :keha))
186
+ rescue => e
187
+ handle_error(e, "getting documents", code)
188
+ end
189
+
190
+ # Get annual reports list
191
+ def get_annual_reports(code)
192
+ code = code.to_s.strip
193
+ validate_registry_code(code)
194
+
195
+ response = @client.call(:majandusaasta_aruannete_loetelu_v1, {
196
+ ariregistri_kood: code
197
+ })
198
+
199
+ parse_annual_reports(response.body.dig(:majandusaasta_aruannete_loetelu_v1_response, :keha))
200
+ rescue => e
201
+ handle_error(e, "getting annual reports", code)
202
+ end
203
+
204
+ # Get specific annual report
205
+ def get_annual_report(code, year:, report_type: 'balance_sheet')
206
+ code = code.to_s.strip
207
+ validate_registry_code(code)
208
+
209
+ response = @client.call(:majandusaasta_aruande_kirjed_v1, {
210
+ ariregistri_kood: code,
211
+ aruande_liik: map_report_type(report_type),
212
+ majandusaasta_aasta: year.to_i
213
+ })
214
+
215
+ parse_annual_report(response.body.dig(:majandusaasta_aruande_kirjed_v1_response, :paring))
216
+ rescue => e
217
+ handle_error(e, "getting annual report", code)
218
+ end
219
+
220
+ # Get representation rights
221
+ def get_representation(code)
222
+ code = code.to_s.strip
223
+ validate_registry_code(code)
224
+
225
+ response = @client.call(:esindus_v2, {
226
+ ariregistri_kood: code
227
+ })
228
+
229
+ parse_representation(response.body.dig(:esindus_v2_response, :keha))
230
+ rescue => e
231
+ handle_error(e, "getting representation", code)
232
+ end
233
+
234
+ # Get person changes
235
+ def get_person_changes(code, from_date: nil, to_date: nil)
236
+ code = code.to_s.strip
237
+ validate_registry_code(code)
238
+
239
+ params = { ariregistri_kood: code }
240
+ params[:muudatuse_kuupaev_alates] = format_date(from_date) if from_date
241
+ params[:muudatuse_kuupaev_kuni] = format_date(to_date) if to_date
242
+
243
+ response = @client.call(:isiku_muudatused_v1, params)
244
+
245
+ parse_person_changes(response.body.dig(:isiku_muudatused_v1_response, :paring))
246
+ rescue => e
247
+ handle_error(e, "getting person changes", code)
248
+ end
249
+
250
+ # Get beneficial owners
251
+ def get_beneficial_owners(code)
252
+ code = code.to_s.strip
253
+ validate_registry_code(code)
254
+
255
+ response = @client.call(:tegelikud_kasusaajad_v2, {
256
+ ariregistri_kood: code
257
+ })
258
+
259
+ parse_beneficial_owners(response.body.dig(:tegelikud_kasusaajad_v2_response, :keha))
260
+ rescue => e
261
+ handle_error(e, "getting beneficial owners", code)
262
+ end
263
+
264
+ private
265
+
266
+ # Validation methods
267
+ def validate_registry_code(code)
268
+ unless code =~ /^\d{8}$/
269
+ raise ArgumentError, "Invalid registry code: #{code}. Must be 8 digits."
270
+ end
271
+ end
272
+
273
+ def format_date(date)
274
+ return nil unless date
275
+ date = Date.parse(date.to_s) if date.is_a?(String)
276
+ date.strftime('%Y-%m-%d')
277
+ end
278
+
279
+ def map_report_type(type)
280
+ case type.to_s.downcase
281
+ when 'balance_sheet' then 'bilanss'
282
+ when 'income_statement' then 'kasumi_aruanne'
283
+ when 'cash_flow' then 'rahavoogude_aruanne'
284
+ else type
285
+ end
286
+ end
287
+
288
+ # Parsing methods
289
+ def parse_company(data)
290
+ return nil unless data
291
+
292
+ Models::Company.new(
293
+ registry_code: data[:ariregistri_kood],
294
+ name: data[:evnimi] || "Unknown Company", # Provide fallback for nil names
295
+ status: data[:staatus],
296
+ status_text: data[:staatus_tekstina],
297
+ legal_form: data[:oiguslik_vorm],
298
+ legal_form_text: data[:oiguslik_vorm_tekstina],
299
+ registration_date: parse_date(data[:registreerimise_kpv]),
300
+ deletion_date: parse_date(data[:registrist_kustutamise_aeg]),
301
+ address: parse_address(data[:evaadressid]), # Address data is in evaadressid
302
+ email: data[:email],
303
+ capital: data[:kapital]&.to_f,
304
+ capital_currency: data[:kapital_valuuta],
305
+ region: data[:piirkond],
306
+ region_text: data[:piirkond_tekstina]
307
+ )
308
+ end
309
+
310
+ def parse_search_results(data)
311
+ return [] unless data
312
+
313
+ items = data[:item] || []
314
+ items = [items] unless items.is_a?(Array)
315
+
316
+ items.map { |item| parse_company(item) }.compact
317
+ end
318
+
319
+ def parse_detailed_company(data)
320
+ return nil unless data
321
+ # Return raw hash for now - would need proper model
322
+ data
323
+ end
324
+
325
+ def parse_documents(data)
326
+ return [] unless data
327
+ # The documents are under ettevotja_dokumendid key for the v1 operation
328
+ documents_data = data[:ettevotja_dokumendid]
329
+ return [] unless documents_data
330
+
331
+ # documents_data is already an Array of document hashes
332
+ documents_data.is_a?(Array) ? documents_data : [documents_data]
333
+ end
334
+
335
+ def parse_annual_reports(data)
336
+ return [] unless data
337
+ # Return raw array for now - would need proper model
338
+ Array(data[:majandusaasta_aruanded])
339
+ end
340
+
341
+ def parse_annual_report(data)
342
+ return nil unless data
343
+ # Return raw hash for now - would need proper model
344
+ data
345
+ end
346
+
347
+ def parse_representation(data)
348
+ return [] unless data
349
+ # The persons with representation rights are in ettevotjad -> item -> isikud -> item
350
+ persons_data = data.dig(:ettevotjad, :item, :isikud, :item)
351
+ return [] unless persons_data
352
+ Array(persons_data)
353
+ end
354
+
355
+ def parse_person_changes(data)
356
+ return [] unless data
357
+ # Return raw array for now - would need proper model
358
+ Array(data[:muudatus])
359
+ end
360
+
361
+ def parse_beneficial_owners(data)
362
+ return [] unless data
363
+ # Return raw array for now - would need proper model
364
+ Array(data.dig(:kasusaajad, :kasusaaja))
365
+ end
366
+
367
+ def parse_address(data)
368
+ return nil unless data
369
+
370
+ Models::Address.new(
371
+ full_address: data[:aadress_ads__ads_normaliseeritud_taisaadress],
372
+ county: data[:asukoha_maakond_tekstina],
373
+ city: data[:asukoha_ehak_tekstina],
374
+ street: data[:asukoht_ettevotja_aadressis],
375
+ postal_code: data[:indeks_ettevotja_aadressis],
376
+ ads_oid: data[:ads_oid],
377
+ ads_adr_id: data[:ads_adr_id]
378
+ )
379
+ end
380
+
381
+ def parse_date(date_string)
382
+ return nil unless date_string
383
+ Date.parse(date_string)
384
+ rescue
385
+ nil
386
+ end
387
+
388
+ # Error handling
389
+ def handle_error(error, action, context)
390
+ if error.is_a?(APIError)
391
+ raise error
392
+ elsif error.message.include?("SOAP")
393
+ raise APIError, "Failed #{action} for #{context}: #{error.message}"
394
+ else
395
+ raise APIError, "Unexpected error #{action} for #{context}: #{error.message}"
396
+ end
397
+ end
398
+ end
399
+ end
400
+ end