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);