Payment-Helper _skonto_charts... debugs und kommentare aufgerÀumt
[kivitendo-erp.git] / SL / DB / Helper / Payment.pm
index dced3d7..6db8951 100644 (file)
@@ -4,20 +4,22 @@ use strict;
 
 use parent qw(Exporter);
 our @EXPORT = qw(pay_invoice);
-our @EXPORT_OK = qw(skonto_date skonto_charts amount_less_skonto within_skonto_period percent_skonto reference_account reference_amount open_amount open_percent remaining_skonto_days skonto_amount check_skonto_configuration valid_skonto_amount get_payment_suggestions validate_payment_type open_sepa_transfer_amount get_payment_select_options_for_bank_transaction exchangerate forex);
+our @EXPORT_OK = qw(skonto_date amount_less_skonto within_skonto_period percent_skonto reference_account reference_amount open_amount open_percent remaining_skonto_days skonto_amount check_skonto_configuration valid_skonto_amount get_payment_suggestions validate_payment_type open_sepa_transfer_amount get_payment_select_options_for_bank_transaction exchangerate forex _skonto_charts_and_tax_correction);
 our %EXPORT_TAGS = (
   "ALL" => [@EXPORT, @EXPORT_OK],
 );
 
 require SL::DB::Chart;
+
+use Carp;
 use Data::Dumper;
 use DateTime;
-use SL::DATEV qw(:CONSTANTS);
-use SL::Locale::String qw(t8);
 use List::Util qw(sum);
+
+use SL::DATEV qw(:CONSTANTS);
 use SL::DB::Exchangerate;
 use SL::DB::Currency;
-use Carp;
+use SL::Locale::String qw(t8);
 
 #
 # Public functions not exported by default
@@ -39,6 +41,7 @@ sub pay_invoice {
 
   # check for required parameters and optional params depending on payment_type
   Common::check_params(\%params, qw(chart_id transdate));
+  Common::check_params(\%params, qw(bt_id)) unless $params{payment_type} eq 'without_skonto';
   if ( $params{'payment_type'} eq 'without_skonto' && abs($params{'amount'}) < 0) {
     croak "invalid amount for payment_type 'without_skonto': $params{'amount'}\n";
   }
@@ -58,10 +61,10 @@ sub pay_invoice {
   }
 
   my $transdate_obj;
-  if (ref($params{transdate} eq 'DateTime')) {
+  if (ref($params{transdate}) eq 'DateTime') {
     $transdate_obj = $params{transdate};
   } else {
-   $transdate_obj = $::locale->parse_date_to_object($params{transdate});
+    $transdate_obj = $::locale->parse_date_to_object($params{transdate});
   };
   croak t8('Illegal date') unless ref $transdate_obj;
 
@@ -77,8 +80,9 @@ sub pay_invoice {
   };
 
   # currency is either passed or use the invoice currency if it differs from the default currency
+  # TODO remove
   my ($exchangerate,$currency);
-  if ($params{currency} || $params{currency_id} || $self->currency_id != $::instance_conf->get_currency_id) {
+  if ($params{currency} || $params{currency_id}) {
     if ($params{currency} || $params{currency_id} ) { # currency was specified
       $currency = SL::DB::Manager::Currency->find_by(name => $params{currency}) || SL::DB::Manager::Currency->find_by(id => $params{currency_id});
     } else { # use invoice currency
@@ -219,12 +223,14 @@ sub pay_invoice {
       if ( $params{payment_type} eq 'with_skonto_pt' ) {
         $total_skonto_amount = $self->skonto_amount;
       } elsif ( $params{payment_type} eq 'difference_as_skonto' ) {
+        # only used for tests. no real code calls this payment_type!
         $total_skonto_amount = $self->open_amount;
       } elsif ( $params{payment_type} eq 'free_skonto') {
         $total_skonto_amount = $params{skonto_amount};
       }
-      my @skonto_bookings = $self->skonto_charts($total_skonto_amount);
-
+      my @skonto_bookings = $self->_skonto_charts_and_tax_correction(amount => $total_skonto_amount, bt_id => $params{bt_id},
+                                                                     transdate_obj => $transdate_obj, memo => $params{memo},
+                                                                     source => $params{source});
       # error checking:
       if ( $params{payment_type} eq 'difference_as_skonto' ) {
         my $calculated_skonto_sum  = sum map { $_->{skonto_amount} } @skonto_bookings;
@@ -234,6 +240,7 @@ sub pay_invoice {
       my $reference_amount = $total_skonto_amount;
 
       # create an acc_trans entry for each result of $self->skonto_charts
+      # TODO create internal sub _skonto_bookings
       foreach my $skonto_booking ( @skonto_bookings ) {
         next unless $skonto_booking->{'chart_id'};
         next unless $skonto_booking->{'skonto_amount'} != 0;
@@ -254,7 +261,7 @@ sub pay_invoice {
         $reference_amount -= abs($amount);
         $paid_amount      += -1 * $amount * $exchangerate;
         $skonto_amount_check -= $skonto_booking->{'skonto_amount'};
-      };
+      }
       if ( $params{payment_type} eq 'difference_as_skonto' ) {
           die "difference_as_skonto calculated incorrectly, sum of calculated payments doesn't add up to open amount $total_open_amount, reference_amount = $reference_amount\n" unless _round($reference_amount) == 0;
       }
@@ -290,6 +297,67 @@ sub pay_invoice {
     $arap_booking->save;
     push @new_acc_ids, $arap_booking->acc_trans_id;
 
+    # hook for invoice_for_advance_payment DATEV always pairs, acc_trans_id has to be higher than arap_booking ;-)
+    if ($self->invoice_type eq 'invoice_for_advance_payment') {
+      my $clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load;
+      die "No Clearing Chart for Advance Payment" unless ref $clearing_chart eq 'SL::DB::Chart';
+
+      # what does ptc say
+      my %inv_calc = $self->calculate_prices_and_taxes();
+      my @trans_ids = keys %{ $inv_calc{amounts} };
+      die "Invalid state for advance payment more than one trans_id" if (scalar @trans_ids > 1);
+      my $entry = delete $inv_calc{amounts}{$trans_ids[0]};
+      my $tax;
+      if ($entry->{tax_id}) {
+        $tax = SL::DB::Manager::Tax->find_by(id => $entry->{tax_id}); # || die "Can't find tax with id " . $entry->{tax_id};
+      }
+      if ($tax and $tax->rate != 0) {
+        my ($netamount, $taxamount);
+        my $roundplaces = 2;
+        # we dont have a clue about skonto, that's why we use $arap_amount as taxincluded
+        ($netamount, $taxamount) = Form->calculate_tax($arap_amount, $tax->rate, 1, $roundplaces);
+        # for debugging database set
+        my $fullmatch = $netamount == $entry->{amount} ? '::netamount total true' : '';
+        my $transfer_chart = $tax->taxkey == 2 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_7_id)->load
+                          :  $tax->taxkey == 3 ? SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_taxable_19_id)->load
+                          :  undef;
+        die "No Transfer Chart for Advance Payment" unless ref $transfer_chart eq 'SL::DB::Chart';
+
+        my $arap_full_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                           chart_id   => $clearing_chart->id,
+                                                           chart_link => $clearing_chart->link,
+                                                           amount     => $arap_amount * -1, # full amount
+                                                           transdate  => $transdate_obj,
+                                                           source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                           taxkey     => 0,
+                                                           tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
+        $arap_full_booking->save;
+        push @new_acc_ids, $arap_full_booking->acc_trans_id;
+
+        my $arap_tax_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                          chart_id   => $transfer_chart->id,
+                                                          chart_link => $transfer_chart->link,
+                                                          amount     => _round($netamount), # full amount
+                                                          transdate  => $transdate_obj,
+                                                          source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                          taxkey     => $tax->taxkey,
+                                                          tax_id     => $tax->id);
+        $arap_tax_booking->save;
+        push @new_acc_ids, $arap_tax_booking->acc_trans_id;
+
+        my $tax_booking= SL::DB::AccTransaction->new(trans_id   => $self->id,
+                                                     chart_id   => $tax->chart_id,
+                                                     chart_link => $tax->chart->link,
+                                                     amount     => _round($taxamount),
+                                                     transdate  => $transdate_obj,
+                                                     source     => 'Automatic Tax Booking for Payment in Advance' . $fullmatch,
+                                                     taxkey     => 0,
+                                                     tax_id     => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
+
+        $tax_booking->save;
+        push @new_acc_ids, $tax_booking->acc_trans_id;
+      }
+    }
     $fx_gain_loss_amount *= -1 if $self->is_sales;
     $self->paid($self->paid + _round($paid_amount) + $fx_gain_loss_amount) if $paid_amount;
     $self->datepaid($transdate_obj);
@@ -338,21 +406,9 @@ sub skonto_date {
 
   my $self = shift;
 
-  my $is_sales = ref($self) eq 'SL::DB::Invoice';
-
-  my $skonto_date;
-
-  if ( $is_sales ) {
-    return undef unless ref $self->payment_terms;
-    return undef unless $self->payment_terms->terms_skonto > 0;
-    $skonto_date = DateTime->from_object(object => $self->transdate)->add(days => $self->payment_terms->terms_skonto);
-  } else {
-    return undef unless ref $self->vendor->payment_terms;
-    return undef unless $self->vendor->payment_terms->terms_skonto > 0;
-    $skonto_date = DateTime->from_object(object => $self->transdate)->add(days => $self->vendor->payment_terms->terms_skonto);
-  };
-
-  return $skonto_date;
+  return undef unless ref $self->payment_terms;
+  return undef unless $self->payment_terms->terms_skonto > 0;
+  return DateTime->from_object(object => $self->transdate)->add(days => $self->payment_terms->terms_skonto);
 };
 
 sub reference_account {
@@ -442,25 +498,19 @@ sub remaining_skonto_days {
 sub percent_skonto {
   my $self = shift;
 
-  my $is_sales = ref($self) eq 'SL::DB::Invoice';
-
   my $percent_skonto = 0;
 
-  if ( $is_sales ) {
-    return undef unless ref $self->payment_terms;
-    return undef unless $self->payment_terms->percent_skonto > 0;
-    $percent_skonto = $self->payment_terms->percent_skonto;
-  } else {
-    return undef unless ref $self->vendor->payment_terms;
-    return undef unless $self->vendor->payment_terms->terms_skonto > 0;
-    $percent_skonto = $self->vendor->payment_terms->percent_skonto;
-  };
+  return undef unless ref $self->payment_terms;
+  return undef unless $self->payment_terms->percent_skonto > 0;
+  $percent_skonto = $self->payment_terms->percent_skonto;
 
   return $percent_skonto;
 };
 
 sub amount_less_skonto {
   # amount that has to be paid if skonto applies, always return positive rounded values
+  # no, rare case, but credit_notes and negative ap have negative amounts
+  # and therefore this comment may be misguiding
   # the result is rounded so we can directly compare it with the user input
   my $self = shift;
 
@@ -481,6 +531,12 @@ sub check_skonto_configuration {
   foreach my $transaction (@{ $self->transactions }) {
     # find all transactions with an AR_amount or AP_amount link
     my $tax = SL::DB::Manager::Tax->get_first( where => [taxkey => $transaction->taxkey, id => $transaction->tax_id ]);
+
+    # acc_trans entries for the taxes (chart_link == A[RP]_tax) often
+    # have combinations of taxkey & tax_id that don't exist in
+    # tax. Those must be skipped.
+    next if !$tax && ($transaction->chart_link !~ m{A[RP]_amount});
+
     croak "no tax for taxkey " . $transaction->{taxkey} unless ref $tax;
 
     $transaction->{chartlinks} = { map { $_ => 1 } split(m/:/, $transaction->chart_link) };
@@ -516,111 +572,125 @@ sub open_sepa_transfer_amount {
 
   return $open_sepa_amount || 0;
 
-};
-
-
-sub skonto_charts {
-  my $self = shift;
-
-  # TODO: use param for amount, may also want to calculate skonto_amounts by
-  # passing percentage in the future
-
-  my $amount = shift || $self->skonto_amount;
-
-  croak "no amount passed to skonto_charts" unless abs(_round($amount)) >= 0.01;
-
-  # TODO: check whether there are negative values in invoice / acc_trans ... credited items
-
-  # don't check whether skonto applies, because user may want to override this
-  # return undef unless $self->percent_skonto;  # for is_sales
-  # return undef unless $self->vendor->payment_terms->percent_skonto;  # for purchase
-
-  my $is_sales = ref($self) eq 'SL::DB::Invoice';
-
-  my $mult = $is_sales ? 1 : -1;  # multiplier for getting the right sign
-
-  my @skonto_charts;  # resulting array with all income/expense accounts that have to be corrected
-
-  # calculate effective skonto (percentage) in difference_as_skonto mode
-  # only works if there are no negative acc_trans values
-  my $effective_skonto_rate = $amount ? $amount / $self->amount : 0;
-
-  # checks:
-  my $total_skonto_amount  = 0;
-  my $total_rounding_error = 0;
-
-  my $reference_ARAP_amount = 0;
-
-  # my $transactions = $self->transactions;
-  foreach my $transaction (@{ $self->transactions }) {
-    # find all transactions with an AR_amount or AP_amount link
-    $transaction->{chartlinks} = { map { $_ => 1 } split(m/:/, $transaction->{chart_link}) };
-    # second condition is that we can determine an automatic Skonto account for each AR_amount entry
-
-    if ( ( $is_sales && $transaction->{chartlinks}->{AR_amount} ) or ( !$is_sales && $transaction->{chartlinks}->{AP_amount}) ) {
-        # $reference_ARAP_amount += $transaction->{amount} * $mult;
-
-        # quick hack that works around problem of non-unique tax keys in SKR04
-        # ? use tax_id in acc_trans
-        my $tax = SL::DB::Manager::Tax->get_first( where => [id => $transaction->{tax_id}]);
-        croak "no tax for taxkey " . $transaction->{taxkey} unless ref $tax;
+}
 
-        if ( $is_sales ) {
-          die t8('no skonto_chart configured for taxkey #1 : #2 : #3', $transaction->{taxkey} , $tax->taxdescription , $tax->rate*100) unless ref $tax->skonto_sales_chart;
-        } else {
-          die t8('no skonto_chart configured for taxkey #1 : #2 : #3', $transaction->{taxkey} , $tax->taxdescription , $tax->rate*100) unless ref $tax->skonto_purchase_chart;
-        };
+sub _skonto_charts_and_tax_correction {
+  my ($self, %params)   = @_;
+  my $amount = $params{amount} || $self->skonto_amount;
 
-        my $skonto_amount_unrounded;
+  croak "no amount passed to skonto_charts"                    unless abs(_round($amount)) >= 0.01;
+  croak "no banktransaction.id passed to skonto_charts"        unless $params{bt_id};
+  croak "no banktransaction.transdate passed to skonto_charts" unless ref $params{transdate_obj} eq 'DateTime';
 
-        my $skonto_percent_abs = $self->amount ? abs($transaction->amount * (1 + $tax->rate) * 100 / $self->amount) : 0;
+  my $is_sales = $self->is_sales;
+  my (@skonto_charts, $inv_calc, $total_skonto_rounded);
 
-        my $transaction_amount = abs($transaction->{amount} * (1 + $tax->rate));
-        my $transaction_skonto_percent = abs($transaction_amount/$self->amount); # abs($transaction->{amount} * (1 + $tax->rate));
+  $inv_calc = $self->get_tax_and_amount_by_tax_chart_id();
 
+  # foreach tax.chart_id || $entry->{ta..id}
+  while (my ($tax_chart_id, $entry) = each %{ $inv_calc } ) {
+    my $tax = SL::DB::Manager::Tax->find_by(id => $entry->{tax_id}) || die "Can't find tax with id " . $tax_chart_id;
+    die t8('no skonto_chart configured for taxkey #1 : #2 : #3', $tax->taxkey, $tax->taxdescription , $tax->rate * 100)
+      unless $is_sales ? ref $tax->skonto_sales_chart : ref $tax->skonto_purchase_chart;
 
-        $skonto_amount_unrounded   = abs($amount * $transaction_skonto_percent);
-        my $skonto_amount_rounded  = _round($skonto_amount_unrounded);
-        my $rounding_error         = $skonto_amount_unrounded - $skonto_amount_rounded;
-        my $rounded_rounding_error = _round($rounding_error);
+    # percent net amount
+    my $transaction_net_skonto_percent = abs($entry->{netamount} / $self->amount);
+    my $skonto_netamount_unrounded     = abs($amount * $transaction_net_skonto_percent);
 
-        $total_rounding_error += $rounding_error;
-        $total_skonto_amount  += $skonto_amount_rounded;
+    # percent tax amount
+    my $transaction_tax_skonto_percent = abs($entry->{tax} / $self->amount);
+    my $skonto_taxamount_unrounded     = abs($amount * $transaction_tax_skonto_percent);
 
-        my $rec = {
-          # skonto_percent_abs: relative part of amount + tax to the total invoice amount
-          'skonto_percent_abs'     => $skonto_percent_abs,
-          'chart_id'               => $is_sales ? $tax->skonto_sales_chart->id : $tax->skonto_purchase_chart->id,
-          'skonto_amount'          => $skonto_amount_rounded,
-          # 'rounding_error'         => $rounding_error,
-          # 'rounded_rounding_error' => $rounded_rounding_error,
-        };
+    my $skonto_taxamount_rounded   = _round($skonto_taxamount_unrounded);
+    my $skonto_netamount_rounded   = _round($skonto_netamount_unrounded);
+    my $chart_id                   = $is_sales ? $tax->skonto_sales_chart->id : $tax->skonto_purchase_chart->id;
 
-        push @skonto_charts, $rec;
-      };
-  };
-
-  # if the rounded sum of all rounding_errors reaches 0.01 this sum is
-  # subtracted from the largest skonto_amount
-  my $rounded_total_rounding_error = abs(_round($total_rounding_error));
-
-  if ( $rounded_total_rounding_error > 0 ) {
-    my $highest_amount_pos = 0;
-    my $highest_amount = 0;
-    my $i = -1;
-    foreach my $ref ( @skonto_charts ) {
-      $i++;
-      if ( $ref->{skonto_amount} > $highest_amount ) {
-        $highest_amount     = $ref->{skonto_amount};
-        $highest_amount_pos = $i;
-      };
+    # entry net + tax for caller
+    my $rec_net = {
+      chart_id               => $chart_id,
+      skonto_amount          => _round($skonto_netamount_unrounded + $skonto_taxamount_unrounded),
     };
-    $skonto_charts[$i]->{skonto_amount} -= $rounded_total_rounding_error;
-  };
+    push @skonto_charts, $rec_net;
+    $total_skonto_rounded += $rec_net->{skonto_amount};
+
+    # add-on: correct tax with one linked gl booking
+
+    # no skonto tax correction for dual tax (reverse charge) or rate = 0
+    next if ($tax->rate == 0 || $tax->reverse_charge_chart_id);
+
+    my ($credit, $debit);
+    $credit = SL::DB::Manager::Chart->find_by(id => $chart_id);
+    $debit  = SL::DB::Manager::Chart->find_by(id => $tax_chart_id);
+    croak("No such Chart ID")  unless ref $credit eq 'SL::DB::Chart' && ref $debit eq 'SL::DB::Chart';
+
+    my $current_transaction = SL::DB::GLTransaction->new(
+         employee_id    => $self->employee_id,
+         transdate      => $params{transdate_obj},
+         notes          => $params{source} . ' ' . $params{memo},
+         description    => $self->notes || $self->invnumber,
+         reference      => t8('Skonto Tax Correction for') . " " . $tax->rate * 100 . '% ' . $self->invnumber,
+         department_id  => $self->department_id ? $self->department_id : undef,
+         imported       => 0, # not imported
+         taxincluded    => 0,
+      )->add_chart_booking(
+         chart  => $is_sales ? $debit : $credit,
+         debit  => abs($skonto_taxamount_rounded),
+         source => t8('Skonto Tax Correction for') . " " . $self->invnumber,
+         memo   => $params{memo},
+         tax_id => 0,
+      )->add_chart_booking(
+         chart  => $is_sales ? $credit : $debit,
+         credit => abs($skonto_taxamount_rounded),
+         source => t8('Skonto Tax Correction for') . " " . $self->invnumber,
+         memo   => $params{memo},
+         tax_id => 0,
+      )->post;
+
+    # add a stable link acc_trans_id to bank_transactions.id
+    foreach my $transaction (@{ $current_transaction->transactions }) {
+      my %props_acc = (
+           acc_trans_id        => $transaction->acc_trans_id,
+           bank_transaction_id => $params{bt_id},
+           gl                  => $current_transaction->id,
+      );
+      SL::DB::BankTransactionAccTrans->new(%props_acc)->save;
+    }
+    # Record a record link from banktransactions to gl
+    my %props_rl = (
+         from_table => 'bank_transactions',
+         from_id    => $params{bt_id},
+         to_table   => 'gl',
+         to_id      => $current_transaction->id,
+    );
+    SL::DB::RecordLink->new(%props_rl)->save;
+    # Record a record link from arap to gl
+    # linked gl booking will appear in tab linked records
+    # this is just a link for convenience
+    %props_rl = (
+         from_table => $is_sales ? 'ar' : 'ap',
+         from_id    => $self->id,
+         to_table   => 'gl',
+         to_id      => $current_transaction->id,
+    );
+    SL::DB::RecordLink->new(%props_rl)->save;
 
-  return @skonto_charts;
-};
+  }
+  # check for rounding errors, at least for the payment chart
+  # we ignore tax rounding errors as long as the amount (user input or calculated)
+  # is fully assigned.
+  # we simply alter one cent for the first skonto booking entry
+  # should be correct for most of the cases (no invoices with mixed taxes)
+  if ($total_skonto_rounded - $amount > 0.01) {
+    # add one cent
+    $skonto_charts[0]->{skonto_amount} -= 0.01;
+  } elsif ($amount - $total_skonto_rounded > 0.01) {
+    # subtract one cent
+    $skonto_charts[0]->{skonto_amount} += 0.01;
+  }
 
+  # return same array of skonto charts as sub skonto_charts
+  return @skonto_charts;
+}
 
 sub within_skonto_period {
   my $self = shift;
@@ -650,24 +720,26 @@ sub get_payment_select_options_for_bank_transaction {
 
 
   # CAVEAT template code expects with_skonto_pt at position 1 for visual help
+  # due to skonto_charts, we cannot offer skonto for credit notes and neg ap
+  my $skontoable = $self->amount > 0 ? 1 : 0;
   my @options;
   if(!$self->skonto_date) {
     push(@options, { payment_type => 'without_skonto', display => t8('without skonto'), selected => 1 });
     # wrong call to presenter or not implemented? disabled option is ignored
     # push(@options, { payment_type => 'with_skonto_pt', display => t8('with skonto acc. to pt'), disabled => 1 });
-    push(@options, { payment_type => 'free_skonto', display => t8('free skonto') });
+    push(@options, { payment_type => 'free_skonto', display => t8('free skonto') }) if $skontoable;
     return @options;
   }
   # valid skonto date, check if skonto is preferred
   my $bt = SL::DB::BankTransaction->new(id => $bt_id)->load;
   if ($self->skonto_date && $self->within_skonto_period($bt->transdate)) {
     push(@options, { payment_type => 'without_skonto', display => t8('without skonto') });
-    push(@options, { payment_type => 'with_skonto_pt', display => t8('with skonto acc. to pt'), selected => 1 });
+    push(@options, { payment_type => 'with_skonto_pt', display => t8('with skonto acc. to pt'), selected => 1 }) if $skontoable;
   } else {
     push(@options, { payment_type => 'without_skonto', display => t8('without skonto') , selected => 1 });
-    push(@options, { payment_type => 'with_skonto_pt', display => t8('with skonto acc. to pt')});
+    push(@options, { payment_type => 'with_skonto_pt', display => t8('with skonto acc. to pt')}) if $skontoable;
   }
-  push(@options, { payment_type => 'free_skonto', display => t8('free skonto') });
+  push(@options, { payment_type => 'free_skonto', display => t8('free skonto') }) if $skontoable;
   return @options;
 }
 
@@ -704,6 +776,8 @@ sub get_payment_suggestions {
       push(@{$self->{payment_select_options}} , { payment_type => 'with_skonto_pt',  display => t8('with skonto acc. to pt') , selected => 1 });
     } else {
       if ( ( $self->valid_skonto_amount($self->open_amount) || $self->valid_skonto_amount($open_amount) ) and not $params{sepa} ) {
+        # Will never be reached
+        die "This case is as dead as the dead cat. Go to start, don't pick 2,000 \$";
         $self->{invoice_amount_suggestion} = $open_amount;
         # only suggest difference_as_skonto if open_amount exactly matches skonto_amount
         # AND we aren't in SEPA mode
@@ -783,12 +857,16 @@ This function deals with all the acc_trans entries and also updates paid and dat
 The params C<transdate> and C<chart_id> are mandantory.
 If the default payment ('without_skonto') is used the param amount is also
 mandantory.
-If the payment type ('free_skonto') is used the number param skonto_amount
-is as well mandantory and has to be lower than the currently open invoice amount.
+If the payment type ('free_skonto') is used the number params skonto_amount and amount
+are as well mandantory and need to be positive. Furthermore the skonto amount has
+to be lower than the payment or open invoice amount.
 
 Transdate can either be a date object or a date string.
 Chart_id is the id of the payment booking chart.
-Amount is either a postive or negative number, but never 0.
+Amount is either a positive or negative number, but never 0.
+
+CAVEAT! The helper tries to get the sign right and all calls from BankTransaction are
+positive (abs($value)) values.
 
 
 Example:
@@ -881,6 +959,7 @@ invoice is assumed to be the payment currency.
 
 If successful the return value will be 1 in scalar context or in list context
 the two ids (acc_trans_id) of the newly created bookings.
+
 =item C<reference_account>
 
 Returns a chart object which is the chart of the invoice with link AR or AP.
@@ -893,12 +972,11 @@ Example (1200 is the AR account for SKR04):
 =item C<percent_skonto>
 
 Returns the configured skonto percentage of the payment terms of an invoice,
-e.g. 0.02 for 2%. Payment terms come from invoice settings for ar, from vendor
-settings for ap.
+e.g. 0.02 for 2%. Payment terms come from invoice settingssettings for ap.
 
 =item C<amount_less_skonto>
 
-If the invoice has a payment term (via ar for sales, via vendor for purchase),
+If the invoice has a payment term,
 calculate the amount to be paid in the case of skonto.  This doesn't check,
 whether skonto applies (i.e. skonto doesn't wasn't exceeded), it just subtracts
 the configured percentage (e.g. 2%) from the total amount.
@@ -1131,6 +1209,12 @@ Returns 1 if record uses a different currency, 0 if the default currency is used
 when looking at open amount, maybe consider that there may already be queued
 amounts in SEPA Export
 
+=item * C<skonto_charts>
+
+Cannot handle negative skonto amounts, will always calculate the skonto amount
+for credit notes or negative ap transactions with a positive sign.
+
+
 =back
 
 =head1 AUTHOR