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.
data/README.md ADDED
@@ -0,0 +1,1294 @@
1
+ # πŸ›οΈ Estonian e-Business Register
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ee_e_business_register.svg)](https://badge.fury.io/rb/ee_e_business_register)
4
+ [![Ruby](https://img.shields.io/badge/Ruby-3.1+-red.svg)](https://www.ruby-lang.org)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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>