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
data/README.md
ADDED
|
@@ -0,0 +1,1294 @@
|
|
|
1
|
+
# ποΈ Estonian e-Business Register
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/ee_e_business_register)
|
|
4
|
+
[](https://www.ruby-lang.org)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**Clean & Simple Ruby client for the Estonian e-Business Register API**
|
|
8
|
+
|
|
9
|
+
A professional Ruby interface for accessing Estonian company data, following the KISS principle. Built for developers who value simplicity, reliability, and clean code architecture.
|
|
10
|
+
|
|
11
|
+
**Developed by [Sorbeet Payments OΓ](https://sorbet.ee)** - Estonia's premier FinTech infrastructure company.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## β¨ Features
|
|
16
|
+
|
|
17
|
+
- π **Simple & Fast** - Clean API with intuitive method names
|
|
18
|
+
- π‘οΈ **Type Safe** - Immutable data models using dry-struct
|
|
19
|
+
- π **Bilingual** - Estonian and English language support
|
|
20
|
+
- π **Complete Coverage** - Access to all Estonian business data
|
|
21
|
+
- π **Production Ready** - Built-in error handling and retry logic
|
|
22
|
+
- π― **Developer Friendly** - Comprehensive documentation and examples
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## π Quick Start
|
|
27
|
+
|
|
28
|
+
### Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gem install ee_e_business_register
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or add to your Gemfile:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
gem 'ee_e_business_register'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Basic Setup
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require 'ee_e_business_register'
|
|
44
|
+
|
|
45
|
+
# Configure with your Estonian API credentials
|
|
46
|
+
EeEBusinessRegister.configure do |config|
|
|
47
|
+
config.username = 'your_username'
|
|
48
|
+
config.password = 'your_password'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Find a company by registry code
|
|
52
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
53
|
+
puts company.name # => "Sorbeet Payments OΓ"
|
|
54
|
+
puts company.active? # => true
|
|
55
|
+
puts company.legal_form_text # => "OsaΓΌhing"
|
|
56
|
+
puts company.email # => "info@sorbeet.ee"
|
|
57
|
+
|
|
58
|
+
# Access comprehensive company details
|
|
59
|
+
details = EeEBusinessRegister.get_company_details(company.registry_code)
|
|
60
|
+
puts details[:general_data] if details[:general_data]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## π Complete API Reference
|
|
66
|
+
|
|
67
|
+
### π’ Company Operations
|
|
68
|
+
|
|
69
|
+
#### `find_company(registry_code)`
|
|
70
|
+
Get basic company information by 8-digit registry code.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
# Find a specific company
|
|
74
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
75
|
+
|
|
76
|
+
puts "Company: #{company.name}"
|
|
77
|
+
puts "Status: #{company.active? ? 'Active' : 'Inactive'}"
|
|
78
|
+
puts "Legal form: #{company.legal_form_text}"
|
|
79
|
+
puts "Registered: #{company.registration_date}"
|
|
80
|
+
puts "Email: #{company.email}"
|
|
81
|
+
puts "Capital: #{company.capital} #{company.capital_currency}"
|
|
82
|
+
|
|
83
|
+
# Address information
|
|
84
|
+
address = company.address
|
|
85
|
+
puts "Address: #{address.full_address}"
|
|
86
|
+
puts "County: #{address.county}"
|
|
87
|
+
puts "Postal code: #{address.postal_code}"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Company Search [DEPRECATED]
|
|
91
|
+
β οΈ **Company name search is no longer available.** The Estonian e-Business Register removed the company name search operation from their API as of 2024.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
# This functionality is no longer supported:
|
|
95
|
+
# banks = EeEBusinessRegister.search_companies('pank') # β Will raise APIError
|
|
96
|
+
|
|
97
|
+
# Use find_company with registry code instead:
|
|
98
|
+
company = EeEBusinessRegister.find_company('10060701') # β
Swedbank
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `get_company_details(registry_code)`
|
|
102
|
+
Get comprehensive company data including personnel and detailed information.
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
details = EeEBusinessRegister.get_company_details('16863232')
|
|
106
|
+
# Returns raw hash with comprehensive data
|
|
107
|
+
puts details.inspect
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### π Documents & Reports
|
|
111
|
+
|
|
112
|
+
#### `get_annual_reports(registry_code)`
|
|
113
|
+
Get list of annual reports filed by a company.
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
reports = EeEBusinessRegister.get_annual_reports('16863232')
|
|
117
|
+
reports.each do |report|
|
|
118
|
+
puts "Year #{report[:year]}: #{report[:status]}"
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### `get_annual_report(registry_code, year, type: 'balance_sheet')`
|
|
123
|
+
Get specific annual report data.
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
# Get balance sheet for 2023
|
|
127
|
+
report = EeEBusinessRegister.get_annual_report('16863232', 2023)
|
|
128
|
+
|
|
129
|
+
# Get income statement
|
|
130
|
+
income = EeEBusinessRegister.get_annual_report('16863232', 2023, type: 'income_statement')
|
|
131
|
+
|
|
132
|
+
# Get cash flow statement
|
|
133
|
+
cash_flow = EeEBusinessRegister.get_annual_report('16863232', 2023, type: 'cash_flow')
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### `get_documents(registry_code)`
|
|
137
|
+
Get list of documents filed by a company.
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
documents = EeEBusinessRegister.get_documents('16863232')
|
|
141
|
+
documents.each do |doc|
|
|
142
|
+
puts "#{doc[:type]}: #{doc[:name]}"
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### π₯ Personnel & Representation
|
|
147
|
+
|
|
148
|
+
#### `get_representatives(registry_code)`
|
|
149
|
+
Get people authorized to represent the company.
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
reps = EeEBusinessRegister.get_representatives('16863232')
|
|
153
|
+
reps.each do |rep|
|
|
154
|
+
puts "#{rep[:name]} - #{rep[:role]}"
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### `get_person_changes(registry_code, from: nil, to: nil)`
|
|
159
|
+
Get personnel changes for a company within a date range.
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# All changes
|
|
163
|
+
changes = EeEBusinessRegister.get_person_changes('16863232')
|
|
164
|
+
|
|
165
|
+
# Changes since specific date
|
|
166
|
+
recent = EeEBusinessRegister.get_person_changes('16863232', from: '2023-01-01')
|
|
167
|
+
|
|
168
|
+
# Changes in date range
|
|
169
|
+
range = EeEBusinessRegister.get_person_changes('16863232',
|
|
170
|
+
from: '2023-01-01',
|
|
171
|
+
to: '2023-12-31'
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### π Reference Data
|
|
176
|
+
|
|
177
|
+
#### `get_legal_forms()`
|
|
178
|
+
Get all available legal form types.
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
forms = EeEBusinessRegister.get_legal_forms
|
|
182
|
+
forms.each { |form| puts "#{form[:code]}: #{form[:name]}" }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### `get_company_statuses()`
|
|
186
|
+
Get all company status types.
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
statuses = EeEBusinessRegister.get_company_statuses
|
|
190
|
+
statuses.each { |status| puts "#{status[:code]}: #{status[:name]}" }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### `get_classifier(type)`
|
|
194
|
+
Get any classifier by type.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# Available classifier types:
|
|
198
|
+
# :legal_forms, :company_statuses, :person_roles, :regions,
|
|
199
|
+
# :countries, :report_types, :company_subtypes, :currencies
|
|
200
|
+
|
|
201
|
+
regions = EeEBusinessRegister.get_classifier(:regions)
|
|
202
|
+
countries = EeEBusinessRegister.get_classifier(:countries)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### `list_classifiers()`
|
|
206
|
+
Get descriptions of all available classifiers.
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
EeEBusinessRegister.list_classifiers
|
|
210
|
+
# => {
|
|
211
|
+
# legal_forms: "Company legal structures (OΓ, AS, MTΓ, etc.)",
|
|
212
|
+
# company_statuses: "Registration statuses (Active, Liquidation, Deleted)",
|
|
213
|
+
# ...
|
|
214
|
+
# }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### π οΈ Utility Methods
|
|
218
|
+
|
|
219
|
+
#### `check_health()`
|
|
220
|
+
Check if the Estonian API is accessible and working.
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
if EeEBusinessRegister.check_health
|
|
224
|
+
puts "β
Estonian API is working"
|
|
225
|
+
else
|
|
226
|
+
puts "β Estonian API is not responding"
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `validate_code(registry_code)`
|
|
231
|
+
Validate if a registry code has the correct format.
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
EeEBusinessRegister.validate_code('16863232') # => true
|
|
235
|
+
EeEBusinessRegister.validate_code('123') # => false
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### π οΈ Beneficial Owners
|
|
239
|
+
|
|
240
|
+
#### `get_beneficial_owners(registry_code)`
|
|
241
|
+
Get beneficial ownership information for a company.
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
owners = EeEBusinessRegister.get_beneficial_owners('16863232')
|
|
245
|
+
owners.each do |owner|
|
|
246
|
+
name = "#{owner[:eesnimi]} #{owner[:nimi]}"
|
|
247
|
+
country = owner[:valis_kood_riik_tekstina]
|
|
248
|
+
control = owner[:kontrolli_teostamise_viis_tekstina]
|
|
249
|
+
|
|
250
|
+
puts "#{name} (#{country}) - #{control}"
|
|
251
|
+
end
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## ποΈ Architecture
|
|
257
|
+
|
|
258
|
+
### Clean Architecture Design
|
|
259
|
+
|
|
260
|
+
The gem follows a **clean architecture** pattern with clear separation of concerns:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
264
|
+
β PUBLIC API β
|
|
265
|
+
β EeEBusinessRegister.find_company() β
|
|
266
|
+
β EeEBusinessRegister.get_company_details() β
|
|
267
|
+
β EeEBusinessRegister.get_beneficial_owners() β
|
|
268
|
+
β EeEBusinessRegister.get_classifier() β
|
|
269
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
270
|
+
β
|
|
271
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
272
|
+
β SERVICE LAYER β
|
|
273
|
+
β Services::CompanyService β
|
|
274
|
+
β Services::ClassifierService β
|
|
275
|
+
β Services::TrustsService β
|
|
276
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
277
|
+
β
|
|
278
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
279
|
+
β CLIENT LAYER β
|
|
280
|
+
β Client (SOAP communication) β
|
|
281
|
+
β - Authentication β
|
|
282
|
+
β - Request/Response handling β
|
|
283
|
+
β - Retry logic β
|
|
284
|
+
β - Error translation β
|
|
285
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
286
|
+
β
|
|
287
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
288
|
+
β DATA MODELS β
|
|
289
|
+
β Models::Company (immutable) β
|
|
290
|
+
β Models::Address (immutable) β
|
|
291
|
+
β Type-safe with dry-struct β
|
|
292
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Key Components
|
|
296
|
+
|
|
297
|
+
#### 1. **Configuration**
|
|
298
|
+
Centralized configuration with sensible defaults:
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
EeEBusinessRegister.configure do |config|
|
|
302
|
+
config.username = 'your_username' # Required: API username
|
|
303
|
+
config.password = 'your_password' # Required: API password
|
|
304
|
+
config.language = 'eng' # 'eng' or 'est' (default: 'eng')
|
|
305
|
+
config.timeout = 30 # Request timeout in seconds
|
|
306
|
+
config.test_mode = false # Use test endpoints
|
|
307
|
+
config.logger = Rails.logger # Optional logger
|
|
308
|
+
end
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
#### 2. **SOAP Client**
|
|
312
|
+
Handles all communication with the Estonian API:
|
|
313
|
+
|
|
314
|
+
- **Authentication**: WSSE username/password authentication
|
|
315
|
+
- **Retry Logic**: Automatic retry with exponential backoff for timeouts
|
|
316
|
+
- **Error Handling**: Converts SOAP faults to friendly Ruby exceptions
|
|
317
|
+
- **Language Support**: Automatically adds language parameter to requests
|
|
318
|
+
|
|
319
|
+
#### 3. **Service Classes**
|
|
320
|
+
Business logic layer that orchestrates API calls and data parsing:
|
|
321
|
+
|
|
322
|
+
```ruby
|
|
323
|
+
# Direct service usage for advanced scenarios
|
|
324
|
+
client = EeEBusinessRegister::Client.new
|
|
325
|
+
service = EeEBusinessRegister::Services::CompanyService.new(client)
|
|
326
|
+
company = service.find_by_registry_code('16863232')
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
#### 4. **Type-Safe Models**
|
|
330
|
+
All data returned as immutable, validated structs:
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
334
|
+
|
|
335
|
+
# These are guaranteed types
|
|
336
|
+
company.name # String
|
|
337
|
+
company.active? # Boolean
|
|
338
|
+
company.registration_date # String (YYYY-MM-DD format)
|
|
339
|
+
company.address.postal_code # String or nil
|
|
340
|
+
company.capital # Float or nil
|
|
341
|
+
|
|
342
|
+
# Attempting to modify raises an error (immutable)
|
|
343
|
+
company.name = "New name" # => Dry::Struct::Error
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Data Flow
|
|
347
|
+
|
|
348
|
+
1. **Request**: User calls public API method
|
|
349
|
+
2. **Validation**: Input parameters are validated and sanitized
|
|
350
|
+
3. **Service**: Appropriate service class handles business logic
|
|
351
|
+
4. **Client**: SOAP client makes authenticated request to Estonian API
|
|
352
|
+
5. **Parsing**: Raw XML/SOAP response is parsed into structured data
|
|
353
|
+
6. **Models**: Data is converted to immutable, type-safe model objects
|
|
354
|
+
7. **Response**: Clean, validated data is returned to user
|
|
355
|
+
|
|
356
|
+
### Error Handling Strategy
|
|
357
|
+
|
|
358
|
+
The gem uses a hierarchical error handling approach:
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
begin
|
|
362
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
363
|
+
rescue EeEBusinessRegister::ValidationError => e
|
|
364
|
+
puts "Invalid input: #{e.message}"
|
|
365
|
+
rescue EeEBusinessRegister::AuthenticationError => e
|
|
366
|
+
puts "Check credentials: #{e.message}"
|
|
367
|
+
rescue EeEBusinessRegister::APIError => e
|
|
368
|
+
puts "API error: #{e.message}"
|
|
369
|
+
rescue EeEBusinessRegister::Error => e
|
|
370
|
+
puts "General error: #{e.message}"
|
|
371
|
+
end
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Error Types:**
|
|
375
|
+
- `ValidationError` - Invalid input parameters
|
|
376
|
+
- `AuthenticationError` - Invalid API credentials
|
|
377
|
+
- `ConfigurationError` - Missing or invalid configuration
|
|
378
|
+
- `APIError` - Estonian API service errors, network issues, timeouts
|
|
379
|
+
- `Error` - Base class for all gem errors
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## π Working Examples
|
|
384
|
+
|
|
385
|
+
### Example 1: Company Analysis Dashboard
|
|
386
|
+
|
|
387
|
+
```ruby
|
|
388
|
+
require 'ee_e_business_register'
|
|
389
|
+
|
|
390
|
+
class CompanyAnalyzer
|
|
391
|
+
def initialize
|
|
392
|
+
EeEBusinessRegister.configure do |config|
|
|
393
|
+
config.username = ENV['EE_API_USERNAME']
|
|
394
|
+
config.password = ENV['EE_API_PASSWORD']
|
|
395
|
+
config.language = 'eng'
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def analyze_company(registry_code)
|
|
400
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
401
|
+
return nil unless company
|
|
402
|
+
|
|
403
|
+
details = EeEBusinessRegister.get_company_details(registry_code)
|
|
404
|
+
reports = EeEBusinessRegister.get_annual_reports(registry_code)
|
|
405
|
+
|
|
406
|
+
{
|
|
407
|
+
basic_info: {
|
|
408
|
+
name: company.name,
|
|
409
|
+
status: company.active? ? 'Active' : 'Inactive',
|
|
410
|
+
age_years: EeEBusinessRegister.company_age(registry_code),
|
|
411
|
+
legal_form: company.legal_form_text,
|
|
412
|
+
capital: format_currency(company.capital, company.capital_currency)
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
location: {
|
|
416
|
+
address: company.address&.full_address,
|
|
417
|
+
county: company.address&.county,
|
|
418
|
+
postal_code: company.address&.postal_code
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
compliance: {
|
|
422
|
+
latest_report_year: EeEBusinessRegister.latest_report_year(registry_code),
|
|
423
|
+
total_reports: reports&.size || 0,
|
|
424
|
+
has_recent_reports: EeEBusinessRegister.has_reports_for_year?(registry_code, Date.current.year - 1)
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
contact: {
|
|
428
|
+
email: company.email
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def analyze_multiple_companies(registry_codes)
|
|
434
|
+
registry_codes.map do |code|
|
|
435
|
+
begin
|
|
436
|
+
company = EeEBusinessRegister.find_company(code)
|
|
437
|
+
{
|
|
438
|
+
name: company.name,
|
|
439
|
+
registry_code: company.registry_code,
|
|
440
|
+
status: company.active? ? 'Active' : 'Inactive',
|
|
441
|
+
county: company.address&.county,
|
|
442
|
+
legal_form: company.legal_form_text
|
|
443
|
+
}
|
|
444
|
+
rescue => e
|
|
445
|
+
{ registry_code: code, error: e.message }
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
private
|
|
451
|
+
|
|
452
|
+
def format_currency(amount, currency)
|
|
453
|
+
return 'N/A' unless amount && currency
|
|
454
|
+
"#{currency} #{amount.round(2)}"
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# Usage
|
|
459
|
+
analyzer = CompanyAnalyzer.new
|
|
460
|
+
analysis = analyzer.analyze_company('16863232')
|
|
461
|
+
puts analysis.inspect
|
|
462
|
+
|
|
463
|
+
# Analyze multiple companies at once
|
|
464
|
+
companies = analyzer.analyze_multiple_companies(['10060701', '16863232', '11051370'])
|
|
465
|
+
companies.each { |c| puts "#{c[:name]}: #{c[:status]}" if c[:name] }
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Example 2: Compliance Monitor
|
|
469
|
+
|
|
470
|
+
```ruby
|
|
471
|
+
class ComplianceMonitor
|
|
472
|
+
def initialize
|
|
473
|
+
EeEBusinessRegister.configure do |config|
|
|
474
|
+
config.username = ENV['EE_API_USERNAME']
|
|
475
|
+
config.password = ENV['EE_API_PASSWORD']
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def check_companies(registry_codes)
|
|
480
|
+
results = []
|
|
481
|
+
|
|
482
|
+
registry_codes.each do |code|
|
|
483
|
+
begin
|
|
484
|
+
company = EeEBusinessRegister.find_company(code)
|
|
485
|
+
|
|
486
|
+
if company.nil?
|
|
487
|
+
results << { code: code, status: 'NOT_FOUND', issues: ['Company not found'] }
|
|
488
|
+
next
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
issues = []
|
|
492
|
+
issues << 'Inactive company' unless company.active?
|
|
493
|
+
issues << 'No email address' if company.email.nil? || company.email.empty?
|
|
494
|
+
issues << 'Old reports' unless has_recent_reports?(code)
|
|
495
|
+
|
|
496
|
+
results << {
|
|
497
|
+
code: code,
|
|
498
|
+
name: company.name,
|
|
499
|
+
status: company.active? ? 'ACTIVE' : 'INACTIVE',
|
|
500
|
+
issues: issues,
|
|
501
|
+
compliance_score: calculate_compliance_score(company, code)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
rescue EeEBusinessRegister::Error => e
|
|
505
|
+
results << { code: code, status: 'ERROR', error: e.message }
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
results
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def generate_report(results)
|
|
513
|
+
active_companies = results.count { |r| r[:status] == 'ACTIVE' }
|
|
514
|
+
total_issues = results.sum { |r| r[:issues]&.size || 0 }
|
|
515
|
+
|
|
516
|
+
puts "=== COMPLIANCE REPORT ==="
|
|
517
|
+
puts "Total companies checked: #{results.size}"
|
|
518
|
+
puts "Active companies: #{active_companies}"
|
|
519
|
+
puts "Total compliance issues: #{total_issues}"
|
|
520
|
+
puts ""
|
|
521
|
+
|
|
522
|
+
results.each do |result|
|
|
523
|
+
puts "#{result[:code]}: #{result[:name] || 'N/A'}"
|
|
524
|
+
puts " Status: #{result[:status]}"
|
|
525
|
+
if result[:issues] && !result[:issues].empty?
|
|
526
|
+
puts " Issues: #{result[:issues].join(', ')}"
|
|
527
|
+
end
|
|
528
|
+
if result[:compliance_score]
|
|
529
|
+
puts " Compliance score: #{result[:compliance_score]}/100"
|
|
530
|
+
end
|
|
531
|
+
puts ""
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
private
|
|
536
|
+
|
|
537
|
+
def has_recent_reports?(registry_code)
|
|
538
|
+
last_year = Date.current.year - 1
|
|
539
|
+
EeEBusinessRegister.has_reports_for_year?(registry_code, last_year)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def calculate_compliance_score(company, code)
|
|
543
|
+
score = 100
|
|
544
|
+
score -= 30 unless company.active?
|
|
545
|
+
score -= 20 if company.email.nil? || company.email.empty?
|
|
546
|
+
score -= 25 unless has_recent_reports?(code)
|
|
547
|
+
score -= 15 if company.address&.full_address.nil?
|
|
548
|
+
[score, 0].max
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
# Usage
|
|
553
|
+
monitor = ComplianceMonitor.new
|
|
554
|
+
companies_to_check = ['16863232', '10000001', '12345678']
|
|
555
|
+
results = monitor.check_companies(companies_to_check)
|
|
556
|
+
monitor.generate_report(results)
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Example 3: Beneficial Owners Analyzer
|
|
560
|
+
|
|
561
|
+
```ruby
|
|
562
|
+
class OwnershipAnalyzer
|
|
563
|
+
def initialize
|
|
564
|
+
EeEBusinessRegister.configure do |config|
|
|
565
|
+
config.username = ENV['EE_API_USERNAME']
|
|
566
|
+
config.password = ENV['EE_API_PASSWORD']
|
|
567
|
+
config.language = 'eng'
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def analyze_ownership(registry_code)
|
|
572
|
+
begin
|
|
573
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
574
|
+
owners = EeEBusinessRegister.get_beneficial_owners(registry_code)
|
|
575
|
+
|
|
576
|
+
{
|
|
577
|
+
company: {
|
|
578
|
+
name: company.name,
|
|
579
|
+
registry_code: company.registry_code,
|
|
580
|
+
status: company.active? ? 'Active' : 'Inactive'
|
|
581
|
+
},
|
|
582
|
+
ownership: {
|
|
583
|
+
total_owners: owners.size,
|
|
584
|
+
active_owners: owners.count { |o| !o[:lopp_kpv] },
|
|
585
|
+
foreign_owners: owners.count { |o| o[:valis_kood_riik] != 'EST' },
|
|
586
|
+
control_methods: owners.map { |o| o[:kontrolli_teostamise_viis_tekstina] }.uniq
|
|
587
|
+
},
|
|
588
|
+
owners: owners.map { |owner| format_owner(owner) }
|
|
589
|
+
}
|
|
590
|
+
rescue => e
|
|
591
|
+
{ error: e.message, registry_code: registry_code }
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def analyze_multiple_companies(registry_codes)
|
|
596
|
+
registry_codes.map { |code| analyze_ownership(code) }
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
private
|
|
600
|
+
|
|
601
|
+
def format_owner(owner)
|
|
602
|
+
{
|
|
603
|
+
name: "#{owner[:eesnimi]} #{owner[:nimi]}",
|
|
604
|
+
country: owner[:valis_kood_riik_tekstina] || owner[:aadress_riik_tekstina],
|
|
605
|
+
control_method: owner[:kontrolli_teostamise_viis_tekstina],
|
|
606
|
+
active: !owner[:lopp_kpv],
|
|
607
|
+
period: format_period(owner[:algus_kpv], owner[:lopp_kpv])
|
|
608
|
+
}
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def format_period(start_date, end_date)
|
|
612
|
+
start_str = start_date ? start_date.strftime('%Y-%m-%d') : 'Unknown'
|
|
613
|
+
end_str = end_date ? end_date.strftime('%Y-%m-%d') : 'Present'
|
|
614
|
+
"#{start_str} - #{end_str}"
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# Usage examples
|
|
619
|
+
analyzer = OwnershipAnalyzer.new
|
|
620
|
+
|
|
621
|
+
# Analyze ownership of Swedbank
|
|
622
|
+
swedbank_analysis = analyzer.analyze_ownership('10060701')
|
|
623
|
+
puts "Company: #{swedbank_analysis[:company][:name]}"
|
|
624
|
+
puts "Total owners: #{swedbank_analysis[:ownership][:total_owners]}"
|
|
625
|
+
puts "Foreign owners: #{swedbank_analysis[:ownership][:foreign_owners]}"
|
|
626
|
+
|
|
627
|
+
swedbank_analysis[:owners].each do |owner|
|
|
628
|
+
puts " #{owner[:name]} (#{owner[:country]}) - #{owner[:control_method]}"
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
# Analyze multiple companies
|
|
632
|
+
companies = ['10060701', '16863232', '11051370'] # Swedbank, Sorbeet, Tallinn Airport
|
|
633
|
+
results = analyzer.analyze_multiple_companies(companies)
|
|
634
|
+
|
|
635
|
+
results.each do |result|
|
|
636
|
+
if result[:error]
|
|
637
|
+
puts "Error for #{result[:registry_code]}: #{result[:error]}"
|
|
638
|
+
else
|
|
639
|
+
company = result[:company]
|
|
640
|
+
ownership = result[:ownership]
|
|
641
|
+
puts "#{company[:name]}: #{ownership[:total_owners]} owners, #{ownership[:foreign_owners]} foreign"
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## π§ Configuration
|
|
649
|
+
|
|
650
|
+
### Environment Variables
|
|
651
|
+
|
|
652
|
+
Set up your credentials using environment variables:
|
|
653
|
+
|
|
654
|
+
```bash
|
|
655
|
+
export EE_API_USERNAME="your_username"
|
|
656
|
+
export EE_API_PASSWORD="your_password"
|
|
657
|
+
export EE_API_LANGUAGE="eng" # Optional: 'eng' or 'est'
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Rails Configuration
|
|
661
|
+
|
|
662
|
+
For Rails applications, add to `config/initializers/ee_business_register.rb`:
|
|
663
|
+
|
|
664
|
+
```ruby
|
|
665
|
+
EeEBusinessRegister.configure do |config|
|
|
666
|
+
config.username = Rails.application.credentials.dig(:estonian_api, :username)
|
|
667
|
+
config.password = Rails.application.credentials.dig(:estonian_api, :password)
|
|
668
|
+
config.language = 'eng'
|
|
669
|
+
config.timeout = Rails.env.production? ? 30 : 10
|
|
670
|
+
config.logger = Rails.logger
|
|
671
|
+
end
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### Test Mode
|
|
675
|
+
|
|
676
|
+
For development and testing, enable test mode to use Estonian test endpoints:
|
|
677
|
+
|
|
678
|
+
```ruby
|
|
679
|
+
EeEBusinessRegister.configure do |config|
|
|
680
|
+
config.username = 'test_username'
|
|
681
|
+
config.password = 'test_password'
|
|
682
|
+
config.test_mode! # Switches to test endpoints
|
|
683
|
+
end
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### All Configuration Options
|
|
687
|
+
|
|
688
|
+
```ruby
|
|
689
|
+
EeEBusinessRegister.configure do |config|
|
|
690
|
+
# Required
|
|
691
|
+
config.username = 'your_username' # API username
|
|
692
|
+
config.password = 'your_password' # API password
|
|
693
|
+
|
|
694
|
+
# Optional
|
|
695
|
+
config.language = 'eng' # Response language: 'eng' or 'est'
|
|
696
|
+
config.timeout = 30 # Request timeout in seconds
|
|
697
|
+
config.test_mode = false # Use test endpoints
|
|
698
|
+
config.logger = Logger.new(STDOUT) # Custom logger
|
|
699
|
+
|
|
700
|
+
# WSDL and endpoint URLs (auto-set based on test_mode)
|
|
701
|
+
config.wsdl_url = 'https://ariregxml.rik.ee/schemas/xtee6/arireg.wsdl'
|
|
702
|
+
config.endpoint_url = 'https://ariregxml.rik.ee/cgi-bin/ariregxml'
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
# Check current configuration
|
|
706
|
+
config = EeEBusinessRegister.get_configuration
|
|
707
|
+
puts "Using language: #{config.language}"
|
|
708
|
+
puts "Test mode: #{config.test_mode}"
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
## π Common Use Cases & Walkthroughs
|
|
714
|
+
|
|
715
|
+
### Walkthrough 1: Basic Company Lookup
|
|
716
|
+
|
|
717
|
+
The most common use case - looking up a company by registry code:
|
|
718
|
+
|
|
719
|
+
```ruby
|
|
720
|
+
# Step 1: Configure (usually done once in your app initialization)
|
|
721
|
+
EeEBusinessRegister.configure do |config|
|
|
722
|
+
config.username = ENV['EE_API_USERNAME']
|
|
723
|
+
config.password = ENV['EE_API_PASSWORD']
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
# Step 2: Look up company
|
|
727
|
+
registry_code = '16863232' # Sorbeet Payments OΓ
|
|
728
|
+
|
|
729
|
+
# First validate the format (optional but recommended)
|
|
730
|
+
unless EeEBusinessRegister.valid_registry_code?(registry_code)
|
|
731
|
+
puts "Invalid registry code format"
|
|
732
|
+
exit
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
# Step 3: Fetch company data
|
|
736
|
+
begin
|
|
737
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
738
|
+
|
|
739
|
+
if company
|
|
740
|
+
puts "β
Found: #{company.name}"
|
|
741
|
+
puts "π Address: #{company.address.full_address}"
|
|
742
|
+
puts "π§ Email: #{company.email || 'Not provided'}"
|
|
743
|
+
puts "π° Capital: #{company.capital} #{company.capital_currency}"
|
|
744
|
+
puts "π
Registered: #{company.registration_date}"
|
|
745
|
+
puts "βοΈ Legal form: #{company.legal_form_text}"
|
|
746
|
+
puts "π Status: #{company.active? ? 'Active' : 'Inactive'}"
|
|
747
|
+
else
|
|
748
|
+
puts "β Company not found"
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
rescue EeEBusinessRegister::Error => e
|
|
752
|
+
puts "Error: #{e.message}"
|
|
753
|
+
end
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
### Walkthrough 2: Analyzing Beneficial Ownership
|
|
757
|
+
|
|
758
|
+
Building an ownership analysis tool with detailed reporting:
|
|
759
|
+
|
|
760
|
+
```ruby
|
|
761
|
+
class OwnershipInvestigator
|
|
762
|
+
def self.investigate(registry_code, options = {})
|
|
763
|
+
# Configure if not already done
|
|
764
|
+
ensure_configured!
|
|
765
|
+
|
|
766
|
+
puts "π Investigating ownership of #{registry_code}..."
|
|
767
|
+
|
|
768
|
+
# Get company and ownership data
|
|
769
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
770
|
+
owners = EeEBusinessRegister.get_beneficial_owners(registry_code)
|
|
771
|
+
puts "π Found #{owners.size} beneficial owners"
|
|
772
|
+
|
|
773
|
+
# Analyze ownership patterns
|
|
774
|
+
active_owners = owners.reject { |o| o[:lopp_kpv] }
|
|
775
|
+
foreign_owners = owners.select { |o| o[:valis_kood_riik] != 'EST' }
|
|
776
|
+
control_methods = owners.map { |o| o[:kontrolli_teostamise_viis_tekstina] }.uniq
|
|
777
|
+
|
|
778
|
+
puts "π Active owners: #{active_owners.size}"
|
|
779
|
+
puts "π Foreign owners: #{foreign_owners.size}"
|
|
780
|
+
puts "βοΈ Control methods: #{control_methods.size} different types"
|
|
781
|
+
|
|
782
|
+
# Apply filters if requested
|
|
783
|
+
filtered_owners = owners
|
|
784
|
+
if options[:active_only]
|
|
785
|
+
filtered_owners = filtered_owners.reject { |o| o[:lopp_kpv] }
|
|
786
|
+
puts "π Filtered to active owners: #{filtered_owners.size}"
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
if options[:foreign_only]
|
|
790
|
+
filtered_owners = filtered_owners.select { |o| o[:valis_kood_riik] != 'EST' }
|
|
791
|
+
puts "π Filtered to foreign owners: #{filtered_owners.size}"
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
display_investigation_results(company, filtered_owners, options)
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
def self.display_investigation_results(company, owners, options = {})
|
|
798
|
+
puts "\n" + "=" * 80
|
|
799
|
+
puts "π’ COMPANY: #{company.name}"
|
|
800
|
+
puts "π Registry Code: #{company.registry_code}"
|
|
801
|
+
puts "π Status: #{company.active? ? 'π’ Active' : 'π΄ Inactive'}"
|
|
802
|
+
puts "=" * 80
|
|
803
|
+
|
|
804
|
+
if owners.empty?
|
|
805
|
+
puts "β No beneficial owners found (or none matching filters)"
|
|
806
|
+
return
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
puts "\nπ₯ BENEFICIAL OWNERS (#{owners.size}):"
|
|
810
|
+
owners.each_with_index do |owner, index|
|
|
811
|
+
puts "\n#{index + 1}. #{owner[:eesnimi]} #{owner[:nimi]}"
|
|
812
|
+
puts " π Country: #{owner[:valis_kood_riik_tekstina] || owner[:aadress_riik_tekstina]}"
|
|
813
|
+
puts " βοΈ Control: #{owner[:kontrolli_teostamise_viis_tekstina]}"
|
|
814
|
+
|
|
815
|
+
period_start = owner[:algus_kpv] ? owner[:algus_kpv].strftime('%Y-%m-%d') : 'Unknown'
|
|
816
|
+
period_end = owner[:lopp_kpv] ? owner[:lopp_kpv].strftime('%Y-%m-%d') : 'Present'
|
|
817
|
+
puts " π
Period: #{period_start} - #{period_end}"
|
|
818
|
+
|
|
819
|
+
puts " π Status: #{owner[:lopp_kpv] ? 'π΄ Historical' : 'π’ Active'}"
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
private
|
|
824
|
+
|
|
825
|
+
def self.ensure_configured!
|
|
826
|
+
config = EeEBusinessRegister.get_configuration
|
|
827
|
+
return if config.username && config.password
|
|
828
|
+
|
|
829
|
+
puts "β οΈ Please configure API credentials first:"
|
|
830
|
+
puts "EeEBusinessRegister.configure do |config|"
|
|
831
|
+
puts " config.username = 'your_username'"
|
|
832
|
+
puts " config.password = 'your_password'"
|
|
833
|
+
puts "end"
|
|
834
|
+
exit
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Usage examples
|
|
839
|
+
|
|
840
|
+
# Investigate Swedbank ownership
|
|
841
|
+
OwnershipInvestigator.investigate('10060701')
|
|
842
|
+
|
|
843
|
+
# Investigate with filters (active foreign owners only)
|
|
844
|
+
OwnershipInvestigator.investigate('10060701', {
|
|
845
|
+
active_only: true,
|
|
846
|
+
foreign_only: true
|
|
847
|
+
})
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Walkthrough 3: Annual Reports Analysis
|
|
851
|
+
|
|
852
|
+
Analyzing a company's financial reports:
|
|
853
|
+
|
|
854
|
+
```ruby
|
|
855
|
+
class FinancialAnalyzer
|
|
856
|
+
def self.analyze_company_finances(registry_code)
|
|
857
|
+
ensure_api_health!
|
|
858
|
+
|
|
859
|
+
puts "π Analyzing finances for #{registry_code}..."
|
|
860
|
+
|
|
861
|
+
# Get basic company info first
|
|
862
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
863
|
+
unless company
|
|
864
|
+
puts "β Company not found"
|
|
865
|
+
return
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
puts "π’ Company: #{company.name}"
|
|
869
|
+
puts "π° Share capital: #{format_currency(company.capital, company.capital_currency)}"
|
|
870
|
+
|
|
871
|
+
# Get list of annual reports
|
|
872
|
+
reports = EeEBusinessRegister.get_annual_reports(registry_code)
|
|
873
|
+
|
|
874
|
+
if reports.empty?
|
|
875
|
+
puts "π No annual reports found"
|
|
876
|
+
return
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
puts "π Found #{reports.size} annual reports"
|
|
880
|
+
|
|
881
|
+
# Analyze recent years
|
|
882
|
+
recent_reports = reports.sort_by { |r| -r[:year].to_i }.first(3)
|
|
883
|
+
|
|
884
|
+
puts "\nπ RECENT FINANCIAL HISTORY:"
|
|
885
|
+
puts "=" * 50
|
|
886
|
+
|
|
887
|
+
recent_reports.each do |report_info|
|
|
888
|
+
year = report_info[:year]
|
|
889
|
+
puts "\nπ
Year #{year}:"
|
|
890
|
+
|
|
891
|
+
begin
|
|
892
|
+
# Try to get detailed report data
|
|
893
|
+
balance_sheet = EeEBusinessRegister.get_annual_report(
|
|
894
|
+
registry_code, year, type: 'balance_sheet'
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
income_statement = EeEBusinessRegister.get_annual_report(
|
|
898
|
+
registry_code, year, type: 'income_statement'
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
puts " πΌ Balance Sheet: Available" if balance_sheet
|
|
902
|
+
puts " π Income Statement: Available" if income_statement
|
|
903
|
+
|
|
904
|
+
# Display whatever data is available
|
|
905
|
+
if balance_sheet && balance_sheet.is_a?(Hash)
|
|
906
|
+
puts " π Report status: #{report_info[:status] || 'Filed'}"
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
rescue EeEBusinessRegister::APIError => e
|
|
910
|
+
puts " β οΈ Could not fetch detailed data: #{e.message}"
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
# Check reporting compliance
|
|
915
|
+
current_year = Date.current.year
|
|
916
|
+
last_year = current_year - 1
|
|
917
|
+
|
|
918
|
+
has_recent = EeEBusinessRegister.has_reports_for_year?(registry_code, last_year)
|
|
919
|
+
latest_year = EeEBusinessRegister.latest_report_year(registry_code)
|
|
920
|
+
|
|
921
|
+
puts "\nπ COMPLIANCE CHECK:"
|
|
922
|
+
puts " Latest report year: #{latest_year || 'None'}"
|
|
923
|
+
puts " Has #{last_year} report: #{has_recent ? 'β
Yes' : 'β No'}"
|
|
924
|
+
|
|
925
|
+
if latest_year && latest_year < last_year
|
|
926
|
+
puts " β οΈ Reporting may be behind schedule"
|
|
927
|
+
elsif has_recent
|
|
928
|
+
puts " β
Reporting appears up to date"
|
|
929
|
+
end
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
private
|
|
933
|
+
|
|
934
|
+
def self.ensure_api_health!
|
|
935
|
+
unless EeEBusinessRegister.check_health
|
|
936
|
+
puts "β Estonian API is not responding. Please try again later."
|
|
937
|
+
exit
|
|
938
|
+
end
|
|
939
|
+
puts "β
Estonian API is healthy"
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
def self.format_currency(amount, currency)
|
|
943
|
+
return 'Not specified' unless amount && currency
|
|
944
|
+
"#{currency} #{amount.round(2)}"
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
# Usage
|
|
949
|
+
FinancialAnalyzer.analyze_company_finances('16863232')
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
### Walkthrough 4: Monitoring Company Changes
|
|
953
|
+
|
|
954
|
+
Setting up monitoring for personnel and other changes:
|
|
955
|
+
|
|
956
|
+
```ruby
|
|
957
|
+
class CompanyMonitor
|
|
958
|
+
def initialize(companies_to_monitor)
|
|
959
|
+
@companies = companies_to_monitor
|
|
960
|
+
ensure_configured!
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
def check_recent_changes(days_back: 7)
|
|
964
|
+
puts "π Checking for changes in last #{days_back} days..."
|
|
965
|
+
|
|
966
|
+
from_date = (Date.current - days_back).strftime('%Y-%m-%d')
|
|
967
|
+
to_date = Date.current.strftime('%Y-%m-%d')
|
|
968
|
+
|
|
969
|
+
@companies.each do |registry_code|
|
|
970
|
+
puts "\n" + "="*60
|
|
971
|
+
puts "π’ Checking #{registry_code}..."
|
|
972
|
+
|
|
973
|
+
begin
|
|
974
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
975
|
+
unless company
|
|
976
|
+
puts "β Company not found"
|
|
977
|
+
next
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
puts "Company: #{company.name}"
|
|
981
|
+
puts "Status: #{company.active? ? 'π’ Active' : 'π΄ Inactive'}"
|
|
982
|
+
|
|
983
|
+
# Check for personnel changes
|
|
984
|
+
changes = EeEBusinessRegister.get_person_changes(
|
|
985
|
+
registry_code,
|
|
986
|
+
from: from_date,
|
|
987
|
+
to: to_date
|
|
988
|
+
)
|
|
989
|
+
|
|
990
|
+
if changes.empty?
|
|
991
|
+
puts "β
No personnel changes detected"
|
|
992
|
+
else
|
|
993
|
+
puts "β οΈ Personnel changes found: #{changes.size}"
|
|
994
|
+
changes.each do |change|
|
|
995
|
+
puts " π #{change[:type] || 'Change'}: #{change[:description] || 'Personnel update'}"
|
|
996
|
+
puts " Date: #{change[:date] || 'Recent'}"
|
|
997
|
+
end
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
# Check representatives
|
|
1001
|
+
reps = EeEBusinessRegister.get_representatives(registry_code)
|
|
1002
|
+
puts "π₯ Current representatives: #{reps.size}"
|
|
1003
|
+
reps.first(3).each do |rep|
|
|
1004
|
+
puts " β’ #{rep[:name]} (#{rep[:role]})"
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
# Check recent reports
|
|
1008
|
+
latest_year = EeEBusinessRegister.latest_report_year(registry_code)
|
|
1009
|
+
if latest_year
|
|
1010
|
+
puts "π Latest report: #{latest_year}"
|
|
1011
|
+
else
|
|
1012
|
+
puts "π No reports found"
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
rescue EeEBusinessRegister::Error => e
|
|
1016
|
+
puts "β Error checking #{registry_code}: #{e.message}"
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
sleep(1) # Be nice to the API
|
|
1020
|
+
end
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
def generate_summary
|
|
1024
|
+
puts "\n" + "="*60
|
|
1025
|
+
puts "π MONITORING SUMMARY"
|
|
1026
|
+
puts "="*60
|
|
1027
|
+
|
|
1028
|
+
active_count = 0
|
|
1029
|
+
total_reps = 0
|
|
1030
|
+
companies_with_changes = 0
|
|
1031
|
+
|
|
1032
|
+
@companies.each do |registry_code|
|
|
1033
|
+
begin
|
|
1034
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
1035
|
+
next unless company
|
|
1036
|
+
|
|
1037
|
+
active_count += 1 if company.active?
|
|
1038
|
+
|
|
1039
|
+
reps = EeEBusinessRegister.get_representatives(registry_code)
|
|
1040
|
+
total_reps += reps.size
|
|
1041
|
+
|
|
1042
|
+
# Check for any recent changes (simplified)
|
|
1043
|
+
changes = EeEBusinessRegister.get_person_changes(registry_code)
|
|
1044
|
+
companies_with_changes += 1 unless changes.empty?
|
|
1045
|
+
|
|
1046
|
+
rescue EeEBusinessRegister::Error
|
|
1047
|
+
# Skip companies with errors for summary
|
|
1048
|
+
end
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
puts "Companies monitored: #{@companies.size}"
|
|
1052
|
+
puts "Active companies: #{active_count}"
|
|
1053
|
+
puts "Total representatives: #{total_reps}"
|
|
1054
|
+
puts "Companies with recent changes: #{companies_with_changes}"
|
|
1055
|
+
puts "Average representatives per company: #{(total_reps.to_f / @companies.size).round(1)}"
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
private
|
|
1059
|
+
|
|
1060
|
+
def ensure_configured!
|
|
1061
|
+
config = EeEBusinessRegister.get_configuration
|
|
1062
|
+
unless config.username && config.password
|
|
1063
|
+
raise "API credentials not configured"
|
|
1064
|
+
end
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
# Usage
|
|
1069
|
+
companies_to_watch = ['16863232', '10000001', '12345678']
|
|
1070
|
+
monitor = CompanyMonitor.new(companies_to_watch)
|
|
1071
|
+
|
|
1072
|
+
monitor.check_recent_changes(days_back: 30)
|
|
1073
|
+
monitor.generate_summary
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
---
|
|
1077
|
+
|
|
1078
|
+
## π Error Handling
|
|
1079
|
+
|
|
1080
|
+
### Error Types & Solutions
|
|
1081
|
+
|
|
1082
|
+
#### `ValidationError` - Invalid Input
|
|
1083
|
+
```ruby
|
|
1084
|
+
begin
|
|
1085
|
+
company = EeEBusinessRegister.find_company('invalid-code')
|
|
1086
|
+
rescue EeEBusinessRegister::ValidationError => e
|
|
1087
|
+
puts "Input error: #{e.message}"
|
|
1088
|
+
# Solution: Check input format, use validate_code() first
|
|
1089
|
+
end
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
#### `AuthenticationError` - Credentials Issue
|
|
1093
|
+
```ruby
|
|
1094
|
+
begin
|
|
1095
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
1096
|
+
rescue EeEBusinessRegister::AuthenticationError => e
|
|
1097
|
+
puts "Auth failed: #{e.message}"
|
|
1098
|
+
# Solution: Verify username/password, check API account status
|
|
1099
|
+
end
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
#### `APIError` - Service Issues
|
|
1103
|
+
```ruby
|
|
1104
|
+
begin
|
|
1105
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
1106
|
+
rescue EeEBusinessRegister::APIError => e
|
|
1107
|
+
puts "API error: #{e.message}"
|
|
1108
|
+
|
|
1109
|
+
if e.message.include?("timeout")
|
|
1110
|
+
# Solution: Retry with longer timeout
|
|
1111
|
+
puts "Service is slow, retrying..."
|
|
1112
|
+
elsif e.message.include?("not found")
|
|
1113
|
+
# Solution: Company doesn't exist
|
|
1114
|
+
puts "Company not in register"
|
|
1115
|
+
elsif e.message.include?("rate limit")
|
|
1116
|
+
# Solution: Wait before next request
|
|
1117
|
+
puts "Too many requests, please wait"
|
|
1118
|
+
end
|
|
1119
|
+
end
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
### Robust Error Handling Pattern
|
|
1123
|
+
|
|
1124
|
+
```ruby
|
|
1125
|
+
class RobustEeClient
|
|
1126
|
+
MAX_RETRIES = 3
|
|
1127
|
+
|
|
1128
|
+
def self.find_company_safely(registry_code)
|
|
1129
|
+
retries = 0
|
|
1130
|
+
|
|
1131
|
+
begin
|
|
1132
|
+
# Validate input first
|
|
1133
|
+
unless EeEBusinessRegister.valid_registry_code?(registry_code)
|
|
1134
|
+
return { error: "Invalid registry code format", code: nil }
|
|
1135
|
+
end
|
|
1136
|
+
|
|
1137
|
+
# Make API call
|
|
1138
|
+
company = EeEBusinessRegister.find_company(registry_code)
|
|
1139
|
+
|
|
1140
|
+
if company
|
|
1141
|
+
{ success: true, company: company }
|
|
1142
|
+
else
|
|
1143
|
+
{ error: "Company not found", code: registry_code }
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
rescue EeEBusinessRegister::AuthenticationError => e
|
|
1147
|
+
{ error: "Authentication failed: #{e.message}", code: registry_code }
|
|
1148
|
+
|
|
1149
|
+
rescue EeEBusinessRegister::APIError => e
|
|
1150
|
+
if e.message.include?("timeout") && retries < MAX_RETRIES
|
|
1151
|
+
retries += 1
|
|
1152
|
+
puts "Timeout, retrying (#{retries}/#{MAX_RETRIES})..."
|
|
1153
|
+
sleep(2 ** retries) # Exponential backoff
|
|
1154
|
+
retry
|
|
1155
|
+
else
|
|
1156
|
+
{ error: "API error: #{e.message}", code: registry_code }
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
rescue => e
|
|
1160
|
+
{ error: "Unexpected error: #{e.message}", code: registry_code }
|
|
1161
|
+
end
|
|
1162
|
+
end
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
# Usage
|
|
1166
|
+
result = RobustEeClient.find_company_safely('16863232')
|
|
1167
|
+
|
|
1168
|
+
if result[:success]
|
|
1169
|
+
puts "Found: #{result[:company].name}"
|
|
1170
|
+
else
|
|
1171
|
+
puts "Error: #{result[:error]}"
|
|
1172
|
+
end
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## π§ͺ Testing
|
|
1178
|
+
|
|
1179
|
+
### Basic Test Setup
|
|
1180
|
+
|
|
1181
|
+
```ruby
|
|
1182
|
+
# test_helper.rb
|
|
1183
|
+
require 'minitest/autorun'
|
|
1184
|
+
require 'ee_e_business_register'
|
|
1185
|
+
|
|
1186
|
+
class TestEeBusinessRegister < Minitest::Test
|
|
1187
|
+
def setup
|
|
1188
|
+
EeEBusinessRegister.configure do |config|
|
|
1189
|
+
config.username = 'test_user'
|
|
1190
|
+
config.password = 'test_pass'
|
|
1191
|
+
config.test_mode! # Use test endpoints
|
|
1192
|
+
config.timeout = 5
|
|
1193
|
+
end
|
|
1194
|
+
end
|
|
1195
|
+
end
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
### Example Test Cases
|
|
1199
|
+
|
|
1200
|
+
```ruby
|
|
1201
|
+
class CompanyLookupTest < TestEeBusinessRegister
|
|
1202
|
+
def test_find_existing_company
|
|
1203
|
+
# Skip if no test credentials
|
|
1204
|
+
skip unless test_credentials_available?
|
|
1205
|
+
|
|
1206
|
+
company = EeEBusinessRegister.find_company('16863232')
|
|
1207
|
+
|
|
1208
|
+
assert_not_nil company
|
|
1209
|
+
assert_equal 'Sorbeet Payments OΓ', company.name
|
|
1210
|
+
assert company.active?
|
|
1211
|
+
assert_match /\d{4}-\d{2}-\d{2}/, company.registration_date
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
def test_validate_registry_code
|
|
1215
|
+
assert EeEBusinessRegister.valid_registry_code?('16863232')
|
|
1216
|
+
refute EeEBusinessRegister.valid_registry_code?('123')
|
|
1217
|
+
refute EeEBusinessRegister.valid_registry_code?('invalid')
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1220
|
+
def test_beneficial_owners
|
|
1221
|
+
skip unless test_credentials_available?
|
|
1222
|
+
|
|
1223
|
+
owners = EeEBusinessRegister.get_beneficial_owners('10060701') # Swedbank
|
|
1224
|
+
|
|
1225
|
+
assert_kind_of Array, owners
|
|
1226
|
+
assert owners.size >= 0
|
|
1227
|
+
assert owners.all? { |o| o.is_a?(Hash) && o.key?(:eesnimi) && o.key?(:nimi) }
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
def test_error_handling
|
|
1231
|
+
assert_raises(EeEBusinessRegister::ValidationError) do
|
|
1232
|
+
EeEBusinessRegister.find_company('invalid')
|
|
1233
|
+
end
|
|
1234
|
+
end
|
|
1235
|
+
|
|
1236
|
+
private
|
|
1237
|
+
|
|
1238
|
+
def test_credentials_available?
|
|
1239
|
+
config = EeEBusinessRegister.get_configuration
|
|
1240
|
+
config.username && config.password
|
|
1241
|
+
end
|
|
1242
|
+
end
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
---
|
|
1246
|
+
|
|
1247
|
+
## π License
|
|
1248
|
+
|
|
1249
|
+
This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
1250
|
+
|
|
1251
|
+
---
|
|
1252
|
+
|
|
1253
|
+
## π€ Contributing
|
|
1254
|
+
|
|
1255
|
+
We welcome contributions! Please feel free to submit a Pull Request.
|
|
1256
|
+
|
|
1257
|
+
### Development Setup
|
|
1258
|
+
|
|
1259
|
+
```bash
|
|
1260
|
+
git clone https://github.com/angeloskapis/ee_e_business_register.git
|
|
1261
|
+
cd ee_e_business_register
|
|
1262
|
+
bundle install
|
|
1263
|
+
rake test
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
### Code Standards
|
|
1267
|
+
- Follow Ruby style guide
|
|
1268
|
+
- Add tests for new features
|
|
1269
|
+
- Update documentation
|
|
1270
|
+
- Keep it simple (KISS principle)
|
|
1271
|
+
|
|
1272
|
+
---
|
|
1273
|
+
|
|
1274
|
+
## π Support
|
|
1275
|
+
|
|
1276
|
+
- **GitHub Issues**: [Report bugs or request features](https://github.com/angeloskapis/ee_e_business_register/issues)
|
|
1277
|
+
- **Email**: angelos@sorbet.ee
|
|
1278
|
+
- **Documentation**: [API Reference](https://rubydoc.info/gems/ee_e_business_register)
|
|
1279
|
+
|
|
1280
|
+
---
|
|
1281
|
+
|
|
1282
|
+
<div align="center">
|
|
1283
|
+
|
|
1284
|
+
### π Ready to get started?
|
|
1285
|
+
|
|
1286
|
+
```bash
|
|
1287
|
+
gem install ee_e_business_register
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
**[β Star us on GitHub](https://github.com/angeloskapis/ee_e_business_register)** if this gem helps your project!
|
|
1291
|
+
|
|
1292
|
+
Made with β€οΈ in Estonia πͺπͺ by [Sorbeet Payments OΓ](https://sorbet.ee)
|
|
1293
|
+
|
|
1294
|
+
</div>
|