GDPDU: customer, vendor und chart auf einige wenige Spalten beschränken
[kivitendo-erp.git] / SL / Controller / BankTransaction.pm
index c161735..68047f3 100644 (file)
@@ -25,6 +25,8 @@ use SL::DB::Draft;
 use SL::DB::BankAccount;
 use SL::DBUtils qw(like);
 use SL::Presenter;
+
+use List::MoreUtils qw(any);
 use List::Util qw(max);
 
 use Rose::Object::MakeMethods::Generic
@@ -435,8 +437,9 @@ sub save_single_bank_transaction {
     my $bank_transaction      = $data{bank_transaction};
     my $sign                  = $bank_transaction->amount < 0 ? -1 : 1;
     my $amount_of_transaction = $sign * $bank_transaction->amount;
+    my $payment_received      = $bank_transaction->amount > 0;
+    my $payment_sent          = $bank_transaction->amount < 0;
 
-    my @invoices;
     foreach my $invoice_id (@{ $params{invoice_ids} }) {
       my $invoice = SL::DB::Manager::Invoice->find_by(id => $invoice_id) || SL::DB::Manager::PurchaseInvoice->find_by(id => $invoice_id);
       if (!$invoice) {
@@ -447,26 +450,38 @@ sub save_single_bank_transaction {
         };
       }
 
-      push @invoices, $invoice;
+      push @{ $data{invoices} }, $invoice;
     }
 
-    $data{invoices} = \@invoices;
+    if (   $payment_received
+        && any {    ( $_->is_sales && ($_->amount < 0))
+                 || (!$_->is_sales && ($_->amount > 0))
+               } @{ $data{invoices} }) {
+      return {
+        %data,
+        result  => 'error',
+        message => $::locale->text("Received payments can only be posted for sales invoices and purchase credit notes."),
+      };
+    }
 
-    @invoices = sort { return 1 if ( $a->is_sales and $a->amount > 0);
-                       return 1 if (!$a->is_sales and $a->amount < 0);
-                       return -1;
-                     } @invoices if $bank_transaction->amount > 0;
-    @invoices = sort { return -1 if ( $a->is_sales and $a->amount > 0);
-                       return -1 if (!$a->is_sales and $a->amount < 0);
-                       return 1;
-                     } @invoices if $bank_transaction->amount < 0;
+    if (   $payment_sent
+        && any {    ( $_->is_sales && ($_->amount > 0))
+                 || (!$_->is_sales && ($_->amount < 0))
+               } @{ $data{invoices} }) {
+      return {
+        %data,
+        result  => 'error',
+        message => $::locale->text("Sent payments can only be posted for purchase invoices and sales credit notes."),
+      };
+    }
 
-    my $max_invoices = scalar(@invoices);
+    my $max_invoices = scalar(@{ $data{invoices} });
     my $n_invoices   = 0;
 
-    foreach my $invoice (@invoices) {
+    foreach my $invoice (@{ $data{invoices} }) {
 
       $n_invoices++ ;
+
       # Check if bank_transaction already has a link to the invoice, may only be linked once per invoice
       # This might be caused by the user reloading a page and resending the form
       if (_existing_record_link($bank_transaction, $invoice)) {
@@ -477,14 +492,12 @@ sub save_single_bank_transaction {
         };
       }
 
-      if ($amount_of_transaction == 0) {
-        push @warnings, {
+      if (!$amount_of_transaction && $invoice->open_amount) {
+        return {
           %data,
-          result  => 'warning',
-          message => $::locale->text('There are invoices which could not be paid by bank transaction #1 (Account number: #2, bank code: #3)!',
-                                     $bank_transaction->purpose, $bank_transaction->remote_account_number, $bank_transaction->remote_bank_code),
+          result  => 'error',
+          message => $::locale->text("A payment can only be posted for multiple invoices if the amount to post is equal to or bigger than the sum of the open amounts of the affected invoices."),
         };
-        last;
       }
 
       my $payment_type;
@@ -511,6 +524,7 @@ sub save_single_bank_transaction {
                               payment_type => $payment_type,
                               transdate    => $bank_transaction->transdate->to_kivitendo);
       } else { # use the whole amount of the bank transaction for the invoice, overpay the invoice if necessary
+        my $overpaid_amount = $amount_of_transaction - $invoice->open_amount;
         $invoice->pay_invoice(chart_id     => $bank_transaction->local_bank_account->chart_id,
                               trans_id     => $invoice->id,
                               amount       => $amount_of_transaction,
@@ -518,6 +532,14 @@ sub save_single_bank_transaction {
                               transdate    => $bank_transaction->transdate->to_kivitendo);
         $bank_transaction->invoice_amount($bank_transaction->amount);
         $amount_of_transaction = 0;
+
+        if ($overpaid_amount >= 0.01) {
+          push @warnings, {
+            %data,
+            result  => 'warning',
+            message => $::locale->text('Invoice #1 was overpaid by #2.', $invoice->invnumber, $::form->format_amount(\%::myconfig, $overpaid_amount, 2)),
+          };
+        }
       }
 
       # Record a record link from the bank transaction to the invoice
@@ -755,3 +777,68 @@ sub init_models {
 }
 
 1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Controller::BankTransaction - Posting payments to invoices from
+bank transactions imported earlier
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<save_single_bank_transaction %params>
+
+Takes a bank transaction ID (as parameter C<bank_transaction_id> and
+tries to post its amount to a certain number of invoices (parameter
+C<invoice_ids>, an array ref of database IDs to purchase or sales
+invoice objects).
+
+The whole function is wrapped in a database transaction. If an
+exception occurs the bank transaction is not posted at all. The same
+is true if the code detects an error during the execution, e.g. a bank
+transaction that's already been posted earlier. In both cases the
+database transaction will be rolled back.
+
+If warnings but not errors occur the database transaction is still
+committed.
+
+The return value is an error object or C<undef> if the function
+succeeded. The calling function will collect all warnings and errors
+and display them in a nicely formatted table if any occurred.
+
+An error object is a hash reference containing the following members:
+
+=over 2
+
+=item * C<result> — can be either C<warning> or C<error>. Warnings are
+displayed slightly different than errors.
+
+=item * C<message> — a human-readable message included in the list of
+errors meant as the description of why the problem happened
+
+=item * C<bank_transaction_id>, C<invoice_ids> — the same parameters
+that the function was called with
+
+=item * C<bank_transaction> — the database object
+(C<SL::DB::BankTransaction>) corresponding to C<bank_transaction_id>
+
+=item * C<invoices> — an array ref of the database objects (either
+C<SL::DB::Invoice> or C<SL::DB::PurchaseInvoice>) corresponding to
+C<invoice_ids>
+
+=back
+
+=back
+
+=head1 AUTHOR
+
+Niclas Zimmermann E<lt>niclas@kivitendo-premium.deE<gt>,
+Geoffrey Richardson E<lt>information@richardson-bueren.deE<gt>
+
+=cut