From 101a5d60634fd9473dc8a4956ab8fb554c5a77b3 Mon Sep 17 00:00:00 2001 From: Andreas Finke Date: Wed, 13 Apr 2022 22:30:38 +0100 Subject: [PATCH 1/2] #228 Fixes bug that eref was not matched against statement.information. Adds test cases. Removes exclude statement (credit_received and debit_received are not the correct values in any case) --- box/business_processes/import_statements.rb | 2 +- .../import_statement_spec.rb | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/box/business_processes/import_statements.rb b/box/business_processes/import_statements.rb index a6c9e175..78d9114f 100644 --- a/box/business_processes/import_statements.rb +++ b/box/business_processes/import_statements.rb @@ -51,7 +51,7 @@ def self.link_statement_to_transaction(account, statement) # find transactions via EREF transaction = account.transactions_dataset.where(eref: statement.eref).first # fallback to finding via statement information - transaction ||= account.transactions_dataset.exclude(currency: 'EUR', status: %w[credit_received debit_received]).where { created_at > 14.days.ago }.detect { |t| statement.information =~ /#{t.eref}/i } + transaction ||= account.transactions_dataset.where { created_at > 14.days.ago }.detect { |t| statement.information =~ /#{t.eref}/i } return unless transaction diff --git a/spec/business_processes/import_statement_spec.rb b/spec/business_processes/import_statement_spec.rb index f38dedea..6fd35208 100644 --- a/spec/business_processes/import_statement_spec.rb +++ b/spec/business_processes/import_statement_spec.rb @@ -205,6 +205,45 @@ def exec_link_action end end + describe '.link_statement_to_transaction fallback' do + let(:statement) { Statement.create(information: 'fallback-eref', account_id: account.id) } + + def exec_link_action + described_class.link_statement_to_transaction(account, statement) + end + + context 'no transaction could be found' do + it 'does not trigger a webhook' do + expect(Event).to_not receive(:statement_created) + exec_link_action + end + end + + context 'transaction exists' do + let!(:transaction) { Transaction.create(account_id: account.id, eref: 'fallback-eref', created_at: Date.today) } + let(:event) { object_double(Event).as_stubbed_const } + + context 'statement is a credit' do + before { statement.update(debit: false) } + + it 'sets correct transaction state' do + expect_any_instance_of(Transaction).to receive(:update_status).with('credit_received') + exec_link_action + end + end + + context 'statement is a debit' do + before { statement.update(debit: true) } + + it 'sets correct transaction state' do + expect_any_instance_of(Transaction).to receive(:update_status).with('debit_received') + exec_link_action + end + end + end + end + + describe 'duplicates' do let(:mt940) { File.read('spec/fixtures/similar_but_not_dup_transactions.mt940') } let(:mt940b) { File.read('spec/fixtures/dup_whitespace_transaction.mt940') } From 46bf02bee439c2b5af47863a8cba52b887ed4614 Mon Sep 17 00:00:00 2001 From: Andreas Finke Date: Thu, 14 Apr 2022 14:19:56 +0100 Subject: [PATCH 2/2] #228 Stores SEPA msg_id in transaction. Extracts SEPA msg_id from bank_statement and stores it in statement. --- Gemfile | 2 +- Gemfile.lock | 3 +- box/business_processes/direct_debit.rb | 3 +- box/business_processes/import_statements.rb | 8 +- box/jobs/debit.rb | 1 + ...d_msg_id_to_transactions_and_statements.rb | 9 ++ .../import_statement_spec.rb | 33 +++++- spec/fixtures/camt_statement.xml | 108 ++++++++++++++++++ 8 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 migrations/20220413163800_add_msg_id_to_transactions_and_statements.rb diff --git a/Gemfile b/Gemfile index 07eadf3a..7537eb34 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ source 'https://rubygems.org' ruby '2.5.8' gem 'activesupport' -gem 'camt_parser', git: 'https://github.com/railslove/camt_parser.git' +gem 'camt_parser', git: 'https://github.com/railslove/camt_parser.git', ref: '23e99b7' gem 'cmxl' gem 'epics', '~> 1.8.1' gem 'faraday' diff --git a/Gemfile.lock b/Gemfile.lock index 3c61c759..86edbde9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,7 @@ GIT remote: https://github.com/railslove/camt_parser.git - revision: 90780332f60caa9d5d0e92fef7974f1f0b5338cc + revision: 23e99b772253150f2ff4edff0d7123b810af32db + ref: 23e99b7 specs: camt_parser (1.0.2) nokogiri diff --git a/box/business_processes/direct_debit.rb b/box/business_processes/direct_debit.rb index 8c1dfdb0..24e6d05c 100644 --- a/box/business_processes/direct_debit.rb +++ b/box/business_processes/direct_debit.rb @@ -36,7 +36,8 @@ def self.create!(account, params, user) payload: Base64.strict_encode64(sdd.to_xml), amount: params[:amount], eref: params[:eref], - instrument: params[:instrument] + instrument: params[:instrument], + message_identification: sdd.message_identification ) else raise Box::BusinessProcessFailure, sdd.errors diff --git a/box/business_processes/import_statements.rb b/box/business_processes/import_statements.rb index 78d9114f..098e2466 100644 --- a/box/business_processes/import_statements.rb +++ b/box/business_processes/import_statements.rb @@ -49,9 +49,12 @@ def self.create_statement(bank_statement, bank_transaction, upcoming = false) def self.link_statement_to_transaction(account, statement) # find transactions via EREF - transaction = account.transactions_dataset.where(eref: statement.eref).first + transaction = account.transactions_dataset.where(eref: statement.eref).first unless statement.eref.nil? + # find transactions via SEPA msg_id + # ToDo: This fallback works at the moment, because we only add one transaction to each CDD batch. + transaction = account.transactions_dataset.where(msg_id: statement.msg_id).first unless statement.msg_id.nil? # fallback to finding via statement information - transaction ||= account.transactions_dataset.where { created_at > 14.days.ago }.detect { |t| statement.information =~ /#{t.eref}/i } + transaction ||= account.transactions_dataset.where { created_at > 14.days.ago }.detect { |t| statement.information =~ /#{t.eref}/i } unless statement.information.nil? return unless transaction @@ -112,6 +115,7 @@ def self.statement_attributes_from_bank_transaction(transaction, bank_statement) eref: transaction.respond_to?(:eref) ? transaction.eref : transaction.sepa['EREF'], mref: transaction.respond_to?(:mref) ? transaction.mref : transaction.sepa['MREF'], svwz: transaction.respond_to?(:svwz) ? transaction.svwz : transaction.sepa['SVWZ'], + msg_id: transaction.try(:msg_id), tx_id: transaction.try(:transaction_id), creditor_identifier: transaction.respond_to?(:creditor_identifier) ? transaction.creditor_identifier : transaction.sepa['CRED'] } diff --git a/box/jobs/debit.rb b/box/jobs/debit.rb index c5277d24..3b7e6be7 100644 --- a/box/jobs/debit.rb +++ b/box/jobs/debit.rb @@ -27,6 +27,7 @@ def perform(message) trx.account_id = message[:account_id] trx.payload = Base64.strict_decode64(message[:payload]) trx.status = 'created' + trx.msg_id = message[:message_identification] end return false unless transaction.status == 'created' diff --git a/migrations/20220413163800_add_msg_id_to_transactions_and_statements.rb b/migrations/20220413163800_add_msg_id_to_transactions_and_statements.rb new file mode 100644 index 00000000..f836a228 --- /dev/null +++ b/migrations/20220413163800_add_msg_id_to_transactions_and_statements.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +Sequel.migration do + up do + add_column :statements, :msg_id, String + add_column :transactions, :msg_id, String + end + +end diff --git a/spec/business_processes/import_statement_spec.rb b/spec/business_processes/import_statement_spec.rb index 6fd35208..ab57dde2 100644 --- a/spec/business_processes/import_statement_spec.rb +++ b/spec/business_processes/import_statement_spec.rb @@ -145,7 +145,8 @@ def exec_create_action it 'imports camt statements' do parsed_camt = CamtParser::String.parse(camt).statements bank_statement = ImportBankStatement.from_cmxl(parsed_camt.first, camt_account) - expect { described_class.from_bank_statement(bank_statement) }.to change { Statement.count }.by(4) + expect { described_class.from_bank_statement(bank_statement) }.to change { Statement.count }.by(5) + expect( Statement.last.msg_id ).to eq("EBICS-BOX/123") end end @@ -205,7 +206,7 @@ def exec_link_action end end - describe '.link_statement_to_transaction fallback' do + describe '.link_statement_to_transaction fallback eref in information' do let(:statement) { Statement.create(information: 'fallback-eref', account_id: account.id) } def exec_link_action @@ -243,6 +244,34 @@ def exec_link_action end end + describe '.link_statement_to_transaction fallback msg_id for debits' do + let(:statement) { Statement.create(msg_id: 'EBICS-BOX/123', account_id: account.id) } + + def exec_link_action + described_class.link_statement_to_transaction(account, statement) + end + + context 'no transaction could be found' do + it 'does not trigger a webhook' do + expect(Event).to_not receive(:statement_created) + exec_link_action + end + end + + context 'transaction exists for debits' do + let!(:transaction) { Transaction.create(account_id: account.id, msg_id: 'EBICS-BOX/123', created_at: Date.today) } + let(:event) { object_double(Event).as_stubbed_const } + + context 'statement is a debit' do + before { statement.update(debit: true) } + + it 'sets correct transaction state' do + expect_any_instance_of(Transaction).to receive(:update_status).with('debit_received') + exec_link_action + end + end + end + end describe 'duplicates' do let(:mt940) { File.read('spec/fixtures/similar_but_not_dup_transactions.mt940') } diff --git a/spec/fixtures/camt_statement.xml b/spec/fixtures/camt_statement.xml index e42ad44d..1fb61717 100755 --- a/spec/fixtures/camt_statement.xml +++ b/spec/fixtures/camt_statement.xml @@ -307,6 +307,114 @@ + + 8.00 + DBIT + BOOK + +
2013-12-27
+
+ +
2013-12-27
+
+ 2013122711513230000 + + + + EBICS-BOX/123 + STZV-PmInf27122013-11:02-2 + 2 + + + + STZV-Msg27122013-11:02 + STZV-EtE27122013-11:02-1 + + + + 3.50 + + + + + NMSC+201 + ZKA + + + + + Testkonto Nummer 2 + + + + DE58740618130100033626 + + + + keine Information vorhanden + + + Testkonto Nummer 1 + + + + DE14740618130000033626 + + + + Testkonto + + + + Sammelueberwseisung 2. Zahlung TAN:283044 + + + + + STZV-Msg27122013-11:02 + STZV-EtE27122013-11:02-2 + + + + 2.50 + + + + + NMSC+201 + ZKA + + + + + Testkonto Nummer 2 + + + + DE58740618130100033626 + + + + keine Information vorhanden + + + Testkonto Nummer 1 + + + + DE14740618130000033626 + + + + Testkonto + + + + Sammelueberweisung 1. Zahlung TAN:283044 + + + +