Revision 66d468b0
Von Moritz Bunkus vor mehr als 8 Jahren hinzugefügt
SL/Controller/BankTransaction.pm | ||
---|---|---|
29 | 29 |
|
30 | 30 |
use Rose::Object::MakeMethods::Generic |
31 | 31 |
( |
32 |
'scalar --get_set_init' => [ qw(models) ],
|
|
32 |
'scalar --get_set_init' => [ qw(models problems) ],
|
|
33 | 33 |
); |
34 | 34 |
|
35 | 35 |
__PACKAGE__->run_before('check_auth'); |
... | ... | |
394 | 394 |
# '44' => [ '50', '51', 52' ] |
395 | 395 |
# }; |
396 | 396 |
|
397 |
my $skonto_hash = delete $::form->{invoice_skontos} || {}; # array containing the payment type, could be empty
|
|
397 |
$::form->{invoice_skontos} ||= {}; # hash of arrays containing the payment types, could be empty
|
|
398 | 398 |
|
399 | 399 |
# a bank_transaction may be assigned to several invoices, i.e. a customer |
400 | 400 |
# might pay several open invoices with one transaction |
401 | 401 |
|
402 |
while ( my ($bt_id, $invoice_ids) = each(%$invoice_hash) ) { |
|
403 |
my $bank_transaction = SL::DB::Manager::BankTransaction->find_by(id => $bt_id); |
|
402 |
$self->problems([]); |
|
403 |
|
|
404 |
while ( my ($bank_transaction_id, $invoice_ids) = each(%$invoice_hash) ) { |
|
405 |
push @{ $self->problems }, $self->save_single_bank_transaction( |
|
406 |
bank_transaction_id => $bank_transaction_id, |
|
407 |
invoice_ids => $invoice_ids, |
|
408 |
); |
|
409 |
} |
|
410 |
|
|
411 |
$self->action_list(); |
|
412 |
} |
|
413 |
|
|
414 |
sub save_single_bank_transaction { |
|
415 |
my ($self, %params) = @_; |
|
416 |
|
|
417 |
my %data = ( |
|
418 |
%params, |
|
419 |
bank_transaction => SL::DB::Manager::BankTransaction->find_by(id => $params{bank_transaction_id}), |
|
420 |
invoices => [], |
|
421 |
); |
|
422 |
|
|
423 |
if (!$data{bank_transaction}) { |
|
424 |
return { |
|
425 |
%data, |
|
426 |
result => 'error', |
|
427 |
message => $::locale->text('The ID #1 is not a valid database ID.', $data{bank_transaction_id}), |
|
428 |
}; |
|
429 |
} |
|
430 |
|
|
431 |
my (@warnings); |
|
432 |
|
|
433 |
my $worker = sub { |
|
434 |
my $bt_id = $data{bank_transaction_id}; |
|
435 |
my $bank_transaction = $data{bank_transaction}; |
|
404 | 436 |
my $sign = $bank_transaction->amount < 0 ? -1 : 1; |
405 | 437 |
my $amount_of_transaction = $sign * $bank_transaction->amount; |
406 | 438 |
|
407 | 439 |
my @invoices; |
408 |
foreach my $invoice_id (@{ $invoice_ids }) { |
|
409 |
push @invoices, (SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id)); |
|
440 |
foreach my $invoice_id (@{ $params{invoice_ids} }) { |
|
441 |
my $invoice = SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id); |
|
442 |
if (!$invoice) { |
|
443 |
return { |
|
444 |
%data, |
|
445 |
result => 'error', |
|
446 |
message => $::locale->text("The ID #1 is not a valid database ID.", $invoice_id), |
|
447 |
}; |
|
448 |
} |
|
449 |
|
|
450 |
push @invoices, $invoice; |
|
410 | 451 |
} |
452 |
|
|
453 |
$data{invoices} = \@invoices; |
|
454 |
|
|
411 | 455 |
@invoices = sort { return 1 if ( $a->is_sales and $a->amount > 0); |
412 | 456 |
return 1 if (!$a->is_sales and $a->amount < 0); |
413 | 457 |
return -1; |
... | ... | |
425 | 469 |
$n_invoices++ ; |
426 | 470 |
# Check if bank_transaction already has a link to the invoice, may only be linked once per invoice |
427 | 471 |
# This might be caused by the user reloading a page and resending the form |
428 |
die t8("Bank transaction with id #1 has already been linked to #2.", $bank_transaction->id, $invoice->displayable_name) |
|
429 |
if _existing_record_link($bank_transaction, $invoice); |
|
472 |
if (_existing_record_link($bank_transaction, $invoice)) { |
|
473 |
return { |
|
474 |
%data, |
|
475 |
result => 'error', |
|
476 |
message => $::locale->text("Bank transaction with id #1 has already been linked to #2.", $bank_transaction->id, $invoice->displayable_name), |
|
477 |
}; |
|
478 |
} |
|
479 |
|
|
480 |
if ($amount_of_transaction == 0) { |
|
481 |
push @warnings, { |
|
482 |
%data, |
|
483 |
result => 'warning', |
|
484 |
message => $::locale->text('There are invoices which could not be paid by bank transaction #1 (Account number: #2, bank code: #3)!', |
|
485 |
$bank_transaction->purpose, $bank_transaction->remote_account_number, $bank_transaction->remote_bank_code), |
|
486 |
}; |
|
487 |
last; |
|
488 |
} |
|
430 | 489 |
|
431 | 490 |
my $payment_type; |
432 |
if ( defined $skonto_hash->{"$bt_id"} ) {
|
|
433 |
$payment_type = shift(@{ $skonto_hash->{"$bt_id"} });
|
|
491 |
if ( defined $::form->{invoice_skontos}->{"$bt_id"} ) {
|
|
492 |
$payment_type = shift(@{ $::form->{invoice_skontos}->{"$bt_id"} });
|
|
434 | 493 |
} else { |
435 | 494 |
$payment_type = 'without_skonto'; |
436 | 495 |
}; |
437 |
if ($amount_of_transaction == 0) { |
|
438 |
flash('warning', $::locale->text('There are invoices which could not be paid by bank transaction #1 (Account number: #2, bank code: #3)!', |
|
439 |
$bank_transaction->purpose, |
|
440 |
$bank_transaction->remote_account_number, |
|
441 |
$bank_transaction->remote_bank_code)); |
|
442 |
last; |
|
443 |
} |
|
496 |
|
|
444 | 497 |
# pay invoice or go to the next bank transaction if the amount is not sufficiently high |
445 | 498 |
if ($invoice->open_amount <= $amount_of_transaction && $n_invoices < $max_invoices) { |
446 | 499 |
# first calculate new bank transaction amount ... |
... | ... | |
490 | 543 |
|
491 | 544 |
} |
492 | 545 |
$bank_transaction->save; |
493 |
} |
|
494 | 546 |
|
495 |
$self->action_list(); |
|
547 |
# 'undef' means 'no error' here. |
|
548 |
return undef; |
|
549 |
}; |
|
550 |
|
|
551 |
my $error; |
|
552 |
my $rez = $data{bank_transaction}->db->with_transaction(sub { |
|
553 |
eval { |
|
554 |
$error = $worker->(); |
|
555 |
1; |
|
556 |
|
|
557 |
} or do { |
|
558 |
$error = { |
|
559 |
%data, |
|
560 |
result => 'error', |
|
561 |
message => $@, |
|
562 |
}; |
|
563 |
}; |
|
564 |
|
|
565 |
die if $error; |
|
566 |
}); |
|
567 |
|
|
568 |
return grep { $_ } ($error, @warnings); |
|
496 | 569 |
} |
497 | 570 |
|
498 | 571 |
sub action_save_proposals { |
... | ... | |
651 | 724 |
return @$linked_records ? 1 : 0; |
652 | 725 |
}; |
653 | 726 |
|
727 |
sub init_problems { [] } |
|
654 | 728 |
|
655 | 729 |
sub init_models { |
656 | 730 |
my ($self) = @_; |
locale/de/all | ||
---|---|---|
369 | 369 |
'Bank transaction with id #1 has already been linked to #2.' => 'Bankbuchung mit id #1 wurde schon mit #2 verlinkt.', |
370 | 370 |
'Bank transactions' => 'Bankbewegungen', |
371 | 371 |
'Bank transactions MT940' => 'Kontoauszug verbuchen', |
372 |
'Bank transactions that either only have warnings or no message at all have been posted.' => 'Banktransaktionen, die entweder nur Warnungen oder gar keine Nachricht haben, wurden hingegen verbucht.', |
|
373 |
'Bank transactions with errors have not been posted.' => 'Banktransaktionen mit Fehlern wurden nicht verbucht.', |
|
372 | 374 |
'Bank transfer amount' => 'Überweisungssumme', |
373 | 375 |
'Bank transfer payment list for export #1' => 'Überweisungszahlungsliste für SEPA-Export #1', |
374 | 376 |
'Bank transfer via SEPA' => 'Überweisung via SEPA', |
... | ... | |
2730 | 2732 |
'The GL transaction #1 has been deleted.' => 'Die Dialogbuchung #1 wurde gelöscht.', |
2731 | 2733 |
'The IBAN \'#1\' is not valid as IBANs in #2 must be exactly #3 characters long.' => 'Die IBAN \'#1\' ist ungültig, da IBANs in #2 genau #3 Zeichen lang sein müssen.', |
2732 | 2734 |
'The IBAN is missing.' => 'Die IBAN fehlt.', |
2735 |
'The ID #1 is not a valid database ID.' => 'Die ID #1 ist keine gültige Datenbank-ID.', |
|
2733 | 2736 |
'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.', |
2734 | 2737 |
'The MT940 import needs an import profile called MT940' => 'Der MT940 Import benötigt ein Importprofil mit dem Namen "MT940"', |
2735 | 2738 |
'The PDF has been created' => 'Die PDF-Datei wurde erstellt.', |
... | ... | |
3327 | 3330 |
'Warn before saving orders with duplicate parts (new controller only)' => 'Beim Speichern warnen, wenn doppelte Artikel in einem Auftrag sind', |
3328 | 3331 |
'Warning' => 'Warnung', |
3329 | 3332 |
'Warning! Loading a draft will discard unsaved data!' => 'Achtung! Beim Laden eines Entwurfs werden ungespeicherte Daten verworfen!', |
3333 |
'Warnings and errors' => 'Warnungen und Fehler', |
|
3330 | 3334 |
'WebDAV' => 'WebDAV', |
3331 | 3335 |
'WebDAV link' => 'WebDAV-Link', |
3332 | 3336 |
'WebDAV save documents' => 'Belege in WebDAV-Ablage speichern', |
templates/webpages/bank_transactions/_problems.html | ||
---|---|---|
1 |
[%- USE LxERP -%][%- USE T8 -%][%- USE HTML -%][%- USE P -%] |
|
2 |
<h3> |
|
3 |
[% LxERP.t8("Warnings and errors") %] |
|
4 |
</h3> |
|
5 |
|
|
6 |
<p> |
|
7 |
[% LxERP.t8("Bank transactions with errors have not been posted.") %] |
|
8 |
[% LxERP.t8("Bank transactions that either only have warnings or no message at all have been posted.") %] |
|
9 |
</p> |
|
10 |
|
|
11 |
<table> |
|
12 |
<thead> |
|
13 |
<tr class="listheading"> |
|
14 |
<th>[% LxERP.t8("Type") %]</th> |
|
15 |
<th>[% LxERP.t8("Invoices") %]</th> |
|
16 |
<th>[% LxERP.t8("Transdate") %]</th> |
|
17 |
<th>[% LxERP.t8("Amount") %]</th> |
|
18 |
<th>[% LxERP.t8("Remote name") %]</th> |
|
19 |
<th>[% LxERP.t8("Purpose") %]</th> |
|
20 |
<th>[% LxERP.t8("Remote account number") %]</th> |
|
21 |
<th>[% LxERP.t8("Remote bank code") %]</th> |
|
22 |
<th>[% LxERP.t8("Message") %]</th> |
|
23 |
</tr> |
|
24 |
</thead> |
|
25 |
|
|
26 |
<tbody> |
|
27 |
[% FOREACH problem = SELF.problems %] |
|
28 |
<tr class="listrow[% IF problem.result == 'error' %]_error[% END %]"> |
|
29 |
<td>[% IF problem.result == 'error' %][% LxERP.t8("Error") %][% ELSE %][% LxERP.t8("Warning") %][% END %]</td> |
|
30 |
<td> |
|
31 |
[% FOREACH invoice = problem.invoices %] |
|
32 |
[% P.invoice(invoice) %] |
|
33 |
[% UNLESS loop.last %]<br>[% END %] |
|
34 |
[% END %] |
|
35 |
</td> |
|
36 |
<td>[% HTML.escape(problem.bank_transaction.transdate.to_kivitendo) %]</td> |
|
37 |
<td>[% HTML.escape(LxERP.format_amount(problem.bank_transaction.amount, 2)) %]</td> |
|
38 |
<td>[% HTML.escape(problem.bank_transaction.remote_name) %]</td> |
|
39 |
<td>[% HTML.escape(problem.bank_transaction.purpose) %]</td> |
|
40 |
<td>[% HTML.escape(problem.bank_transaction.remote_account_number) %]</td> |
|
41 |
<td>[% HTML.escape(problem.bank_transaction.remote_bank_code) %]</td> |
|
42 |
<td>[% HTML.escape(problem.message) %]</td> |
|
43 |
</tr> |
|
44 |
[% END %] |
|
45 |
</tbody> |
|
46 |
</table> |
templates/webpages/bank_transactions/list.html | ||
---|---|---|
4 | 4 |
|
5 | 5 |
[%- INCLUDE 'common/flash.html' %] |
6 | 6 |
|
7 |
[% IF SELF.problems.size %] |
|
8 |
[% INCLUDE 'bank_transactions/_problems.html' %] |
|
9 |
[% END %] |
|
10 |
|
|
7 | 11 |
<p>[% HTML.escape(bank_account.name) %] [% HTML.escape(bank_account.iban) %], [% 'Bank code' | $T8 %] [% HTML.escape(bank_account.bank_code) %], [% 'Bank' | $T8 %] [% HTML.escape(bank_account.bank) %]</p> |
8 | 12 |
<p> |
9 | 13 |
[% IF FORM.filter.fromdate %] [% 'From' | $T8 %] [% FORM.filter.fromdate %] [% END %] |
Auch abrufbar als: Unified diff
Bankauszug verbuchen: Warnungen/Fehler anzeigen; pro Zeile eine DB-Transaktion
Das Verbuchen von Bankauszügen wird nun in Datenbanktransaktionen
gekapselt. Damit die BenutzerIn bei einem Fehler nicht alles erneut
einstellen muss, wird eine Datenbanktransaktion pro
Banktransaktion benutzt.
Treten dabei Warnungen oder Fehler auf, so werden diese nun in einer
Tabelle übersichtlich dargestellt. Die Tabelle enthält sowohl die Daten
der Banktransaktion, bei der das Problem auftrat (entfernte
Kontonummer/BIC, entferne KontobesitzerIn, Betrag, Transaktionsdatum),
als auch Links zu den mit dieser Transaktion verknüpften
Rechnungen. Damit wird die BearbeiterIn in die Lage versetzt, die Fehler
genauer zu analysieren.