brot 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf6f5e4155bf59cb2cc724e9ea723bb066d5c95e745e687b66e36ad4d058d81f
4
- data.tar.gz: 931cfa4d40375498939bdec52b97b9f63987e16e9efa825aec351987f7b97d6e
3
+ metadata.gz: 554c5401cab39dd06ddee8d095edb4121621c9abc88cc57aacc2e53ef945a720
4
+ data.tar.gz: e3f4dcc0317331c41f7f6f4228b4c26dc77f89b91826c80704e45fa56dfdc39a
5
5
  SHA512:
6
- metadata.gz: 46155574730e67a42bba7c1a815f1be3dcec94c2fda775a66b646b6237b6f51ee83f1d15907a4d4ed503ef9d0195316a42891d64acaa7326b83b1cca542651e5
7
- data.tar.gz: 9e5c22fdb7710f461e28639032e8534c8aafed98c77943e8ba4c27460cda071db18a0bdb10b0f1f11036cd44e117d1acea521762fac85e3c7971a06fd78beaf7
6
+ metadata.gz: c37b1944939b3808a7ff3fac28827b22f1228dbd5d3c10f3ce1728718a30d44374f6595cfe9900cda74f98f577db06871d30b988f27ffd4cb3d1a22e583fdff9
7
+ data.tar.gz: 9ef486499ef15d58947fba7250905af25ebde97f45363d40d55b663d89beeaaa9898f5ae5bece7bc6d6bfb4db205c4722eb20820958d353caaa4c9b2fa83c263
data/.rubocop.yml CHANGED
@@ -7,3 +7,8 @@ AllCops:
7
7
  SuggestExtensions: false
8
8
  TargetRubyVersion: 4.0
9
9
 
10
+ Metrics/ClassLength:
11
+ Max: 120
12
+
13
+ Metrics/ModuleLength:
14
+ Max: 150
data/README.md CHANGED
@@ -3,71 +3,43 @@
3
3
  `brot` builds ISO 20022 SEPA credit transfer XML for DATEV-style uploads with
4
4
  `Nokogiri::XML::Builder`.
5
5
 
6
- The gem emits `pain.001.001.12` and is designed to validate directly against
7
- the provided `pain.001.001.12.xsd`.
6
+ The gem supports these bundled schema targets:
8
7
 
9
- ## Installation
10
-
11
- ```ruby
12
- gem 'brot'
13
- ```
14
-
15
- ## What The Gem Does
8
+ - `pain.001.003.03`
9
+ - `pain.001.001.12`
16
10
 
17
- `brot` is intentionally focused on one narrow job:
11
+ Use `Brot::PainVersion::PAIN_001_003_03` or
12
+ `Brot::PainVersion::PAIN_001_001_12` instead of raw symbols when selecting the
13
+ output format.
18
14
 
19
- - Generate `pain.001.001.12` customer credit transfer files.
20
- - Build XML with `Nokogiri::XML::Builder`.
21
- - Validate generated XML against the bundled `pain.001.001.12.xsd`.
22
- - Stay close to the DATEV upload use case instead of exposing the full ISO
23
- 20022 surface area.
24
-
25
- Current assumptions baked into the serializer:
15
+ The public API is intentionally narrow and DATEV-oriented. It models one
16
+ debtor, one payment information block, one or more transfers, and unstructured
17
+ remittance information.
26
18
 
27
- - Service level is always `SEPA`.
28
- - Payment method is always `TRF`.
29
- - Charge bearer is always `SLEV`.
30
- - Instructed amount is always emitted as `EUR`.
31
- - A debtor BIC or creditor BIC is optional. If absent, the XML uses the
32
- fallback financial institution identifier `NOTPROVIDED`.
19
+ ## Official References
33
20
 
34
- ## Scope And Gaps
21
+ For authoritative field semantics and message context, use these primary
22
+ sources:
35
23
 
36
- `brot` is intentionally a DATEV-oriented subset of `pain.001.001.12`, not a
37
- full abstraction over every branch allowed by the ISO 20022 schema.
24
+ - ISO 20022 message catalogue for current `pain.001` definitions, including
25
+ `pain.001.001.12`:
26
+ https://www.iso20022.org/iso-20022-message-definitions
27
+ - ISO 20022 message archive for older definitions, including
28
+ `pain.001.001.03`:
29
+ https://www.iso20022.org/catalogue-messages/iso-20022-messages-archive?search=pain
30
+ - European Payments Council SCT rulebook and implementation-guideline entry
31
+ point for SEPA-specific usage constraints and terminology:
32
+ https://www.europeanpaymentscouncil.eu/what-we-do/epc-payment-schemes/sepa-credit-transfer/sepa-credit-transfer-rulebook-and
38
33
 
39
- What is currently supported:
34
+ These external documents describe the full ISO 20022 or EPC meaning of many
35
+ fields. `brot` intentionally implements only the narrower subset documented in
36
+ this README.
40
37
 
41
- - SEPA credit transfers
42
- - One debtor per document
43
- - One payment information block (`PmtInf`) per document
44
- - One or more credit transfer entries (`CdtTrfTxInf`)
45
- - Debtor and creditor names
46
- - Debtor and creditor IBANs
47
- - Optional debtor and creditor BICs
48
- - End-to-end IDs
49
- - Optional instruction IDs
50
- - Optional purpose codes
51
- - Unstructured remittance information (`RmtInf/Ustrd`)
52
- - Validation against the bundled `pain.001.001.12.xsd`
53
-
54
- What is not modeled yet, even though the schema allows it:
55
-
56
- - Structured remittance information
57
- - Postal addresses and richer party identification data
58
- - Ultimate debtor and ultimate creditor
59
- - Multiple `PmtInf` sections in one document
60
- - Non-SEPA service levels or non-EUR transfer setups
61
- - Local instrument and category purpose variants beyond the current subset
62
- - Intermediary agents and extra account layers
63
- - Regulatory reporting, tax, cheque, and supplementary data sections
64
-
65
- The practical meaning is:
38
+ ## Installation
66
39
 
67
- - The gem aims to produce a clean DATEV-style payment initiation file for the
68
- supported SEPA use case.
69
- - The gem does not yet try to represent the entire `pain.001.001.12` standard
70
- as a generic Ruby object model.
40
+ ```ruby
41
+ gem 'brot'
42
+ ```
71
43
 
72
44
  ## Quick Start
73
45
 
@@ -75,7 +47,7 @@ The practical meaning is:
75
47
  require 'brot'
76
48
  require 'date'
77
49
 
78
- transfer = Brot::Pain00100112::Transfer.new(
50
+ transfer = Brot::Transfer.new(
79
51
  amount: '1250.50',
80
52
  creditor_name: 'Example Supplier GmbH',
81
53
  creditor_iban: 'DE89370400440532013000',
@@ -85,7 +57,7 @@ transfer = Brot::Pain00100112::Transfer.new(
85
57
  instruction_id: 'PAY-0001'
86
58
  )
87
59
 
88
- document = Brot::Pain00100112::Document.new(
60
+ document = Brot::Document.new(
89
61
  message_id: 'MSG-20260313-01',
90
62
  payment_information_id: 'PMT-20260313-01',
91
63
  initiating_party_name: 'Example Debtor GmbH',
@@ -94,7 +66,7 @@ document = Brot::Pain00100112::Document.new(
94
66
  debtor_bic: 'INGDDEFFXXX',
95
67
  requested_execution_date: Date.new(2026, 3, 13),
96
68
  transfers: [transfer],
97
- batch_booking: true
69
+ version: Brot::PainVersion::PAIN_001_003_03
98
70
  )
99
71
 
100
72
  xml = document.to_xml
@@ -103,60 +75,90 @@ result = document.validate
103
75
  raise result.errors.join("\n") unless result.valid?
104
76
  ```
105
77
 
78
+ If you omit `version:`, the document defaults to
79
+ `Brot::PainVersion::PAIN_001_003_03`.
80
+ Call `document.validate!` if you prefer a raised `Brot::ValidationError`.
81
+
82
+ ## Supported Subset
83
+
84
+ Current assumptions baked into the serializer:
85
+
86
+ - Service level is always `SEPA`.
87
+ - Payment method is always `TRF`.
88
+ - Charge bearer is always `SLEV`.
89
+ - Instructed amount is always emitted as `EUR`.
90
+ - One debtor and one `PmtInf` block are emitted per document.
91
+ - Remittance information is emitted as unstructured text only.
92
+ - If a debtor BIC or creditor BIC is absent, the XML falls back to
93
+ `NOTPROVIDED`.
94
+
95
+ Supported input data:
96
+
97
+ - Debtor and creditor names
98
+ - Debtor and creditor IBANs
99
+ - Optional debtor and creditor BICs
100
+ - End-to-end IDs
101
+ - Optional instruction IDs
102
+ - Optional purpose codes
103
+ - Unstructured remittance information
104
+ - Validation against bundled XSDs for both supported versions
105
+
106
+ Not modeled:
107
+
108
+ - Structured remittance information
109
+ - Postal addresses and richer party identification
110
+ - Ultimate debtor and ultimate creditor
111
+ - Multiple `PmtInf` sections in one document
112
+ - Non-SEPA service levels or non-EUR transfer setups
113
+ - Broader ISO 20022 branches outside the current DATEV-oriented subset
114
+
106
115
  ## Public API
107
116
 
108
117
  The main public classes are:
109
118
 
110
- - `Brot::Pain00100112::Account`
111
- - `Brot::Pain00100112::Transfer`
112
- - `Brot::Pain00100112::Document`
113
- - `Brot::Pain00100112::Schema`
114
- - `Brot::Pain00100112::Schema::Result`
119
+ - `Brot::Account`
120
+ - `Brot::Transfer`
121
+ - `Brot::Document`
122
+ - `Brot::Schema`
123
+ - `Brot::PainVersion`
124
+ - `Brot::Schema::Result`
115
125
 
116
- ### `Brot::Pain00100112::Account`
126
+ ### `Brot::Account`
117
127
 
118
- Use `Account` when you want a standalone object representing debtor or creditor
119
- banking details. You do not need to instantiate it manually for normal usage,
120
- because `Document` and `Transfer` build accounts from `*_iban` and `*_bic`
121
- attributes internally.
128
+ Use `Brot::Account` for standalone debtor or creditor banking details when you
129
+ need it directly. Most callers do not instantiate it manually because
130
+ `Brot::Document` and `Brot::Transfer` build accounts from `*_iban` and `*_bic`
131
+ arguments.
122
132
 
123
133
  Example:
124
134
 
125
135
  ```ruby
126
- account = Brot::Pain00100112::Account.new(
136
+ account = Brot::Account.new(
127
137
  iban: 'DE89370400440532013000',
128
138
  bic: 'COBADEFFXXX'
129
139
  )
130
-
131
- account.iban
132
- # => "DE89370400440532013000"
133
-
134
- account.bic
135
- # => "COBADEFFXXX"
136
140
  ```
137
141
 
138
142
  Attributes:
139
143
 
140
144
  - `iban`
141
- Example: `DE89370400440532013000`
142
- Must be a syntactically valid IBAN. Spaces are removed automatically.
145
+ Validated IBAN with spaces removed.
143
146
  - `bic`
144
- Example: `COBADEFFXXX`
145
- Optional. If omitted, the serializer falls back to `NOTPROVIDED` in the XML.
147
+ Optional validated BIC.
146
148
 
147
- Useful methods:
149
+ Method:
148
150
 
149
151
  - `bic_or_placeholder`
150
- Returns the actual BIC if present, otherwise `NOTPROVIDED`.
152
+ Returns the BIC if present, otherwise `NOTPROVIDED`.
151
153
 
152
- ### `Brot::Pain00100112::Transfer`
154
+ ### `Brot::Transfer`
153
155
 
154
- Use `Transfer` for each outgoing credit transfer inside the payment batch.
156
+ Use `Brot::Transfer` for each outgoing credit transfer inside the batch.
155
157
 
156
158
  Example:
157
159
 
158
160
  ```ruby
159
- transfer = Brot::Pain00100112::Transfer.new(
161
+ transfer = Brot::Transfer.new(
160
162
  amount: '1250.50',
161
163
  creditor_name: 'Example Supplier GmbH',
162
164
  creditor_iban: 'DE89370400440532013000',
@@ -171,254 +173,118 @@ transfer = Brot::Pain00100112::Transfer.new(
171
173
  Initialization attributes:
172
174
 
173
175
  - `amount`
174
- Example: `'1250.50'`
175
- Required. Must be positive and have at most two decimal places.
176
+ Required. Positive, maximum two decimal places.
176
177
  - `creditor_name`
177
- Example: `'Example Supplier GmbH'`
178
- Required. Maximum length is 70 characters in this implementation.
178
+ Required. Maximum 70 characters.
179
179
  - `creditor_iban`
180
- Example: `'DE89370400440532013000'`
181
180
  Required. Validated as an IBAN.
182
181
  - `creditor_bic`
183
- Example: `'COBADEFFXXX'`
184
182
  Optional. Validated as a BIC if supplied.
185
183
  - `end_to_end_id`
186
- Example: `'INV-2026-0001'`
187
- Required. Maximum length is 35 characters.
188
- - `remittance_information`
189
- Example: `'Invoice 2026-0001'`
190
- Required. Maximum length is 140 characters.
191
- - `instruction_id`
192
- Example: `'PAY-0001'`
193
- Optional. Maximum length is 35 characters.
194
- - `purpose_code`
195
- Example: `'SUPP'`
196
- Optional. Maximum length is 4 characters.
197
-
198
- Readable attributes after initialization:
199
-
200
- - `amount`
201
- - `creditor_name`
202
- - `creditor_account`
203
- - `end_to_end_id`
184
+ Required. Maximum 35 characters.
204
185
  - `remittance_information`
186
+ Required. Maximum 140 characters.
205
187
  - `instruction_id`
188
+ Optional. Maximum 35 characters.
206
189
  - `purpose_code`
190
+ Optional. Maximum 4 characters.
207
191
 
208
- ### `Brot::Pain00100112::Document`
192
+ ### `Brot::Document`
209
193
 
210
- Use `Document` to represent the full payment file. This is the main entry
211
- point of the gem.
194
+ Use `Brot::Document` for the full payment file.
212
195
 
213
- Example:
196
+ Example using `.12` output:
214
197
 
215
198
  ```ruby
216
- document = Brot::Pain00100112::Document.new(
199
+ document = Brot::Document.new(
217
200
  message_id: 'MSG-20260313-01',
218
201
  payment_information_id: 'PMT-20260313-01',
219
202
  initiating_party_name: 'Example Debtor GmbH',
220
203
  debtor_name: 'Example Debtor GmbH',
221
204
  debtor_iban: 'DE12500105170648489890',
222
- debtor_bic: 'INGDDEFFXXX',
223
205
  requested_execution_date: Date.new(2026, 3, 13),
224
206
  transfers: [transfer],
225
- batch_booking: true,
226
- created_at: Time.utc(2026, 3, 13, 12, 0, 0)
207
+ version: Brot::PainVersion::PAIN_001_001_12
227
208
  )
228
209
  ```
229
210
 
230
211
  Initialization attributes:
231
212
 
232
213
  - `message_id`
233
- Example: `'MSG-20260313-01'`
234
- Required. Group header message identifier. Maximum length is 35 characters.
214
+ Required. Maximum 35 characters.
235
215
  - `payment_information_id`
236
- Example: `'PMT-20260313-01'`
237
- Required. Payment information block identifier. Maximum length is 35
238
- characters.
216
+ Required. Maximum 35 characters.
239
217
  - `initiating_party_name`
240
- Example: `'Example Debtor GmbH'`
241
- Required. Maximum length is 70 characters.
218
+ Required. Maximum 70 characters.
242
219
  - `debtor_name`
243
- Example: `'Example Debtor GmbH'`
244
- Required. Maximum length is 70 characters.
220
+ Required. Maximum 70 characters.
245
221
  - `debtor_iban`
246
- Example: `'DE12500105170648489890'`
247
222
  Required. Validated as an IBAN.
248
223
  - `debtor_bic`
249
- Example: `'INGDDEFFXXX'`
250
224
  Optional. Validated as a BIC if supplied.
251
225
  - `requested_execution_date`
252
- Example: `Date.new(2026, 3, 13)` or `'2026-03-13'`
253
- Required. Date only.
226
+ Required. `Date` or ISO 8601 date string.
254
227
  - `transfers`
255
- Example: `[transfer_1, transfer_2]`
256
- Required. Must contain at least one `Brot::Pain00100112::Transfer`.
228
+ Required. Must contain at least one `Brot::Transfer`.
257
229
  - `batch_booking`
258
- Example: `true`
259
230
  Optional. Defaults to `true`.
260
231
  - `created_at`
261
- Example: `Time.utc(2026, 3, 13, 12, 0, 0)`
262
- Optional. Defaults to the current UTC time.
263
-
264
- Readable attributes after initialization:
265
-
266
- - `message_id`
267
- - `payment_information_id`
268
- - `initiating_party_name`
269
- - `debtor_name`
270
- - `debtor_account`
271
- - `requested_execution_date`
272
- - `transfers`
273
- - `batch_booking`
274
- - `created_at`
232
+ Optional. Defaults to current UTC time.
233
+ - `version`
234
+ Optional. Defaults to `Brot::PainVersion::PAIN_001_003_03`.
235
+ Supported values are `Brot::PainVersion::PAIN_001_003_03` and
236
+ `Brot::PainVersion::PAIN_001_001_12`.
275
237
 
276
238
  Useful methods:
277
239
 
278
- - `to_xml(pretty: true)`
279
- Serializes the document to `pain.001.001.12` XML.
280
- - `validate(xsd_path: Brot::Pain00100112::Schema.bundled_xsd_path)`
281
- Validates the generated XML and returns a `Schema::Result`.
282
240
  - `control_sum`
283
- Returns the sum of all transfer amounts as a `BigDecimal`.
284
241
  - `number_of_transactions`
285
- Returns the number of transfers.
286
-
287
- ### `Brot::Pain00100112::Schema`
288
-
289
- Use `Schema` when you want to validate XML directly without first building a
290
- `Document` object.
291
-
292
- Example:
293
-
294
- ```ruby
295
- xml = document.to_xml(pretty: false)
296
- result = Brot::Pain00100112::Schema.validate(xml)
297
-
298
- raise result.errors.join("\n") unless result.valid?
299
- ```
242
+ - `to_xml(pretty: true)`
243
+ - `validate(xsd_path: nil)`
244
+ - `validate!(xsd_path: nil)`
300
245
 
301
- Available methods:
246
+ Version-specific output differences:
302
247
 
303
- - `bundled_xsd_path`
304
- Returns the absolute path of the XSD bundled inside the gem.
305
- - `validate(xml, xsd_path: bundled_xsd_path)`
306
- Validates an XML string against the bundled schema by default.
248
+ - `pain.001.003.03` uses namespace
249
+ `urn:iso:std:iso:20022:tech:xsd:pain.001.003.03`
250
+ - `pain.001.001.12` uses namespace
251
+ `urn:iso:std:iso:20022:tech:xsd:pain.001.001.12`
252
+ - `.03` writes `BIC` and plain `ReqdExctnDt`
253
+ - `.12` writes `BICFI` and `ReqdExctnDt/Dt`
307
254
 
308
- ### `Brot::Pain00100112::Schema::Result`
255
+ ### `Brot::Schema`
309
256
 
310
- `Schema.validate` and `Document#validate` both return a
311
- `Brot::Pain00100112::Schema::Result`.
257
+ Use `Brot::Schema` when you want to validate XML directly.
312
258
 
313
- Example:
259
+ Examples:
314
260
 
315
261
  ```ruby
316
- result = document.validate
317
-
262
+ result = Brot::Schema.validate(xml)
318
263
  result.valid?
319
- # => true
320
-
321
- result.errors
322
- # => []
323
264
  ```
324
265
 
325
- Attributes and methods:
326
-
327
- - `errors`
328
- An array of schema validation error messages.
329
- - `valid?`
330
- Returns `true` when `errors` is empty.
331
-
332
- ## End-To-End Example
333
-
334
- This example shows the normal workflow from transfers to XML generation and
335
- validation:
336
-
337
266
  ```ruby
338
- require 'brot'
339
- require 'date'
340
-
341
- transfers = [
342
- Brot::Pain00100112::Transfer.new(
343
- amount: '1250.50',
344
- creditor_name: 'Example Supplier GmbH',
345
- creditor_iban: 'DE89370400440532013000',
346
- creditor_bic: 'COBADEFFXXX',
347
- end_to_end_id: 'INV-2026-0001',
348
- remittance_information: 'Invoice 2026-0001'
349
- ),
350
- Brot::Pain00100112::Transfer.new(
351
- amount: '349.99',
352
- creditor_name: 'Another Supplier GmbH',
353
- creditor_iban: 'DE44500105175407324931',
354
- end_to_end_id: 'INV-2026-0002',
355
- remittance_information: 'Invoice 2026-0002'
356
- )
357
- ]
358
-
359
- document = Brot::Pain00100112::Document.new(
360
- message_id: 'MSG-20260313-01',
361
- payment_information_id: 'PMT-20260313-01',
362
- initiating_party_name: 'Example Debtor GmbH',
363
- debtor_name: 'Example Debtor GmbH',
364
- debtor_iban: 'DE12500105170648489890',
365
- debtor_bic: 'INGDDEFFXXX',
366
- requested_execution_date: Date.new(2026, 3, 13),
367
- transfers: transfers
368
- )
369
-
370
- xml = document.to_xml
371
- result = document.validate
372
-
373
- abort(result.errors.join("\n")) unless result.valid?
374
-
375
- puts xml
267
+ path = Brot::Schema.bundled_xsd_path(Brot::PainVersion::PAIN_001_001_12)
268
+ result = Brot::Schema.validate(xml, xsd_path: path)
376
269
  ```
377
270
 
378
- ## Validation
379
-
380
- The gem ships with the `pain.001.001.12.xsd` file, so validation works without
381
- an external path:
382
-
383
271
  ```ruby
384
- result = document.validate
385
-
386
- raise result.errors.join("\n") unless result.valid?
272
+ Brot::Schema.validate!(xml)
387
273
  ```
388
274
 
389
- If you want to override the schema file, pass `xsd_path:` explicitly.
275
+ Methods:
390
276
 
391
- ## Error Handling
277
+ - `bundled_xsd_path(version)`
278
+ - `validate(xml, version: nil, xsd_path: nil)`
279
+ - `validate!(xml, version: nil, xsd_path: nil)`
392
280
 
393
- Invalid input values raise `Brot::Pain00100112::ValidationError`.
281
+ If you omit both `version:` and `xsd_path:`, the gem infers the bundled schema
282
+ from the XML document namespace.
394
283
 
395
- Typical examples:
284
+ ### `Brot::PainVersion`
396
285
 
397
- - Invalid IBAN
398
- - Invalid BIC
399
- - Empty required text fields
400
- - Amounts that are zero, negative, or have too many decimal places
401
- - Missing transfers
286
+ `Brot::PainVersion` is the public value object for supported output formats.
287
+ Prefer these constants when constructing documents or looking up bundled XSDs:
402
288
 
403
- Example:
404
-
405
- ```ruby
406
- begin
407
- Brot::Pain00100112::Transfer.new(
408
- amount: '0',
409
- creditor_name: 'Broken Example',
410
- creditor_iban: 'INVALID',
411
- end_to_end_id: 'X',
412
- remittance_information: 'Broken'
413
- )
414
- rescue Brot::Pain00100112::ValidationError => error
415
- warn error.message
416
- end
417
- ```
418
-
419
- ## Development
420
-
421
- ```sh
422
- bundle install
423
- bundle exec rake
424
- ```
289
+ - `Brot::PainVersion::PAIN_001_003_03`
290
+ - `Brot::PainVersion::PAIN_001_001_12`
data/brot.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = 'Build ISO 20022 pain.001 payment initiation XML for DATEV uploads.'
12
12
  spec.description = <<~TEXT
13
13
  brot builds SEPA credit transfer XML with Nokogiri::XML::Builder.
14
- The output targets pain.001.001.12 for DATEV-oriented uploads.
14
+ The output targets pain.001.003.03 or pain.001.001.12 for DATEV-oriented uploads.
15
15
  TEXT
16
16
  spec.homepage = 'https://github.com/garriguv/brot'
17
17
  spec.license = 'MIT'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Brot
4
+ # Bank account details used in debtor and creditor sections.
5
+ class Account
6
+ attr_reader :bic, :iban
7
+
8
+ # @param iban [String]
9
+ # @param bic [String, nil]
10
+ def initialize(iban:, bic: nil)
11
+ @iban = Utils.normalize_iban!(iban)
12
+ @bic = bic.nil? ? nil : Utils.normalize_bic!(bic)
13
+ end
14
+
15
+ # Returns the BIC if present, otherwise `NOTPROVIDED`.
16
+ #
17
+ # @return [String]
18
+ def bic_or_placeholder
19
+ bic || Utils.default_financial_institution_id
20
+ end
21
+ end
22
+ end