Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 789de0c0

Von Moritz Bunkus vor etwa 4 Jahren hinzugefügt

  • ID 789de0c020f6aa3c289c1c74c3df98f076b3eef6
  • Vorgänger fd5cfc0f
  • Nachfolger e4783220

MT940-Import: Implementation eines eigenen Parsers anstelle von AQBanking

Unterschiede anzeigen:

SL/Controller/BankImport.pm
1 1
package SL::Controller::BankImport;
2

  
2 3
use strict;
3
use Data::Dumper;
4

  
4 5
use parent qw(SL::Controller::Base);
5 6

  
6
use SL::Locale::String qw(t8);
7
use SL::DB::CsvImportProfile;
8
use SL::Helper::MT940;
7
use List::MoreUtils qw(apply);
8
use List::Util qw(max min);
9

  
10
use SL::DB::BankAccount;
11
use SL::DB::BankTransaction;
12
use SL::DB::Default;
13
use SL::Helper::Flash;
14
use SL::MT940;
9 15
use SL::SessionFile::Random;
10 16

  
11 17
use Rose::Object::MakeMethods::Generic
12 18
(
13
 'scalar --get_set_init' => [ qw(profile) ],
19
  scalar                  => [ qw(file_name transactions statistics) ],
20
  'scalar --get_set_init' => [ qw(bank_accounts) ],
14 21
);
15 22

  
16 23
__PACKAGE__->run_before('check_auth');
......
19 26
  my ($self, %params) = @_;
20 27

  
21 28
  $self->setup_upload_mt940_action_bar;
22
  $self->render('bankimport/form', title => $::locale->text('MT940 import'), profile => $self->profile ? 1 : 0);
29
  $self->render('bank_import/upload_mt940', title => $::locale->text('MT940 import'));
23 30
}
24 31

  
25
sub action_import_mt940 {
32
sub action_import_mt940_preview {
26 33
  my ($self, %params) = @_;
27 34

  
28
  die "missing file for action import" unless $::form->{file};
35
  if (!$::form->{file}) {
36
    flash_later('error', $::locale->text('You have to upload an MT940 file to import.'));
37
    return $self->redirect_to(action => 'upload_mt940');
38
  }
29 39

  
30
  my $converted_data = SL::Helper::MT940::convert_mt940_data($::form->{file});
40
  die "missing file for action import_mt940_preview" unless $::form->{file};
31 41

  
32
  # store the converted data in a session file and create a temporary profile with it's name
33 42
  my $file = SL::SessionFile::Random->new(mode => '>');
34
  $file->fh->print($converted_data);
43
  $file->fh->print($::form->{file});
35 44
  $file->fh->close;
36
  $self->profile->set('file_name', $file->file_name);
37
  $self->profile($self->profile->clone_and_reset_deep)->save;
38 45

  
39
  die t8("The MT940 import needs an import profile called MT940") unless $self->profile;
46
  $self->file_name($file->file_name);
47
  $self->parse_and_analyze_transactions;
48

  
49
  $self->setup_upload_mt940_preview_action_bar;
50
  $self->render('bank_import/import_mt940', title => $::locale->text('MT940 import preview'), preview => 1);
51
}
52

  
53
sub action_import_mt940 {
54
  my ($self, %params) = @_;
55

  
56
  die "missing file for action import_mt940" unless $::form->{file_name};
57

  
58
  $self->file_name($::form->{file_name});
59
  $self->parse_and_analyze_transactions;
60
  $self->import_transactions;
61

  
62
  $self->render('bank_import/import_mt940', title => $::locale->text('MT940 import result'));
63
}
64

  
65
sub parse_and_analyze_transactions {
66
  my ($self, %params) = @_;
67

  
68
  my $errors     = 0;
69
  my $duplicates = 0;
70
  my ($min_date, $max_date);
40 71

  
41
  $self->redirect_to(controller => 'controller.pl', action => 'CsvImport/test', 'profile.type' => 'bank_transactions', 'profile.id' => $self->profile->id, force_profile => 1);
72
  my $currency_id = SL::DB::Default->get->currency_id;
73

  
74
  $self->transactions([ sort { $a->{transdate} cmp $b->{transdate} } SL::MT940->parse($self->file_name) ]);
75

  
76
  foreach my $transaction (@{ $self->transactions }) {
77
    $transaction->{bank_account}   = $self->bank_accounts->{ make_bank_account_idx($transaction->{local_bank_code}, $transaction->{local_account_number}) };
78
    $transaction->{bank_account} //= $self->bank_accounts->{ make_bank_account_idx('IBAN',                          $transaction->{local_account_number}) };
79

  
80
    if (!$transaction->{bank_account}) {
81
      $transaction->{error} = $::locale->text('No bank account configured for bank code/BIC #1, account number/IBAN #2.', $transaction->{local_bank_code}, $transaction->{local_account_number});
82
      $errors++;
83
      next;
84
    }
85

  
86
    $transaction->{local_bank_account_id} = $transaction->{bank_account}->id;
87
    $transaction->{currency_id}           = $currency_id;
88

  
89
    $min_date = min($min_date // $transaction->{transdate}, $transaction->{transdate});
90
    $max_date = max($max_date // $transaction->{transdate}, $transaction->{transdate});
91
  }
92

  
93
  my %existing_bank_transactions;
94

  
95
  if ((scalar(@{ $self->transactions }) - $errors) > 0) {
96
    my @entries =
97
      @{ SL::DB::Manager::BankTransaction->get_all(
98
          where => [
99
            transdate => { ge => $min_date },
100
            transdate => { lt => $max_date->clone->add(days => 1) },
101
          ],
102
          inject_results => 1) };
103

  
104
    %existing_bank_transactions = map { (make_transaction_idx($_) => 1) } @entries;
105
  }
106

  
107
  foreach my $transaction (@{ $self->transactions }) {
108
    next if $transaction->{error};
109

  
110
    if ($existing_bank_transactions{make_transaction_idx($transaction)}) {
111
      $transaction->{duplicate} = 1;
112
      $duplicates++;
113
      next;
114
    }
115
  }
116

  
117
  $self->statistics({
118
    total      => scalar(@{ $self->transactions }),
119
    errors     => $errors,
120
    duplicates => $duplicates,
121
    to_import  => scalar(@{ $self->transactions }) - $errors - $duplicates,
122
  });
123
}
124

  
125
sub import_transactions {
126
  my ($self, %params) = @_;
127

  
128
  my $imported = 0;
129

  
130
  SL::DB::client->with_transaction(sub {
131
    # make Emacs happy
132
    1;
133

  
134
    foreach my $transaction (@{ $self->transactions }) {
135
      next if $transaction->{error} || $transaction->{duplicate};
136

  
137
      SL::DB::BankTransaction->new(
138
        map { ($_ => $transaction->{$_}) } qw(amount currency_id local_bank_account_id purpose remote_account_number remote_bank_code remote_name transaction_code transdate valutadate)
139
      )->save;
140

  
141
      $imported++;
142
    }
143

  
144
    1;
145
  });
146

  
147
  $self->statistics->{imported} = $imported;
42 148
}
43 149

  
44 150
sub check_auth {
45 151
  $::auth->assert('bank_transaction');
46 152
}
47 153

  
48
sub init_profile {
49
  my $profile = SL::DB::Manager::CsvImportProfile->find_by(name => 'MT940', login => $::myconfig{login});
50
  if ( ! $profile ) {
51
    $profile = SL::DB::Manager::CsvImportProfile->find_by(name => 'MT940', login => 'default');
154
sub make_bank_account_idx {
155
  return join '/', map { my $q = $_; $q =~ s{ +}{}g; $q } @_;
156
}
157

  
158
sub normalize_text {
159
  my ($text) = @_;
160

  
161
  $text = lc($text // '');
162
  $text =~ s{ }{}g;
163

  
164
  return $text;
165
}
166

  
167
sub make_transaction_idx {
168
  my ($transaction) = @_;
169

  
170
  if (ref($transaction) eq 'SL::DB::BankTransaction') {
171
    $transaction = { map { ($_ => $transaction->$_) } qw(local_bank_account_id transdate valutadate amount purpose) };
52 172
  }
53
  return $profile;
173

  
174
  return normalize_text(join '/',
175
                        map { $_ // '' }
176
                        ($transaction->{local_bank_account_id},
177
                         $transaction->{transdate}->ymd,
178
                         $transaction->{valutadate}->ymd,
179
                         (apply { s{0+$}{} } $transaction->{amount} * 1),
180
                         $transaction->{purpose}));
181
}
182

  
183
sub init_bank_accounts {
184
  my ($self) = @_;
185

  
186
  my %bank_accounts;
187

  
188
  foreach my $bank_account (@{ SL::DB::Manager::BankAccount->get_all }) {
189
    if ($bank_account->bank_code && $bank_account->account_number) {
190
      $bank_accounts{make_bank_account_idx($bank_account->bank_code, $bank_account->account_number)} = $bank_account;
191
    }
192
    if ($bank_account->iban) {
193
      $bank_accounts{make_bank_account_idx('IBAN', $bank_account->iban)} = $bank_account;
194
    }
195
  }
196

  
197
  return \%bank_accounts;
54 198
}
55 199

  
56 200
sub setup_upload_mt940_action_bar {
......
60 204
    $bar->add(
61 205
      action => [
62 206
        $::locale->text('Preview'),
207
        submit    => [ '#form', { action => 'BankImport/import_mt940_preview' } ],
208
        accesskey => 'enter',
209
      ],
210
    );
211
  }
212
}
213

  
214
sub setup_upload_mt940_preview_action_bar {
215
  my ($self) = @_;
216

  
217
  for my $bar ($::request->layout->get('actionbar')) {
218
    $bar->add(
219
      action => [
220
        $::locale->text('Import'),
63 221
        submit    => [ '#form', { action => 'BankImport/import_mt940' } ],
64 222
        accesskey => 'enter',
223
        disabled  => $self->statistics->{to_import} ? undef : $::locale->text('No entries can be imported.'),
65 224
      ],
66 225
    );
67 226
  }
SL/MT940.pm
1
package SL::MT940;
2

  
3
use strict;
4
use warnings;
5

  
6
use Data::Dumper;
7
use DateTime;
8
use Encode;
9
use File::Slurp qw(read_file);
10

  
11
sub _join_entries {
12
  my ($parts, $from, $to, $separator) = @_;
13

  
14
  $separator //= ' ';
15

  
16
  return
17
    join $separator,
18
    grep { $_ }
19
    map  { s{^\s+|\s+$}{}g; $_ }
20
    grep { $_ }
21
    map  { $parts->{$_} }
22
    ($from..$to);
23
}
24

  
25
sub parse {
26
  my ($class, $file_name) = @_;
27

  
28
  my ($local_bank_code, $local_account_number, %transaction, @transactions, @lines);
29
  my $line_number = 0;
30

  
31
  my $store_transaction = sub {
32
    if (%transaction) {
33
      push @transactions, { %transaction };
34
      %transaction = ();
35
    }
36
  };
37

  
38
  foreach my $line (read_file($file_name)) {
39
    chomp $line;
40
    $line = Encode::decode('UTF-8', $line);
41
    $line =~ s{\r+}{};
42
    $line_number++;
43

  
44
    if (@lines && ($line =~ m{^\%})) {
45
      $lines[-1]->[0] .= substr($line, 1);
46

  
47
    } else {
48
      push @lines, [ $line, $line_number ];
49
    }
50
  }
51

  
52
  foreach my $line (@lines) {
53
    if ($line->[0] =~ m{^:25:(\d+)/(\d+)}) {
54
      $local_bank_code      = $1;
55
      $local_account_number = $2;
56

  
57
    } elsif ($line->[0] =~ m{^:61: (\d{2}) (\d{2}) (\d{2}) (\d{2}) (\d{2}) (C|D|RC|RD) (.) (\d+) (?:, (\d*))? N (.{3}) (.*)}x) {
58
      #                       1       2       3       4       5       6                7   8          9         10     11
59
      # :61:2008060806CR952,N051NONREF
60

  
61
      $store_transaction->();
62

  
63
      my $valuta_year      = $1 * 1 + 2000;
64
      my $valuta_month     = $2;
65
      my $valuta_day       = $3;
66
      my $trans_month      = $4;
67
      my $trans_day        = $5;
68
      my $debit_credit     = $6;
69
      my $currency         = $7;
70
      my $amount1          = $8;
71
      my $amount2          = $9 || 0;
72
      my $transaction_code = $10;
73
      my $reference        = $11;
74

  
75
      my $valuta_date      = DateTime->new_local(year => $valuta_year, month => $valuta_month, day => $valuta_day);
76
      my $trans_date       = DateTime->new_local(year => $valuta_year, month => $trans_month,  day => $trans_day);
77
      my $diff             = $valuta_date->subtract_datetime($trans_date);
78
      my $trans_year_diff  = $diff->months < 6           ?  0
79
                           : $valuta_date  > $trans_date ?  1
80
                           :                               -1;
81
      $trans_date          = DateTime->new_local(year => $valuta_year + $trans_year_diff, month => $trans_month,  day => $trans_day);
82
      my $sign             = ($debit_credit eq 'D') || ($debit_credit eq 'RC') ? -1 : 1;
83
      $reference           =~ s{//.*}{};
84
      $reference           = '' if $reference eq 'NONREF';
85

  
86
      %transaction = (
87
        line_number          => $line->[1],
88
        currency             => $currency,
89
        valutadate           => $valuta_date,
90
        transdate            => $trans_date,
91
        amount               => ($amount1 * 1 + ($amount2 / (10 ** length($amount2))))* $sign,
92
        reference            => $reference,
93
        transaction_code     => $transaction_code,
94
        local_bank_code      => $local_bank_code,
95
        local_account_number => $local_account_number,
96
      );
97

  
98
    } elsif (%transaction && ($line->[0] =~ m{^:86:})) {
99
      if ($line->[0] =~ m{^:86:\d+\?(.+)}) {
100
        # structured
101
        my %parts = map { ((substr($_, 0, 2) // '0') * 1 => substr($_, 2)) } split m{\?}, $1;
102

  
103
        $transaction{purpose}               = _join_entries(\%parts, 20, 29);
104
        $transaction{remote_name}           = _join_entries(\%parts, 32, 33, '');
105
        $transaction{remote_bank_code}      = $parts{30};
106
        $transaction{remote_account_number} = $parts{31};
107

  
108
      } else {
109
        # unstructured
110
        $transaction{purpose} = substr($line->[0], 5);
111
      }
112

  
113
      $store_transaction->();
114
    }
115
  }
116

  
117
  $store_transaction->();
118

  
119
  return @transactions;
120
}
121

  
122
1;
locale/de/all
278 278
  'Allow the following users access to my follow-ups:' => 'Erlaube den folgenden Benutzern Zugriff auf meine Wiedervorlagen:',
279 279
  'Allow to delete generated printfiles' => 'Löschen von erzeugten Dokumenten erlaubt',
280 280
  'Already counted'             => 'Bereits erfasst',
281
  'Already imported entries (duplicates)' => 'Bereits importierte Einträge (Duplikate)',
281 282
  'Always edit assembly items (user can change/delete items even if assemblies are already produced)' => 'Erzeugnisbestandteile verändern (Löschen/Umsortieren) auch nachdem dieses Erzeugnis schon produziert wurde.',
282 283
  'Always save orders with a projectnumber (create new projects)' => 'Aufträge immer mit Projektnummer speichern (neue Projekte erstellen)',
283 284
  'Amended Advance Turnover Tax Return' => 'Berichtigte Anmeldung',
......
1134 1135
  'Dunnings'                    => 'Mahnungen',
1135 1136
  'Dunnings (Id -- Dunning Date --Dunning Level -- Dunning Fee)' => 'Mahnungen (Nummer -- Mahndatum -- Mahnstufe -- Mahngebühr/Zinsen)',
1136 1137
  'Dunningstatistic'            => 'Mahnstatistik',
1138
  'Duplicate'                   => 'Duplikat',
1137 1139
  'Duplicate in CSV file'       => 'Duplikat in CSV-Datei',
1138 1140
  'Duplicate in database'       => 'Duplikat in Datenbank',
1139 1141
  'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => 'Beim nächsten Ausführen des Updates wird ein Steuerschlüssel 0 mit einem Steuersatz von 0% automatisch erzeugt.',
......
1275 1277
  'Enter the requested execution date or leave empty for the quickest possible execution:' => 'Geben Sie das jeweils gewünschte Ausführungsdatum an, oder lassen Sie das Feld leer für die schnellstmögliche Ausführung:',
1276 1278
  'Entries for which automatic conversion failed:' => 'Einträge, für die die automatische Umstellung fehlschlug:',
1277 1279
  'Entries for which automatic conversion succeeded:' => 'Einträge, für die die automatische Umstellung erfolgreich war:',
1280
  'Entries ready to import'     => 'Zu importierende Einträge',
1281
  'Entries with errors'         => 'Einträge mit Fehlern',
1278 1282
  'Equity'                      => 'Passiva',
1279 1283
  'Erfolgsrechnung'             => 'Erfolgsrechnung',
1280 1284
  'Error'                       => 'Fehler',
......
1314 1318
  'Error: Invalid language'     => 'Fehler: Sprache ungültig',
1315 1319
  'Error: Invalid part'         => 'Fehler: Artikel ungültig',
1316 1320
  'Error: Invalid part type'    => 'Fehler: ungültiger Artikeltyp',
1317
  'Error: Invalid parts group'  => 'Fehler: Warengruppe ungültig',
1318 1321
  'Error: Invalid parts group id #1' => 'Fehler: Ungültige Warengruppen-ID #1',
1319 1322
  'Error: Invalid parts group name #1' => 'Fehler: Ungültiger Warengruppenname: #1',
1320 1323
  'Error: Invalid payment terms' => 'Fehler: Zahlungsbedingungen ungültig',
......
1668 1671
  'Import result'               => 'Import-Ergebnis',
1669 1672
  'Import scanned documents'    => 'Importiere gescannte Dateien',
1670 1673
  'Importdate'                  => 'Importdatum',
1674
  'Imported'                    => 'Importiert',
1675
  'Imported entries'            => 'Importierte Einträge',
1671 1676
  'In addition to the above date functions, subtract the following amount of days from the calculated date as a buffer.' => 'Der folgende Puffer in Tagen wird von den beiden obigen vorausberechneten Daten abgezogen.',
1672 1677
  'In order to do that hit the button "Delete transaction".' => 'Drücken Sie dafür auf den Button "Buchung löschen".',
1673 1678
  'In order to migrate the old folder structure into the new structure you have to chose which client the old structure will be assigned to.' => 'Um die alte Ordnerstruktur in die neue Struktur zu migrieren, müssen Sie festlegen, welchem Mandanten die bisherige Struktur zugewiesen wird.',
......
1875 1880
  'Loading...'                  => 'Wird geladen...',
1876 1881
  'Local Bank Code'             => 'Lokale Bankleitzahl',
1877 1882
  'Local Tax Office Preferences' => 'Angaben zum Finanzamt',
1883
  'Local account'               => 'Eigenes Konto',
1878 1884
  'Local account number'        => 'Lokale Kontonummer',
1879 1885
  'Local bank account'          => 'Lokales Bankkonto',
1880 1886
  'Local bank code'             => 'Lokale Bankleitzahl',
......
1903 1909
  'MD'                          => 'PT',
1904 1910
  'MIME type'                   => 'MIME-Typ',
1905 1911
  'MT940 import'                => 'MT940 Import',
1912
  'MT940 import preview'        => 'MT940-Import-Vorschau',
1913
  'MT940 import result'         => 'MT940-Import-Ergebnis',
1906 1914
  'Mails'                       => 'E-Mails',
1907 1915
  'Main Contact Person'         => 'Hauptansprechpartner',
1908 1916
  'Main Preferences'            => 'Grundeinstellungen',
......
2044 2052
  'No assembly has been selected yet.' => 'Es wurde noch kein Erzeugnis ausgewahlt.',
2045 2053
  'No background job has been created yet.' => 'Es wurden noch keine Hintergrund-Jobs angelegt.',
2046 2054
  'No bank account chosen!'     => 'Kein Bankkonto ausgewählt!',
2055
  'No bank account configured for bank code/BIC #1, account number/IBAN #2.' => 'Kein Bankkonto für BLZ/BIC #1, Kontonummer/IBAN #2 konfiguriert.',
2047 2056
  'No bank account flagged for ZUGFeRD usage was found.' => 'Es wurde kein Bankkonto gefunden, das für Nutzung mit ZUGFeRD markiert ist.',
2048 2057
  'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => 'Für diesen Kunden wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
2049 2058
  'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => 'Für diesen Lieferanten wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
......
2066 2075
  'No email for user with login #1 defined.' => 'Keine E-Mail-Adresse für den Benutzer mit dem Login #1 definiert.',
2067 2076
  'No email recipient for customer #1 defined.' => 'Keine E-Mail-Adresse (Rechnungs- oder global) für den Kunden #1 definiert.',
2068 2077
  'No end date given, setting to today' => 'Kein Enddatum gegeben, setze Enddatum auf heute',
2078
  'No entries can be imported.' => 'Es können keine Einträge importiert werden.',
2069 2079
  'No entries have been imported yet.' => 'Es wurden noch keine Einträge importiert.',
2070 2080
  'No entries have been selected.' => 'Es wurden keine Einträge ausgewählt.',
2071 2081
  'No errors have occurred.'    => 'Es sind keine Fehler aufgetreten.',
......
2236 2246
  'Orphaned'                    => 'Nie benutzt',
2237 2247
  'Orphaned currencies'         => 'Verwaiste Währungen',
2238 2248
  'Other Matches'               => 'Andere Treffer',
2249
  'Other party'                 => 'Andere Partei',
2239 2250
  'Other recipients'            => 'Weitere EmpfängerInnen',
2240 2251
  'Other users\' follow-ups'    => 'Wiedervorlagen anderer Benutzer',
2241 2252
  'Other values are ignored.'   => 'Andere Eingaben werden ignoriert.',
......
2364 2375
  'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.',
2365 2376
  'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
2366 2377
  'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.',
2367
  'Please create a CSV import profile called "MT940" for the import type bank transactions:' => 'Bitte erstellen Sie ein CSV Import Profil mit dem Namen "MT940" für den Importtyp Bankbewegungen',
2368 2378
  'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:',
2369 2379
  'Please do so in the administration area.' => 'Bitte erledigen Sie dies im Administrationsbereich.',
2370 2380
  'Please enter a profile name.' => 'Bitte geben Sie einen Profilnamen an.',
......
2645 2655
  'Remittance information prefix' => 'Verwendungszweckvorbelegung (Präfix)',
2646 2656
  'Remote Bank Code'            => 'Fremde Bankleitzahl',
2647 2657
  'Remote Name/Customer/Description' => 'Kunden/Lieferantenname und Beschreibung',
2658
  'Remote account'              => 'Gegenkonto',
2648 2659
  'Remote account number'       => 'Fremde Kontonummer',
2649 2660
  'Remote bank code'            => 'Fremde Bankleitzahl',
2650 2661
  'Remote name'                 => 'Fremder Kontoinhaber',
......
3227 3238
  'The IBAN is missing.'        => 'Die IBAN fehlt.',
3228 3239
  'The ID #1 is not a valid database ID.' => 'Die ID #1 ist keine gültige Datenbank-ID.',
3229 3240
  'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => 'Der LDAP-Server "#1:#2" ist nicht erreichbar. Bitte überprüfen Sie die Angaben in config/kivitendo.conf.',
3230
  'The MT940 import needs an import profile called MT940' => 'Der MT940 Import benötigt ein Importprofil mit dem Namen "MT940"',
3231 3241
  'The Mail strings have been saved.' => 'Die vorbelegten E-Mail-Texte wurden gespeichert.',
3232 3242
  'The PDF has been created'    => 'Die PDF-Datei wurde erstellt.',
3233 3243
  'The PDF has been printed'    => 'Das PDF-Dokument wurde gedruckt.',
......
3689 3699
  'To (time)'                   => 'Bis',
3690 3700
  'To Date'                     => 'Bis',
3691 3701
  'To continue please change the taxkey 0 to another value.' => 'Um fortzufahren, ändern Sie bitte den Steuerschlüssel 0 auf einen anderen Wert.',
3702
  'To import'                   => 'Zu importieren',
3692 3703
  'To upload images: Please create shoppart first' => 'Um Bilder hochzuladen bitte Shopartikel zuerst anlegen',
3693 3704
  'To user login'               => 'Zum Benutzerlogin',
3694 3705
  'Toggle marker'               => 'Markierung umschalten',
......
3701 3712
  'Total'                       => 'Summe',
3702 3713
  'Total Fees'                  => 'Kumulierte Gebühren',
3703 3714
  'Total Sales Orders Value'    => 'Auftragseingang',
3715
  'Total number of entries'     => 'Gesamtzahl Einträge',
3704 3716
  'Total stock value'           => 'Gesamter Bestandswert',
3705 3717
  'Total sum'                   => 'Gesamtsumme',
3706 3718
  'Total weight'                => 'Gesamtgewicht',
......
3714 3726
  'Transaction ID missing.'     => 'Die Buchungs-ID fehlt.',
3715 3727
  'Transaction Value'           => 'Umsatz',
3716 3728
  'Transaction Value Currency Code' => 'WKZ Umsatz',
3729
  'Transaction date'            => 'Buchungsdatum',
3717 3730
  'Transaction deleted!'        => 'Buchung gelöscht!',
3718 3731
  'Transaction description'     => 'Vorgangsbezeichnung',
3719 3732
  'Transaction has already been cancelled!' => 'Diese Buchung wurde bereits storniert.',
......
4043 4056
  'You have to grant users access to one or more clients.' => 'Benutzern muss dann Zugriff auf einzelne Mandanten gewährt werden.',
4044 4057
  'You have to specify a department.' => 'Sie müssen eine Abteilung wählen.',
4045 4058
  'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.',
4059
  'You have to upload an MT940 file to import.' => 'Sie müssen die zu importierende MT940-Datei hochladen.',
4046 4060
  'You must chose a user.'      => 'Sie müssen einen Benutzer auswählen.',
4047 4061
  'You must enter a name for your new print templates.' => 'Sie müssen einen Namen für die neuen Druckvorlagen angeben.',
4048 4062
  'You must not change this AP transaction.' => 'Sie dürfen diese Kreditorenbuchung nicht verändern.',
templates/webpages/bank_import/import_mt940.html
1
[%- USE HTML %]
2
[%- USE LxERP %]
3
[%- USE L %]
4
[%- USE T8 %]
5

  
6
<h1>[% FORM.title %]</h1>
7

  
8
[% IF preview %]
9
  <form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
10
    [% L.hidden_tag('file_name', SELF.file_name) %]
11
  </form>
12
[% END %]
13

  
14
<h2>[% LxERP.t8("Overview") %]</h2>
15

  
16
<div>
17
  <table>
18
    <tr>
19
      <td>[% LxERP.t8("Total number of entries") %]:</td>
20
      <td align="right">[% SELF.statistics.total %]</td>
21
    </tr>
22

  
23
    <tr>
24
      <td>[% LxERP.t8("Entries with errors") %]:</td>
25
      <td align="right">[% SELF.statistics.errors %]</td>
26
    </tr>
27

  
28
    <tr>
29
      <td>[% LxERP.t8("Already imported entries (duplicates)") %]:</td>
30
      <td align="right">[% SELF.statistics.duplicates %]</td>
31
    </tr>
32

  
33
    <tr>
34
[% IF preview %]
35
      <td>[% LxERP.t8("Entries ready to import") %]:</td>
36
      <td align="right">[% SELF.statistics.to_import %]</td>
37
[% ELSE %]
38
      <td>[% LxERP.t8("Imported entries") %]:</td>
39
      <td align="right">[% SELF.statistics.imported %]</td>
40
[% END %]
41
    </tr>
42
  </table>
43
</div>
44

  
45
[% IF SELF.statistics.total %]
46

  
47
<h2>[% LxERP.t8("Transactions") %]</h2>
48

  
49
<table>
50
  <thead>
51
    <tr class="listheading">
52
      <th>[% LxERP.t8("Transaction date") %]</th>
53
      <th>[% LxERP.t8("Valutadate") %]</th>
54
      <th>[% LxERP.t8("Other party") %]</th>
55
      <th>[% LxERP.t8("Purpose") %]</th>
56
      <th>[% LxERP.t8("Amount") %]</th>
57
      <th>[% LxERP.t8("Remote account") %]</th>
58
      <th>[% LxERP.t8("Local account") %]</th>
59
      <th>[% LxERP.t8("Information") %]</th>
60
    </tr>
61
  </thead>
62

  
63
  <tbody>
64
    [% FOREACH transaction = SELF.transactions %]
65
      <tr class="listrow[% IF transaction.error %]_error[% END %]">
66
        <td align="right">[% transaction.transdate.to_kivitendo %]</td>
67
        <td align="right">[% transaction.valutadate.to_kivitendo %]</td>
68
        <td>[% HTML.escape(transaction.remote_name) %]</td>
69
        <td>[% HTML.escape(transaction.purpose) %]</td>
70
        <td align="right">[% LxERP.format_amount(transaction.amount, 2) %]</td>
71
        <td>
72
          [% IF transaction.remote_bank_code && transaction.remote_account_number %]
73
            [% HTML.escape(transaction.remote_bank_code) %] / [% HTML.escape(transaction.remote_account_number) %]
74
          [% END %]
75
        </td>
76
        <td>[% HTML.escape(transaction.bank_account.name) %]</td>
77
        <td>
78
          [% IF transaction.error %]
79
            [% HTML.escape(transaction.error) %]
80
          [% ELSIF transaction.duplicate %]
81
            [% LxERP.t8("Duplicate") %]
82
          [% ELSIF preview %]
83
            [% LxERP.t8("To import") %]
84
          [% ELSE %]
85
            [% LxERP.t8("Imported") %]
86
          [% END %]
87
        </td>
88
      </tr>
89
    [% END %]
90
  </tbody>
91
</table>
92

  
93
[% END %]
templates/webpages/bank_import/upload_mt940.html
1
[%- USE HTML %]
2
[%- USE LxERP %]
3
[%- USE L %]
4
[%- USE T8 %]
5

  
6
[%- INCLUDE 'common/flash.html' %]
7

  
8
<h1>[% FORM.title %]</h1>
9

  
10
<p>
11
  [% "Import a MT940 file:" | $T8 %]
12
</p>
13

  
14
<form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
15
  [% L.input_tag('file', '', type => 'file', accept => '*') %]
16
</form>
templates/webpages/bankimport/form.html
1
[%- USE HTML %]
2
[%- USE LxERP %]
3
[%- USE L %]
4
[%- USE T8 %]
5

  
6
 <div class="listtop">[% FORM.title %]</div>
7

  
8
 [% IF profile %]
9
 <p>
10
 [% "Import a MT940 file:" | $T8 %]
11
 </p>
12

  
13
 <form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
14
    [% L.input_tag('file', '', type => 'file', accept => '*') %]
15
 </form>
16
 [% ELSE %]
17
 <p>
18
 [% "Please create a CSV import profile called \"MT940\" for the import type bank transactions:" | $T8 %] <a href="[% SELF.url_for(controller => 'CsvImport', action => 'new', 'profile.type' => 'bank_transactions' ) %]">CsvImport</a>
19
 </p>
20
 [% END %]

Auch abrufbar als: Unified diff