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.
- checksums.yaml +7 -0
- data/.ee_business_register_credentials.yml.example +8 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +93 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/Makefile +392 -0
- data/README.md +1294 -0
- data/Rakefile +12 -0
- data/lib/ee_e_business_register/client.rb +224 -0
- data/lib/ee_e_business_register/configuration.rb +160 -0
- data/lib/ee_e_business_register/errors.rb +98 -0
- data/lib/ee_e_business_register/models/classifier.rb +28 -0
- data/lib/ee_e_business_register/models/company.rb +2363 -0
- data/lib/ee_e_business_register/models/trust.rb +47 -0
- data/lib/ee_e_business_register/services/classifier_service.rb +176 -0
- data/lib/ee_e_business_register/services/company_service.rb +400 -0
- data/lib/ee_e_business_register/services/trusts_service.rb +136 -0
- data/lib/ee_e_business_register/types.rb +24 -0
- data/lib/ee_e_business_register/validation.rb +367 -0
- data/lib/ee_e_business_register/version.rb +5 -0
- data/lib/ee_e_business_register.rb +481 -0
- data/sig/ee_e_business_register.rbs +4 -0
- metadata +212 -0
|
@@ -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
|