]> wagnertech.de Git - mfinanz.git/blobdiff - SL/DB/Helper/Payment.pm
Rechnungsadresse aus den Stammdaten bei periodischen Rechnungen verwenden (Anzeige...
[mfinanz.git] / SL / DB / Helper / Payment.pm
index 660794428aaac513e3445c050d692b55945550bf..3d180f0511bbfd48c92b1a327d23fdb61734d5be 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 
 use parent qw(Exporter);
 our @EXPORT = qw(pay_invoice);
 
 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 create_bank_transaction exchangerate);
+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_TAGS = (
   "ALL" => [@EXPORT, @EXPORT_OK],
 );
 our %EXPORT_TAGS = (
   "ALL" => [@EXPORT, @EXPORT_OK],
 );
@@ -94,7 +94,7 @@ sub pay_invoice {
   if ( $params{'payment_type'} eq 'difference_as_skonto' ) {
     croak "amount $params{amount} doesn't match open amount " . $self->open_amount . ", diff = " . ($params{amount}-$self->open_amount) if $params{amount} && abs($self->open_amount - $params{amount} ) > 0.0000001;
   } elsif ( $params{'payment_type'} eq 'with_skonto_pt' ) {
   if ( $params{'payment_type'} eq 'difference_as_skonto' ) {
     croak "amount $params{amount} doesn't match open amount " . $self->open_amount . ", diff = " . ($params{amount}-$self->open_amount) if $params{amount} && abs($self->open_amount - $params{amount} ) > 0.0000001;
   } elsif ( $params{'payment_type'} eq 'with_skonto_pt' ) {
-    croak "amount $params{amount} doesn't match amount less skonto: " . $self->open_amount . "\n" if $params{amount} && abs($self->amount_less_skonto - $params{amount} ) > 0.0000001;
+    croak "amount $params{amount} doesn't match amount less skonto: " . $self->amount_less_skonto . "\n" if $params{amount} && abs($self->amount_less_skonto - $params{amount} ) > 0.0000001;
     croak "payment type with_skonto_pt can't be used if payments have already been made" if $self->paid != 0;
   };
 
     croak "payment type with_skonto_pt can't be used if payments have already been made" if $self->paid != 0;
   };
 
@@ -107,19 +107,19 @@ sub pay_invoice {
 
   # account where money is paid to/from: bank account or cash
   my $account_bank = SL::DB::Manager::Chart->find_by(id => $params{chart_id});
 
   # account where money is paid to/from: bank account or cash
   my $account_bank = SL::DB::Manager::Chart->find_by(id => $params{chart_id});
-  croak "can't find bank account" unless ref $account_bank;
+  croak "can't find bank account with id " . $params{chart_id} unless ref $account_bank;
 
   my $reference_account = $self->reference_account;
   croak "can't find reference account (link = AR/AP) for invoice" unless ref $reference_account;
 
 
   my $reference_account = $self->reference_account;
   croak "can't find reference account (link = AR/AP) for invoice" unless ref $reference_account;
 
-  my $memo   = $params{'memo'}   || '';
-  my $source = $params{'source'} || '';
+  my $memo   = $params{memo}   // '';
+  my $source = $params{source} // '';
 
   my $rounded_params_amount = _round( $params{amount} ); # / $exchangerate);
   my $fx_gain_loss_amount = 0; # for fx_gain and fx_loss
 
   my $db = $self->db;
 
   my $rounded_params_amount = _round( $params{amount} ); # / $exchangerate);
   my $fx_gain_loss_amount = 0; # for fx_gain and fx_loss
 
   my $db = $self->db;
-  $db->do_transaction(sub {
+  $db->with_transaction(sub {
     my $new_acc_trans;
 
     # all three payment type create 1 AR/AP booking (the paid part)
     my $new_acc_trans;
 
     # all three payment type create 1 AR/AP booking (the paid part)
@@ -278,36 +278,35 @@ sub pay_invoice {
     # than adding them to the transaction relation array.
     $self->forget_related('transactions');
 
     # than adding them to the transaction relation array.
     $self->forget_related('transactions');
 
-  my $datev_check = 0;
-  if ( $is_sales )  {
-    if ( (  $self->invoice && $::instance_conf->get_datev_check_on_sales_invoice  ) ||
-         ( !$self->invoice && $::instance_conf->get_datev_check_on_ar_transaction )) {
-      $datev_check = 1;
-    };
-  } else {
-    if ( (  $self->invoice && $::instance_conf->get_datev_check_on_purchase_invoice ) ||
-         ( !$self->invoice && $::instance_conf->get_datev_check_on_ap_transaction   )) {
-      $datev_check = 1;
-    };
-  };
+    my $datev_check = 0;
+    if ( $is_sales )  {
+      if ( (  $self->invoice && $::instance_conf->get_datev_check_on_sales_invoice  ) ||
+           ( !$self->invoice && $::instance_conf->get_datev_check_on_ar_transaction )) {
+        $datev_check = 1;
+      }
+    } else {
+      if ( (  $self->invoice && $::instance_conf->get_datev_check_on_purchase_invoice ) ||
+           ( !$self->invoice && $::instance_conf->get_datev_check_on_ap_transaction   )) {
+        $datev_check = 1;
+      }
+    }
 
 
-  if ( $datev_check ) {
+    if ( $datev_check ) {
 
 
-    my $datev = SL::DATEV->new(
-      exporttype => DATEV_ET_BUCHUNGEN,
-      format     => DATEV_FORMAT_KNE,
-      dbh        => $db->dbh,
-      trans_id   => $self->{id},
-    );
+      my $datev = SL::DATEV->new(
+        dbh        => $db->dbh,
+        trans_id   => $self->{id},
+      );
 
 
-    $datev->clean_temporary_directories;
-    $datev->export;
+      $datev->generate_datev_data;
 
 
-    if ($datev->errors) {
-      # this exception should be caught by do_transaction, which handles the rollback
-      die join "\n", $::locale->text('DATEV check returned errors:'), $datev->errors;
+      if ($datev->errors) {
+        # this exception should be caught by with_transaction, which handles the rollback
+        die join "\n", $::locale->text('DATEV check returned errors:'), $datev->errors;
+      }
     }
     }
-  };
+
+    1;
 
   }) || die t8('error while paying invoice #1 : ', $self->invnumber) . $db->error . "\n";
 
 
   }) || die t8('error while paying invoice #1 : ', $self->invnumber) . $db->error . "\n";
 
@@ -385,7 +384,7 @@ sub open_amount {
   # if the difference is 0.01 Cent this may end up as 0.009999999999998
   # numerically, so round this value when checking for cent threshold >= 0.01
 
   # if the difference is 0.01 Cent this may end up as 0.009999999999998
   # numerically, so round this value when checking for cent threshold >= 0.01
 
-  return $self->amount - $self->paid;
+  return ($self->amount // 0) - ($self->paid // 0);
 };
 
 sub open_percent {
 };
 
 sub open_percent {
@@ -462,7 +461,7 @@ sub check_skonto_configuration {
   # my $transactions = $self->transactions;
   foreach my $transaction (@{ $self->transactions }) {
     # find all transactions with an AR_amount or AP_amount link
   # my $transactions = $self->transactions;
   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]);
+    my $tax = SL::DB::Manager::Tax->get_first( where => [taxkey => $transaction->taxkey, id => $transaction->tax_id ]);
     croak "no tax for taxkey " . $transaction->{taxkey} unless ref $tax;
 
     $transaction->{chartlinks} = { map { $_ => 1 } split(m/:/, $transaction->chart_link) };
     croak "no tax for taxkey " . $transaction->{taxkey} unless ref $tax;
 
     $transaction->{chartlinks} = { map { $_ => 1 } split(m/:/, $transaction->chart_link) };
@@ -543,7 +542,8 @@ sub skonto_charts {
         # $reference_ARAP_amount += $transaction->{amount} * $mult;
 
         # quick hack that works around problem of non-unique tax keys in SKR04
         # $reference_ARAP_amount += $transaction->{amount} * $mult;
 
         # quick hack that works around problem of non-unique tax keys in SKR04
-        my $tax = SL::DB::Manager::Tax->get_first( where => [taxkey => $transaction->{taxkey}]);
+        # ? 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 ) {
         croak "no tax for taxkey " . $transaction->{taxkey} unless ref $tax;
 
         if ( $is_sales ) {
@@ -629,28 +629,22 @@ sub valid_skonto_amount {
 sub get_payment_select_options_for_bank_transaction {
   my ($self, $bt_id, %params) = @_;
 
 sub get_payment_select_options_for_bank_transaction {
   my ($self, $bt_id, %params) = @_;
 
-  my $bt = SL::DB::Manager::BankTransaction->find_by( id => $bt_id );
-  die unless $bt;
+  # no skonto date  -> no select option
+  return { payment_type => 'without_skonto', display => t8('without skonto') , selected => 1 } unless $self->skonto_date;
 
 
-  my $open_amount = $self->open_amount;
+  my $bt = SL::DB::BankTransaction->new(id => $bt_id)->load;
 
   my @options;
 
   my @options;
-  if ( $open_amount &&                   # invoice amount not 0
-       $self->skonto_date &&             # check whether skonto applies
-       abs(abs($self->amount_less_skonto) - abs($bt->amount)) < 0.01 &&
-       $self->check_skonto_configuration) {
-         if ( $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 });
-         } 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')});
-         };
-  };
 
 
+  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 });
+  } 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')});
+  }
   return @options;
   return @options;
-
-};
+}
 
 sub exchangerate {
   my ($self) = @_;
 
 sub exchangerate {
   my ($self) = @_;
@@ -704,6 +698,13 @@ sub get_payment_suggestions {
   return 1;
 };
 
   return 1;
 };
 
+# locales for payment type
+#
+# $main::locale->text('without_skonto')
+# $main::locale->text('with_skonto_pt')
+# $main::locale->text('difference_as_skonto')
+#
+
 sub validate_payment_type {
   my $payment_type = shift;
 
 sub validate_payment_type {
   my $payment_type = shift;
 
@@ -713,41 +714,11 @@ sub validate_payment_type {
   return 1;
 }
 
   return 1;
 }
 
-sub create_bank_transaction {
-  my ($self, %params) = @_;
-
-  require SL::DB::Chart;
-  require SL::DB::BankAccount;
-
-  my $bank_chart;
-  if ( $params{chart_id} ) {
-    $bank_chart = SL::DB::Manager::Chart->find_by(chart_id => $params{chart_id}) or die "Can't find bank chart";
-  } elsif ( $::instance_conf->get_ar_paid_accno_id ) {
-    $bank_chart   = SL::DB::Manager::Chart->find_by(id => $::instance_conf->get_ar_paid_accno_id);
-  } else {
-    $bank_chart = SL::DB::Manager::Chart->find_by(description => 'Bank') or die "Can't find bank chart";
-  };
-  my $bank_account = SL::DB::Manager::BankAccount->find_by(chart_id => $bank_chart->id) or die "Can't find bank account for chart";
-
-  my $multiplier = $self->is_sales ? 1 : -1;
-  my $amount = ($params{amount} || $self->amount) * $multiplier;
-
-  my $transdate = $params{transdate} || DateTime->today;
-
-  my $bt = SL::DB::BankTransaction->new(
-    local_bank_account_id => $bank_account->id,
-    remote_bank_code      => $self->customervendor->bank_code,
-    remote_account_number => $self->customervendor->account_number,
-    transdate             => $transdate,
-    valutadate            => $transdate,
-    amount                => $::form->round_amount($amount, 2),
-    currency              => $self->currency->id,
-    remote_name           => $self->customervendor->depositor,
-    purpose               => $self->invnumber
-  )->save;
+sub forex {
+  my ($self) = @_;
+  $self->currency_id == $::instance_conf->get_currency_id ? return 0 : return 1;
 };
 
 };
 
-
 sub _round {
   my $value = shift;
   my $num_dec = 2;
 sub _round {
   my $value = shift;
   my $num_dec = 2;
@@ -814,7 +785,7 @@ or in a certain currency:
                    transdate     => DateTime->now->to_kivitendo,
                    memo          => 'foobar',
                    source        => 'barfoo',
                    transdate     => DateTime->now->to_kivitendo,
                    memo          => 'foobar',
                    source        => 'barfoo',
-                   payment_type  => 'with_skonto',
+                   payment_type  => 'with_skonto_pt',
                   );
 
 Allowed payment types are:
                   );
 
 Allowed payment types are:
@@ -1087,25 +1058,18 @@ might always want to pay as late as possible.
 Make suggestion for a skonto payment type by returning an HTML blob of the options
 of a HTML drop-down select with the most likely option preselected.
 
 Make suggestion for a skonto payment type by returning an HTML blob of the options
 of a HTML drop-down select with the most likely option preselected.
 
-This is a helper function for BankTransaction/ajax_payment_suggestion.
+This is a helper function for BankTransaction/ajax_payment_suggestion and
+template/webpages/bank_transactions/invoices.html
 
 We are working with an existing payment, so difference_as_skonto never makes sense.
 
 
 We are working with an existing payment, so difference_as_skonto never makes sense.
 
+If skonto is not possible (skonto_date does not exists) simply return
+the single 'no skonto' option as a visual hint.
+
 If skonto is possible (skonto_date exists), add two possibilities:
 without_skonto and with_skonto_pt if payment date is within skonto_date,
 preselect with_skonto_pt, otherwise preselect without skonto.
 
 If skonto is possible (skonto_date exists), add two possibilities:
 without_skonto and with_skonto_pt if payment date is within skonto_date,
 preselect with_skonto_pt, otherwise preselect without skonto.
 
-=item C<create_bank_transaction %params>
-
-Method used for testing purposes, allows you to quickly create bank
-transactions from invoices to have something to test payments against.
-
- my $ap = SL::DB::Manager::Invoice->find_by(id => 41);
- $ap->create_bank_transaction(amount => $ap->amount/2, transdate => DateTime->today->add(days => 5));
-
-Amount is always relative to the absolute amount of the invoice, use positive
-values for sales and purchases.
-
 =item C<exchangerate>
 
 Returns 1 immediately if the record uses the default currency.
 =item C<exchangerate>
 
 Returns 1 immediately if the record uses the default currency.
@@ -1115,6 +1079,10 @@ invoice's transdate, returning 'buy' for sales, 'sell' for purchases.
 
 If no exchangerate can be found for that day undef is returned.
 
 
 If no exchangerate can be found for that day undef is returned.
 
+=item C<forex>
+
+Returns 1 if record uses a different currency, 0 if the default currency is used.
+
 =back
 
 =head1 TODO AND CAVEATS
 =back
 
 =head1 TODO AND CAVEATS