- @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;
-
- foreach my $invoice (@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
- die t8("Bank transaction with id #1 has already been linked to #2.", $bank_transaction->id, $invoice->displayable_name)
- if _existing_record_link($bank_transaction, $invoice);
-
- my $payment_type;
- if ( defined $skonto_hash->{"$bt_id"} ) {
- $payment_type = shift(@{ $skonto_hash->{"$bt_id"} });
+ }
+ 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."),
+ };
+ }
+
+ 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"} });