+  if ( $::form->{proposal_ids} ) {
+    foreach (@{ $::form->{proposal_ids} }) {
+      my  $bank_transaction_id = $_;
+      my  $invoice_ids = $invoice_hash{$_};
+      push @{ $self->problems }, $self->save_single_bank_transaction(
+        bank_transaction_id => $bank_transaction_id,
+        invoice_ids         => $invoice_ids,
+        sources             => ($::form->{sources} // {})->{$_},
+        memos               => ($::form->{memos}   // {})->{$_},
+      );
+      $count += scalar( @{$invoice_ids} );
+    }
+  } else {
+    while ( my ($bank_transaction_id, $invoice_ids) = each(%invoice_hash) ) {
+      push @{ $self->problems }, $self->save_single_bank_transaction(
+        bank_transaction_id => $bank_transaction_id,
+        invoice_ids         => $invoice_ids,
+        sources             => [  map { $::form->{"sources_${bank_transaction_id}_${_}"} } @{ $invoice_ids } ],
+        memos               => [  map { $::form->{"memos_${bank_transaction_id}_${_}"}   } @{ $invoice_ids } ],
+      );
+      $count += scalar( @{$invoice_ids} );
+    }
+  }
+  my $max_count = $count;
+  foreach (@{ $self->problems }) {
+    $count-- if $_->{result} eq 'error';
+  }
+  return ($count, $max_count);
+}
+
+sub action_save_invoices {
+  my ($self) = @_;
+  my ($success_count, $max_count) = $self->save_invoices();
+
+  if ($success_count == $max_count) {
+    flash('ok', t8('#1 invoice(s) saved.', $success_count));
+  } else {
+    flash('error', t8('At least #1 invoice(s) not saved', $max_count - $success_count));
+  }
+
+  $self->action_list();
+}
+
+sub action_save_proposals {
+  my ($self) = @_;
+
+  if ( $::form->{proposal_ids} ) {
+    my $propcount = scalar(@{ $::form->{proposal_ids} });
+    if ( $propcount > 0 ) {
+      my $count = $self->save_invoices();
+
+      flash('ok', t8('#1 proposal(s) with #2 invoice(s) saved.',  $propcount, $count));
+    }
+  }
+  $self->action_list();
+
+}
+
+sub save_single_bank_transaction {
+  my ($self, %params) = @_;
+
+  my %data = (
+    %params,
+    bank_transaction => SL::DB::Manager::BankTransaction->find_by(id => $params{bank_transaction_id}),
+    invoices         => [],
+  );
+
+  if (!$data{bank_transaction}) {
+    return {
+      %data,
+      result => 'error',
+      message => $::locale->text('The ID #1 is not a valid database ID.', $data{bank_transaction_id}),
+    };
+  }
+
+  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 $payment_received      = $bank_transaction->amount > 0;
+    my $payment_sent          = $bank_transaction->amount < 0;
+
+
+    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) {
+        return {
+          %data,
+          result  => 'error',
+          message => $::locale->text("The ID #1 is not a valid database ID.", $invoice_id),
+        };
+      }
+      push @{ $data{invoices} }, $invoice;
+    }
+
+    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."),
+      };