Revision 789de0c0
Von Moritz Bunkus vor etwa 4 Jahren hinzugefügt
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
MT940-Import: Implementation eines eigenen Parsers anstelle von AQBanking