1 package SL::Controller::CsvImport::BankTransaction;
6 use SL::Controller::CsvImport::Helper::Consistency;
7 use SL::DB::BankTransaction;
11 use parent qw(SL::Controller::CsvImport::Base);
13 use Rose::Object::MakeMethods::Generic
15 'scalar --get_set_init' => [ qw(bank_accounts_by) ],
18 sub set_profile_defaults {
21 $self->controller->profile->_set_defaults(
22 charset => 'UTF8', # override charset from defaults
23 update_policy => 'skip',
29 $self->class('SL::DB::BankTransaction');
32 sub init_bank_accounts_by {
35 return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_bank_accounts } } ) } qw(id account_number iban) };
41 $self->controller->track_progress(phase => 'building data', progress => 0);
42 my $update_policy = $self->controller->profile->get('update_policy') || 'skip';
45 my $num_data = scalar @{ $self->controller->data };
46 foreach my $entry (@{ $self->controller->data }) {
47 $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
49 $self->check_bank_account($entry);
50 $self->check_currency($entry, take_default => 1);
51 $self->join_purposes($entry);
52 $self->join_remote_names($entry);
53 $self->check_existing($entry) unless @{ $entry->{errors} };
58 $self->add_info_columns({ header => $::locale->text('Bank account'), method => 'local_bank_name' });
59 $self->add_raw_data_columns("currency", "currency_id") if grep { /^currency(?:_id)?$/ } @{ $self->csv->header };
63 my ($self, $entry) = @_;
65 my $object = $entry->{object};
67 # for each imported entry (line) we make a database call to find existing entries
68 # we don't use the init_by hash because we have to check several fields
69 # this means that we can't detect duplicates in the import file
71 if ( $object->amount ) {
75 # * remote_account_number (may be empty for records of our own bank)
77 # * local_bank_account_id (case flatrate bank charges for two accounts in one bank: same purpose, transdate, remote_account_number(empty), amount. Just different local_bank_account_id)
79 if ( $num = SL::DB::Manager::BankTransaction->get_all_count(query =>[ remote_account_number => $object->remote_account_number, transdate => $object->transdate, purpose => $object->purpose, amount => $object->amount, local_bank_account_id => $object->local_bank_account_id] ) ) {
80 push(@{$entry->{errors}}, $::locale->text('Skipping due to existing bank transaction in database'));
83 push(@{$entry->{errors}}, $::locale->text('Skipping because transfer amount is empty.'));
87 sub _displayable_columns {
89 { name => 'local_bank_code', description => $::locale->text('Own bank code') },
90 { name => 'local_account_number', description => $::locale->text('Own bank account number or IBAN') },
91 { name => 'local_bank_account_id', description => $::locale->text('ID of own bank account') },
92 { name => 'remote_bank_code', description => $::locale->text('Bank code of the goal/source') },
93 { name => 'remote_account_number', description => $::locale->text('Account number of the goal/source') },
94 { name => 'transdate', description => $::locale->text('Transdate') },
95 { name => 'valutadate', description => $::locale->text('Valutadate') },
96 { name => 'amount', description => $::locale->text('Amount') },
97 { name => 'currency', description => $::locale->text('Currency') },
98 { name => 'currency_id', description => $::locale->text('Currency (database ID)') },
99 { name => 'remote_name', description => $::locale->text('Name of the goal/source (if field names remote_name and remote_name_1 exist they will be combined into field "remote_name")') },
100 { name => 'remote_name_1', description => $::locale->text('Name of the goal/source (if field names remote_name and remote_name_1 exist they will be combined into field "remote_name")') },
101 { name => 'purpose', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
102 { name => 'purpose1', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
103 { name => 'purpose2', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
104 { name => 'purpose3', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
105 { name => 'purpose4', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
106 { name => 'purpose5', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
107 { name => 'purpose6', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
108 { name => 'purpose7', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
109 { name => 'purpose8', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
110 { name => 'purpose9', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
111 { name => 'purpose10', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
112 { name => 'purpose11', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
113 { name => 'purpose12', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') },
114 { name => 'purpose13', description => $::locale->text('Purpose (if field names purpose, purpose1, purpose2 ... exist they will all combined into the field "purpose")') }
118 sub setup_displayable_columns {
121 $self->SUPER::setup_displayable_columns;
123 $self->add_displayable_columns($self->_displayable_columns);
126 sub check_bank_account {
127 my ($self, $entry) = @_;
129 my $object = $entry->{object};
131 # import via id: check whether or not local_bank_account ID exists and is valid.
132 if ($object->local_bank_account_id && !$self->bank_accounts_by->{id}->{ $object->local_bank_account_id }) {
133 push @{ $entry->{errors} }, $::locale->text('Error: unknown local bank account id');
137 # Check whether or not local_bank_account ID, local_account_number and local_bank_code are consistent.
138 if ($object->local_bank_account_id && $entry->{raw_data}->{local_account_number}) {
139 my $bank_account = $self->bank_accounts_by->{id}->{ $object->local_bank_account_id };
140 if ($bank_account->account_number ne $entry->{raw_data}->{local_account_number}) {
141 push @{ $entry->{errors} }, $::locale->text('Error: local bank account id doesn\'t match local bank account number');
144 if ($entry->{raw_data}->{local_bank_code} && $entry->{raw_data}->{local_bank_code} ne $bank_account->bank_code) {
145 push @{ $entry->{errors} }, $::locale->text('Error: local bank account id doesn\'t match local bank code');
151 # Map account information to ID via local_account_number if no local_bank_account_id was given
152 # local_account_number checks for match of account number or IBAN
153 if (!$object->local_bank_account_id && $entry->{raw_data}->{local_account_number}) {
154 my $bank_account = $self->bank_accounts_by->{account_number}->{ $entry->{raw_data}->{local_account_number} };
155 if (!$bank_account) {
156 $bank_account = $self->bank_accounts_by->{iban}->{ $entry->{raw_data}->{local_account_number} };
158 if (!$bank_account) {
159 push @{ $entry->{errors} }, $::locale->text('Error: unknown local bank account') . ": " . $entry->{raw_data}->{local_account_number};
162 if ($entry->{raw_data}->{local_bank_code} && $entry->{raw_data}->{local_bank_code} ne $bank_account->bank_code) {
163 push @{ $entry->{errors} }, $::locale->text('Error: Found local bank account number but local bank code doesn\'t match') . ": " . $entry->{raw_data}->{local_bank_code};
167 $object->local_bank_account_id($bank_account->id);
168 $entry->{info_data}->{local_bank_name} = $bank_account->name;
170 return $object->local_bank_account_id ? 1 : 0;
174 my ($self, $entry) = @_;
176 my $object = $entry->{object};
180 grep { ($_ // '') !~ m{^ *$} }
181 map { $entry->{raw_data}->{"purpose$_"} }
184 $object->purpose($purpose);
188 sub join_remote_names {
189 my ($self, $entry) = @_;
191 my $object = $entry->{object};
193 my $remote_name = join(' ', $entry->{raw_data}->{remote_name},
194 $entry->{raw_data}->{remote_name_1} );
195 $object->remote_name($remote_name);
199 $::auth->assert('config') if ! $::auth->assert('bank_transaction',1);