diff --git a/README.md b/README.md index f2e8878..6315ed8 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,30 @@ gem 'cfonb' `OperationDetails` are lines starting with `05`. They aim at providing additional information about the operation. Below is the list of additional details available for each operation. -These details can be accessed through `operation.details`, which will provide all the attributes. To fetch a specific attribute, you can use `operation.details.attribute`. For example, `operation.details.unstructured_label`. Ultimately, you can also access the 70 characters of the detail by using its code like `operation.details.mmo` +These details can be accessed through `operation.details`, which will provide all the attributes. To fetch a specific attribute, you can use `operation.details.attribute`. For example, `operation.details.unstructured_label`. Ultimately, you can also access the 70 characters of the detail by using its code like `operation.details.mmo`. -If you encounter new ones, please open an issue or a pull request with the appropriate implementation. +All unmapped details can be accessed via `details.unknown` which are stored in a hash with the format `'detail_code' => 'line_detail'`, so +to get the data for the unknown detail_code `AAA` the call would be `details.unknown['AAA']`. + +Object example: + +``` +#"UNKNOWN DETAIL INFO"} +> +``` + +If you encounter new and relevant ones, please open an issue or a pull request with the appropriate implementation. We aimed at making it as easy as possible to add new details. You just need to do the following on initialization: ```ruby @@ -58,14 +79,6 @@ CFONB::OperationDetails.register('FEE', self) | RCN | `client_reference`, `purpose` | Client reference and Payment nature/purpose | | REF | `operation_reference` | Bank operation reference | -TODO: -| Detail Code | Attributes | Description | -| --- | --- | --- | -| RET | `unifi_code`, `sit_code`, `payback_label` | Payback informations | -| CBE | `creditor_account` | Account of the creditor or beneficiary | -| BDB | `creditor_bank` | Bank of the creditor or beneficiary | -| LEM | `issuer_label` | Issuer Label | - ## Usage ```ruby diff --git a/cfonb.gemspec b/cfonb.gemspec index b375876..6750406 100644 --- a/cfonb.gemspec +++ b/cfonb.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'cfonb' - s.version = '1.0.1' + s.version = '1.1.0' s.required_ruby_version = '>= 3.2' s.summary = 'CFONB parser' s.description = 'An easy to use CFONB format parser' diff --git a/lib/cfonb.rb b/lib/cfonb.rb index 24c7d6d..e7be641 100644 --- a/lib/cfonb.rb +++ b/lib/cfonb.rb @@ -32,6 +32,7 @@ require_relative 'cfonb/operation_details/ibe' require_relative 'cfonb/operation_details/npo' require_relative 'cfonb/operation_details/nbu' +require_relative 'cfonb/operation_details/unknown' module CFONB def self.parse(input, optimistic: false) diff --git a/lib/cfonb/operation_details.rb b/lib/cfonb/operation_details.rb index f9bde87..8e06f2b 100644 --- a/lib/cfonb/operation_details.rb +++ b/lib/cfonb/operation_details.rb @@ -15,7 +15,9 @@ def self.register(code, klass) end def self.for(line) - @details[line.detail_code] + return unless line.respond_to?(:detail_code) + + @details[line.detail_code] || Unknown end end end diff --git a/lib/cfonb/operation_details/base.rb b/lib/cfonb/operation_details/base.rb index 5c80a98..83c66ea 100644 --- a/lib/cfonb/operation_details/base.rb +++ b/lib/cfonb/operation_details/base.rb @@ -7,10 +7,23 @@ def self.inherited(base) base.singleton_class.prepend( Module.new do def apply(details, line) - details.instance_variable_set(:"@#{line.detail_code}", line.detail) + code = :"@#{line.detail_code}" + details.instance_variable_set(code, instance_value(details, line, code)) super end + + private + + def append_detail?(details, line, code) + details.instance_variable_defined?(code) && line.detail.is_a?(String) + end + + def instance_value(details, line, code) + return line.detail unless append_detail?(details, line, code) + + details.instance_variable_get(code) + "\n#{line.detail}" + end end, ) end diff --git a/lib/cfonb/operation_details/unknown.rb b/lib/cfonb/operation_details/unknown.rb new file mode 100644 index 0000000..46b3624 --- /dev/null +++ b/lib/cfonb/operation_details/unknown.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module CFONB + module OperationDetails + class Unknown < Base + ATTRIBUTES = %i[unknown].freeze + + def self.apply(details, line) + details.unknown ||= {} + code = line.detail_code + + details.unknown[code] = + if details.unknown[code] && line.detail.is_a?(String) + details.unknown[code] + "\n#{line.detail}" + else + line.detail + end + end + + CFONB::OperationDetails.register('Unknown', self) + end + end +end diff --git a/spec/cfonb/operation_spec.rb b/spec/cfonb/operation_spec.rb index d31a56f..2aff10a 100644 --- a/spec/cfonb/operation_spec.rb +++ b/spec/cfonb/operation_spec.rb @@ -272,6 +272,29 @@ expect(operation.details.ultimate_creditor).to eq('Patrick') end end + + context 'with an unknown detail' do + let(:detail) do + OpenStruct.new( + body: '0530004411001871EUR2 0001016255614090823 AAAEUR200000000000740', + detail_code: 'AAA', + detail: 'EUR200000000000740', + ) + end + + it 'adds the detail to the unknown details hash' do + operation.merge_detail(detail) + + expect(operation.details.unknown).to eq({ 'AAA' => 'EUR200000000000740' }) + end + + it 'updates the current details in case of duplicated codes' do + operation.merge_detail(detail) + operation.merge_detail(detail) + + expect(operation.details.unknown).to eq({ 'AAA' => "EUR200000000000740\nEUR200000000000740" }) + end + end end describe '#type_code' do diff --git a/spec/cfonb/parser_spec.rb b/spec/cfonb/parser_spec.rb index a6fb0eb..2e95437 100644 --- a/spec/cfonb/parser_spec.rb +++ b/spec/cfonb/parser_spec.rb @@ -43,12 +43,17 @@ reference: '', ) expect(statements[0].operations[0].details).to have_attributes( - free_label: 'MENSUEAUHTR13133', + free_label: "MENSUEAUHTR13133\nMENSUEAUHTR13DUP", original_currency: nil, original_amount: nil, exchange_rate: nil, purpose: 'PURPOSE', debtor: 'INTERNET SFR', + unknown: { + 'AAA' => "INTERNETA AAA\nINTERNETA ABB", + 'BBB' => 'INTERNETE BBB', + 'CCC' => 'INTERNETI CCC', + }, ) expect(statements[0].operations[1]).to have_attributes( @@ -272,7 +277,7 @@ expect(statements[0].operations[0].details).to have_attributes( operation_reference: 'REFERENCE', - free_label: 'MENSUEAUHTR13133', + free_label: "MENSUEAUHTR13133\nMENSUEAUHTR13DUP", debtor: 'INTERNET SFR', client_reference: 'OTHER REFERENCE', original_currency: nil, @@ -705,7 +710,7 @@ expect(operation.details).to have_attributes( operation_reference: 'REFERENCE', client_reference: 'OTHER REFERENCE', - free_label: "MENSUEAUHTR13133\nP051928612 22793301700040", + free_label: "MENSUEAUHTR13133\nMENSUEAUHTR13DUP\nP051928612 22793301700040", debtor: 'ELEC ERDF', original_currency: nil, original_amount: nil, diff --git a/spec/files/example.txt b/spec/files/example.txt index 5bb62e0..c8d728e 100644 --- a/spec/files/example.txt +++ b/spec/files/example.txt @@ -2,9 +2,14 @@ 0415589916200000EUR2 98765432100B1160519 160519PRLV SEPA TEST CABINET 0000000000000000000322J 0515589916200000EUR2 98765432100B1160519 LIBMENSUEAUHTR13133 +0515589916200000EUR2 98765432100B1160519 LIBMENSUEAUHTR13DUP 0515589916200000EUR2 98765432100B1160519 REFREFERENCE 0515589916200000EUR2 98765432100B1160519 RCNOTHER REFERENCE PURPOSE 0515589916200000EUR2 98765432100B1160519 NPYINTERNET SFR +0515589916200000EUR2 98765432100B1160519 AAAINTERNETA AAA +0515589916200000EUR2 98765432100B1160519 AAAINTERNETA ABB +0515589916200000EUR2 98765432100B1160519 BBBINTERNETE BBB +0515589916200000EUR2 98765432100B1160519 CCCINTERNETI CCC 0415589916200000EUR2 98765432100B1160519 160519VIR SEPA DEMONSTRATION 0000000000000000000107}REFERENCE 0515589916200000EUR2 98765432100B1160519 NPYELEC ERDF