X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/9af3e1927c6fe3c631b63310888f1909039f24b6..72b10cee5d0beac5c6169f5d18c074fbdc791395:/SL/Controller/BankTransaction.pm diff --git a/SL/Controller/BankTransaction.pm b/SL/Controller/BankTransaction.pm index 69bc63114..98639ff0f 100644 --- a/SL/Controller/BankTransaction.pm +++ b/SL/Controller/BankTransaction.pm @@ -24,11 +24,12 @@ use SL::DB::AccTransaction; use SL::DB::BankTransactionAccTrans; use SL::DB::Tax; use SL::DB::BankAccount; +use SL::DB::GLTransaction; use SL::DB::RecordTemplate; use SL::DB::SepaExportItem; use SL::DBUtils qw(like do_query); -use SL::Presenter::Tag qw(checkbox_tag); +use SL::Presenter::Tag qw(checkbox_tag html_tag); use Carp; use List::UtilsBy qw(partition_by); use List::MoreUtils qw(any); @@ -308,20 +309,16 @@ sub action_ajax_payment_suggestion { # create an HTML blob to be used by the js function add_invoices in templates/webpages/bank_transactions/list.html # and return encoded as JSON - my $bt = SL::DB::Manager::BankTransaction->find_by( id => $::form->{bt_id} ); - my $invoice = SL::DB::Manager::Invoice->find_by( id => $::form->{prop_id} ) || SL::DB::Manager::PurchaseInvoice->find_by( id => $::form->{prop_id} ); + croak("Need bt_id") unless $::form->{bt_id}; - die unless $bt and $invoice; + my $invoice = SL::DB::Manager::Invoice->find_by( id => $::form->{prop_id} ) || SL::DB::Manager::PurchaseInvoice->find_by( id => $::form->{prop_id} ); - my @select_options = $invoice->get_payment_select_options_for_bank_transaction($::form->{bt_id}); + croak("No valid invoice found") unless $invoice; - my $html; - $html = $self->render( + my $html = $self->render( 'bank_transactions/_payment_suggestion', { output => 0 }, bt_id => $::form->{bt_id}, - prop_id => $::form->{prop_id}, invoice => $invoice, - SELECT_OPTIONS => \@select_options, ); $self->render(\ SL::JSON::to_json( { 'html' => "$html" } ), { layout => 0, type => 'json', process => 0 }); @@ -561,12 +558,18 @@ sub save_single_bank_transaction { my $bank_transaction = $data{bank_transaction}; + if ($bank_transaction->closed_period) { + return { + %data, + result => 'error', + message => $::locale->text('Cannot post payment for a closed period!'), + }; + } my (@warnings); my $worker = sub { my $bt_id = $data{bank_transaction_id}; my $sign = $bank_transaction->amount < 0 ? -1 : 1; - my $not_assigned_amount = $bank_transaction->not_assigned_amount; my $payment_received = $bank_transaction->amount > 0; my $payment_sent = $bank_transaction->amount < 0; @@ -613,9 +616,11 @@ sub save_single_bank_transaction { my $memo = ($data{memos} // [])->[$n_invoices]; $n_invoices++ ; + # safety check invoice open + croak("Invoice closed. Cannot proceed.") unless ($invoice->open_amount); - - if (!$not_assigned_amount && $invoice->open_amount) { + if ( ($payment_sent && $bank_transaction->not_assigned_amount >= 0) + || ($payment_received && $bank_transaction->not_assigned_amount <= 0)) { return { %data, result => 'error', @@ -623,32 +628,58 @@ sub save_single_bank_transaction { }; } - my $payment_type; + my ($payment_type, $free_skonto_amount); if ( defined $::form->{invoice_skontos}->{"$bt_id"} ) { $payment_type = shift(@{ $::form->{invoice_skontos}->{"$bt_id"} }); } else { $payment_type = 'without_skonto'; - }; + } + + if ($payment_type eq 'free_skonto') { + # parse user input > 0 + if ($::form->parse_amount(\%::myconfig, $::form->{"free_skonto_amount"}->{"$bt_id"}{$invoice->id}) > 0) { + $free_skonto_amount = $::form->parse_amount(\%::myconfig, $::form->{"free_skonto_amount"}->{"$bt_id"}{$invoice->id}); + } else { + return { + %data, + result => 'error', + message => $::locale->text("Free skonto amount has to be a positive number."), + }; + } + } # pay invoice # TODO rewrite this: really booked amount should be a return value of Payment.pm # also this controller shouldnt care about how to calc skonto. we simply delegate the # payment_type to the helper and get the corresponding bank_transaction values back - - my $open_amount = ($payment_type eq 'with_skonto_pt' ? $invoice->amount_less_skonto : $invoice->open_amount); - my $amount_for_booking = abs(($open_amount < $not_assigned_amount) ? $open_amount : $not_assigned_amount); + # hotfix to get the signs right - compare absolute values and later set the signs + # should be better done elsewhere - changing not_assigned_amount to abs feels seriously bogus + + my $open_amount = $payment_type eq 'with_skonto_pt' ? $invoice->amount_less_skonto : $invoice->open_amount; + $open_amount = abs($open_amount); + $open_amount -= $free_skonto_amount if ($payment_type eq 'free_skonto'); + my $not_assigned_amount = abs($bank_transaction->not_assigned_amount); + my $amount_for_booking = ($open_amount < $not_assigned_amount) ? $open_amount : $not_assigned_amount; + my $amount_for_payment = $amount_for_booking; + + # get the right direction for the payment bookings (all amounts < 0 are stornos, credit notes or negative ap) + $amount_for_payment *= -1 if $invoice->amount < 0; + $free_skonto_amount *= -1 if ($free_skonto_amount && $invoice->amount < 0); + # get the right direction for the bank transaction $amount_for_booking *= $sign; + $bank_transaction->invoice_amount($bank_transaction->invoice_amount + $amount_for_booking); # ... and then pay the invoice my @acc_ids = $invoice->pay_invoice(chart_id => $bank_transaction->local_bank_account->chart_id, - trans_id => $invoice->id, - amount => ($open_amount < $not_assigned_amount) ? $open_amount : $not_assigned_amount, - payment_type => $payment_type, - source => $source, - memo => $memo, - transdate => $bank_transaction->transdate->to_kivitendo); + trans_id => $invoice->id, + amount => $amount_for_payment, + payment_type => $payment_type, + source => $source, + memo => $memo, + skonto_amount => $free_skonto_amount, + transdate => $bank_transaction->valutadate->to_kivitendo); # ... and record the origin via BankTransactionAccTrans - if (scalar(@acc_ids) != 2) { + if (scalar(@acc_ids) < 2) { return { %data, result => 'error', @@ -715,17 +746,17 @@ sub save_single_bank_transaction { return grep { $_ } ($error, @warnings); } sub action_unlink_bank_transaction { - my ($self) = @_; + my ($self, %params) = @_; croak("No bank transaction ids") unless scalar @{ $::form->{ids}} > 0; - my $closedto = $::locale->parse_date_to_object($::instance_conf->get_closedto); my $success_count; foreach my $bt_id (@{ $::form->{ids}} ) { my $bank_transaction = SL::DB::Manager::BankTransaction->find_by(id => $bt_id); croak("No valid bank transaction found") unless (ref($bank_transaction) eq 'SL::DB::BankTransaction'); + croak t8('Cannot unlink payment for a closed period!') if $bank_transaction->closed_period; # everything in one transaction my $rez = $bank_transaction->db->with_transaction(sub { @@ -737,31 +768,33 @@ sub action_unlink_bank_transaction { foreach my $acc_trans_id_entry (@{ SL::DB::Manager::BankTransactionAccTrans->get_all(where => [bank_transaction_id => $bt_id ] )}) { my $acc_trans = SL::DB::Manager::AccTransaction->get_all(where => [acc_trans_id => $acc_trans_id_entry->acc_trans_id]); - # check closedto for acc trans entries - croak t8('Cannot unlink payment for a closed period!') if (ref $closedto && grep { $_->transdate < $closedto } @{ $acc_trans } ); # save trans_id and type die "no type" unless ($acc_trans_id_entry->ar_id || $acc_trans_id_entry->ap_id || $acc_trans_id_entry->gl_id); $trans_ids{$acc_trans_id_entry->ar_id} = 'ar' if $acc_trans_id_entry->ar_id; $trans_ids{$acc_trans_id_entry->ap_id} = 'ap' if $acc_trans_id_entry->ap_id; $trans_ids{$acc_trans_id_entry->gl_id} = 'gl' if $acc_trans_id_entry->gl_id; - # 2. all good -> ready to delete acc_trans and bt_acc link $acc_trans_id_entry->delete; $_->delete for @{ $acc_trans }; } # 3. update arap.paid (may not be 0, yet) + # or in case of gl, delete whole entry while (my ($trans_id, $type) = each %trans_ids) { - next if $type eq 'gl'; + if ($type eq 'gl') { + SL::DB::Manager::GLTransaction->delete_all(where => [ id => $trans_id ]); + next; + } die ("invalid type") unless $type =~ m/^(ar|ap)$/; # recalc and set paid via database query my $query = qq|UPDATE $type SET paid = (SELECT COALESCE(abs(sum(amount)),0) FROM acc_trans WHERE trans_id = ? - AND chart_link ilike '%paid%')|; + AND chart_link ilike '%paid%') + WHERE id = ?|; - die if (do_query($::form, $bank_transaction->db->dbh, $query, $trans_id) == -1); + die if (do_query($::form, $bank_transaction->db->dbh, $query, $trans_id, $trans_id) == -1); } # 4. and delete all (if any) record links my $rl = SL::DB::Manager::RecordLink->delete_all(where => [ from_id => $bt_id, from_table => 'bank_transactions' ]); @@ -779,7 +812,7 @@ sub action_unlink_bank_transaction { } flash('ok', t8('#1 bank transaction bookings undone.', $success_count)); - $self->action_list_all(); + $self->action_list_all() unless $params{testcase}; } # # filters @@ -829,8 +862,13 @@ sub prepare_report { my %column_defs = ( ids => { raw_header_data => checkbox_tag("", id => "check_all", checkall => "[data-checkall=1]"), 'align' => 'center', - raw_data => sub { if (@{ $_[0]->linked_invoices } && !(grep {ref ($_) eq 'SL::DB::GLTransaction' } @{ $_[0]->linked_invoices })) { - checkbox_tag("ids[]", value => $_[0]->id, "data-checkall" => 1); } } }, + raw_data => sub { if (@{ $_[0]->linked_invoices }) { + if ($_[0]->closed_period) { + html_tag('text', "X"); #, tooltip => t8('Bank Transaction is in a closed period.')), + } else { + checkbox_tag("ids[]", value => $_[0]->id, "data-checkall" => 1); + } + } } }, transdate => { sub => sub { $_[0]->transdate_as_date } }, valutadate => { sub => sub { $_[0]->valutadate_as_date } }, remote_name => { }, @@ -936,6 +974,7 @@ sub load_gl_record_template_url { 'form_defaults.callback' => $self->callback, 'form_defaults.bt_id' => $self->transaction->id, 'form_defaults.bt_chart_id' => $self->transaction->local_bank_account->chart->id, + 'form_defaults.description' => $self->transaction->purpose, ); }