+ 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."),
+ };
+ }
+
+ if ( $payment_sent
+ && any { ( $_->is_sales && ($_->amount > 0))
+ || (!$_->is_sales && ($_->amount < 0) && ($_->invoice_type eq 'purchase_invoice'))
+ } @{ $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(@{ $data{invoices} });
+ my $n_invoices = 0;
+
+ foreach my $invoice (@{ $data{invoices} }) {
+ my $source = ($data{sources} // [])->[$n_invoices];
+ my $memo = ($data{memos} // [])->[$n_invoices];
+
+ $n_invoices++ ;
+ # safety check invoice open
+ croak("Invoice closed. Cannot proceed.") unless ($invoice->open_amount);
+
+ if ( ($payment_sent && $bank_transaction->not_assigned_amount >= 0)
+ || ($payment_received && $bank_transaction->not_assigned_amount <= 0)) {
+ return {
+ %data,
+ 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."),
+ };
+ }
+
+ 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
+ # 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 => $amount_for_payment,
+ payment_type => $payment_type,
+ source => $source,
+ memo => $memo,
+ skonto_amount => $free_skonto_amount,
+ bt_id => $bt_id,
+ transdate => $bank_transaction->valutadate->to_kivitendo);
+ # ... and record the origin via BankTransactionAccTrans
+ if (scalar(@acc_ids) < 2) {
+ return {
+ %data,
+ result => 'error',
+ message => $::locale->text("Unable to book transactions for bank purpose #1", $bank_transaction->purpose),
+ };
+ }
+ foreach my $acc_trans_id (@acc_ids) {
+ my $id_type = $invoice->is_sales ? 'ar' : 'ap';
+ my %props_acc = (
+ acc_trans_id => $acc_trans_id,
+ bank_transaction_id => $bank_transaction->id,
+ $id_type => $invoice->id,
+ );
+ SL::DB::BankTransactionAccTrans->new(%props_acc)->save;
+ }
+ # Record a record link from the bank transaction to the invoice
+ my %props = (