]> wagnertech.de Git - mfinanz.git/blobdiff - SL/IR.pm
kivitendo 3.9.2-0.2
[mfinanz.git] / SL / IR.pm
index 3d8ee363728f89e4d90f1b8906e01b6093c3c2cc..f27f6696a56c30877a315e3d9835e42963bc98dd 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -40,16 +40,19 @@ use SL::ARAP;
 use SL::Common;
 use SL::CVar;
 use SL::DATEV qw(:CONSTANTS);
+use SL::Util qw(trim);
 use SL::DBUtils;
 use SL::DB::Draft;
 use SL::DO;
 use SL::GenericTranslations;
 use SL::HTML::Restrict;
 use SL::IO;
+use SL::Locale::String qw(t8);
 use SL::MoreCommon;
 use SL::DB::Default;
 use SL::DB::TaxZone;
 use SL::DB::MakeModel;
+use SL::DB::ValidityToken;
 use SL::DB;
 use SL::Presenter::Part qw(type_abbreviation classification_abbreviation);
 use List::Util qw(min);
@@ -72,6 +75,16 @@ sub post_invoice {
 sub _post_invoice {
   my ($self, $myconfig, $form, $provided_dbh, %params) = @_;
 
+  my $validity_token;
+  if (!$form->{id}) {
+    $validity_token = SL::DB::Manager::ValidityToken->fetch_valid_token(
+      scope => SL::DB::ValidityToken::SCOPE_PURCHASE_INVOICE_POST(),
+      token => $form->{form_validity_token},
+    );
+
+    die $::locale->text('The form is not valid anymore.') if !$validity_token;
+  }
+
   my $payments_only = $params{payments_only};
   my $dbh = $provided_dbh || SL::DB->client->dbh;
   my $restricter = SL::HTML::Restrict->create;
@@ -84,14 +97,14 @@ sub _post_invoice {
 
   my ($query, $sth, @values, $project_id);
   my ($allocated, $taxrate, $taxamount, $taxdiff, $item);
-  my ($amount, $linetotal, $lastinventoryaccno, $lastexpenseaccno);
+  my ($amount, $linetotal, $last_inventory_accno_tax_id_key, $last_expense_accno_tax_id_key);
   my ($netamount, $invoicediff, $expensediff) = (0, 0, 0);
   my $exchangerate = 0;
   my ($basefactor, $baseqty, @taxaccounts, $totaltax);
 
   my $all_units = AM->retrieve_units($myconfig, $form);
 
-#markierung
+  #markierung
   if (!$payments_only) {
     if ($form->{id}) {
       &reverse_invoice($dbh, $form);
@@ -100,16 +113,25 @@ sub _post_invoice {
       do_query($form, $dbh, qq|INSERT INTO ap (id, invnumber, currency_id, taxzone_id) VALUES (?, '', (SELECT id FROM currencies WHERE name=?), ?)|, $form->{id}, $form->{currency}, $form->{taxzone_id});
     }
   }
-
   if ($form->{currency} eq $defaultcurrency) {
     $form->{exchangerate} = 1;
   } else {
-    $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{invdate}, 'sell');
+    $exchangerate         = $form->check_exchangerate($myconfig, $form->{currency}, $form->{invdate}, 'sell');
+    $form->{exchangerate} = $form->parse_amount($myconfig, $form->{exchangerate}, 5);
+
+    # if default exchangerate is not defined, define one
+    unless ($exchangerate) {
+      $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate}, 0,  $form->{exchangerate});
+      # delete records exchangerate -> if user sets new invdate for record
+      $query = qq|UPDATE ap set exchangerate = NULL where id = ?|;
+      do_query($form, $dbh, $query, $form->{"id"});
+    }
+    # update record exchangerate, if the default is set and differs from current
+    if ($exchangerate && ($form->{exchangerate} != $exchangerate)) {
+      $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
+                                 0, $form->{exchangerate}, $form->{id}, 'ap');
+    }
   }
-
-  $form->{exchangerate} = $exchangerate || $form->parse_amount($myconfig, $form->{exchangerate});
-  $form->{exchangerate} = 1 unless ($form->{exchangerate} * 1);
-
   my %item_units;
   my $q_item_unit = qq|SELECT unit FROM parts WHERE id = ?|;
   my $h_item_unit = prepare_query($form, $dbh, $q_item_unit);
@@ -127,7 +149,10 @@ sub _post_invoice {
     $form->{"qty_$i"}  = $form->parse_amount($myconfig, $form->{"qty_$i"});
     $form->{"qty_$i"} *= -1 if $form->{storno};
 
-    if ( $::instance_conf->get_inventory_system eq 'periodic') {
+    if ( ($form->{"tax_chart_type_$i"} eq 'expense')
+      || ($form->{"tax_chart_type_$i"} eq ''
+          && $::instance_conf->get_inventory_system eq 'periodic')
+    ) {
       # inventory account number is overwritten with expense account number, so
       # never book incoming to inventory account but always to expense account
       $form->{"inventory_accno_$i"} = $form->{"expense_accno_$i"}
@@ -155,6 +180,14 @@ sub _post_invoice {
     $allocated   = 0;
     $taxrate     = 0;
 
+    # preset tax_id and accno for all taxaccounts
+    foreach $item (@taxaccounts) {
+      my $accno  = $item;
+      my $tax_id = $form->{"${item}_tax_id"};
+      $form->{amount}{$item . "_" . $tax_id}{tax_id} = $tax_id;
+      $form->{amount}{$item . "_" . $tax_id}{accno}  = $item;
+    }
+
     $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
     (my $fxsellprice = $form->{"sellprice_$i"}) =~ /\.(\d+)/;
     my $dec = length $1;
@@ -192,17 +225,18 @@ sub _post_invoice {
           foreach $item (@taxaccounts) {
             $taxamount =
               $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
-            $taxdiff                              += $taxamount;
-            $form->{amount}{ $form->{id} }{$item} -= $taxamount;
+            $taxdiff += $taxamount;
+            $form->{amount}{$item . "_" . $form->{"${item}_tax_id"}}{amount} -= $taxamount;
           }
-          $form->{amount}{ $form->{id} }{ $taxaccounts[0] } += $taxdiff;
+          my $first_taxaccno = $taxaccounts[0];
+          $form->{amount}{ $first_taxaccno . "_" . $form->{"${first_taxaccno}_tax_id"} }{amount} += $taxdiff;
 
         } else {
-          map { $form->{amount}{ $form->{id} }{$_} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
+          map { $form->{amount}{$_ . "_" . $form->{"${_}_tax_id"}}{amount} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
         }
 
       } else {
-        map { $form->{amount}{ $form->{id} }{$_} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
+        map { $form->{amount}{$_ . "_" . $form->{"${_}_tax_id"}}{amount} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
       }
 
       # add purchase to inventory, this one is without the tax!
@@ -213,19 +247,26 @@ sub _post_invoice {
       # this is the difference for the inventory
       $invoicediff += ($amount - $linetotal);
 
-      $form->{amount}{ $form->{id} }{ $form->{"inventory_accno_$i"} } -= $linetotal;
+      my $inventory_key = $form->{"inventory_accno_$i"} . "_" . $form->{"expense_accno_tax_id_$i"};
+      $form->{amount}{$inventory_key}{amount} -= $linetotal;
+      $form->{amount}{$inventory_key}{accno}   = $form->{"inventory_accno_$i"};
+      $form->{amount}{$inventory_key}{tax_id}  = $form->{"expense_accno_tax_id_$i"};
 
       # adjust and round sellprice
       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
 
-      $lastinventoryaccno = $form->{"inventory_accno_$i"};
+      $last_inventory_accno_tax_id_key = $inventory_key;
 
       next if $payments_only;
 
-      # update parts table by setting lastcost to current price, don't allow negative values by using abs
-      $query = qq|UPDATE parts SET lastcost = ? WHERE id = ?|;
-      @values = (abs($fxsellprice * $form->{exchangerate} / $basefactor), conv_i($form->{"id_$i"}));
-      do_query($form, $dbh, $query, @values);
+      # change lastcost for part and all assemblies and assortments recursively
+      my $a = SL::DB::Part->load_cached(conv_i($form->{"id_$i"}));
+      my $part_price_factor = $a->price_factor_id ? $a->price_factor->factor : 1;
+      my $new_lastcost      = abs($fxsellprice * $form->{exchangerate} / $basefactor / $price_factor * $part_price_factor);
+      if ( abs($a->lastcost - $new_lastcost) >= 0.009 ) {
+        $a->update_attributes(lastcost => $new_lastcost);
+        $a->set_lastcost_assemblies_and_assortiments;
+      }
 
       # check if we sold the item already and
       # make an entry for the expense and inventory
@@ -352,13 +393,13 @@ sub _post_invoice {
           foreach $item (@taxaccounts) {
             $taxamount = $linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"}));
             $totaltax += $taxamount;
-            $form->{amount}{ $form->{id} }{$item} -= $taxamount;
+            $form->{amount}{$item . "_" . $form->{"${item}_tax_id"}}{amount} -= $taxamount;
           }
         } else {
-          map { $form->{amount}{ $form->{id} }{$_} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
+          map { $form->{amount}{$_ . "_" . $form->{"${_}_tax_id"}}{amount} -= $linetotal * $form->{"${_}_rate"} } @taxaccounts;
         }
       } else {
-        map { $form->{amount}{ $form->{id} }{$_} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
+        map { $form->{amount}{$_ . "_" . $form->{"${_}_tax_id"}}{amount} -= $taxamount * $form->{"${_}_rate"} / $taxrate } @taxaccounts;
       }
 
       $amount    = $form->{"sellprice_$i"} * $form->{"qty_$i"} * $form->{exchangerate} / $price_factor;
@@ -369,18 +410,26 @@ sub _post_invoice {
       $expensediff += ($amount - $linetotal);
 
       # add amount to expense
-      $form->{amount}{ $form->{id} }{ $form->{"expense_accno_$i"} } -= $linetotal;
+      my $expense_key = $form->{"expense_accno_$i"} . "_" . $form->{"expense_accno_tax_id_$i"};
+      $form->{amount}{$expense_key}{amount} -= $linetotal;
+      $form->{amount}{$expense_key}{accno}   = $form->{"expense_accno_$i"};
+      $form->{amount}{$expense_key}{tax_id}  = $form->{"expense_accno_tax_id_$i"};
 
-      $lastexpenseaccno = $form->{"expense_accno_$i"};
+      $last_expense_accno_tax_id_key = $expense_key;
 
       # adjust and round sellprice
       $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
 
       next if $payments_only;
 
-      # update lastcost
-      $query = qq|UPDATE parts SET lastcost = ? WHERE id = ?|;
-      do_query($form, $dbh, $query, $form->{"sellprice_$i"} / $basefactor, conv_i($form->{"id_$i"}));
+      # change lastcost for part and all assemblies and assortments recursively
+      my $a = SL::DB::Part->load_cached(conv_i($form->{"id_$i"}));
+      my $part_price_factor = $a->price_factor_id ? $a->price_factor->factor : 1;
+      my $new_lastcost      = abs($fxsellprice * $form->{exchangerate} / $basefactor / $price_factor * $part_price_factor);
+      if ( abs($a->lastcost - $new_lastcost) >= 0.009 ) {
+        $a->update_attributes(lastcost => $new_lastcost);
+        $a->set_lastcost_assemblies_and_assortiments;
+      }
     }
 
     next if $payments_only;
@@ -410,6 +459,7 @@ sub _post_invoice {
                            project_id = ?, serialnumber = ?, price_factor_id = ?,
                            price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?,
                            active_price_source = ?, active_discount_source = ?
+                           ,expense_chart_id = ?, inventory_chart_id = ?, tax_id = ?, tax_chart_type = ?
         WHERE id = ?
 SQL
 
@@ -417,9 +467,10 @@ SQL
                $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"} * -1,
                $baseqty * -1, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated,
                $form->{"unit_$i"}, conv_date($form->{deliverydate}),
-               conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"},
+               conv_i($form->{"project_id_$i"}), trim($form->{"serialnumber_$i"}),
                conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
                $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
+               $form->{"expense_chart_id_$i"}, $form->{"inventory_chart_id_$i"}, $form->{"tax_id_$i"}, $form->{"tax_chart_type_$i"},
                conv_i($form->{"invoice_id_$i"}));
     do_query($form, $dbh, $query, @values);
     push @processed_invoice_ids, $form->{"invoice_id_$i"};
@@ -477,10 +528,11 @@ SQL
     $netamount = $amount;
 
     foreach $item (split / /, $form->{taxaccounts}) {
-      $amount                               = $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate};
-      $form->{amount}{ $form->{id} }{$item} = $form->round_amount($amount, 2);
+      my $key = $item . "_" . $form->{"${item}_tax_id"};
+      $amount = $form->{amount}{$key}{amount} * $form->{exchangerate};
+      $form->{amount}{$key}{amount} = $form->round_amount($amount, 2);
 
-      $amount     = $form->{amount}{ $form->{id} }{$item} * -1;
+      $amount     = $form->{amount}{$key}{amount} * -1;
       $tax       += $amount;
       $netamount -= $amount;
     }
@@ -493,33 +545,33 @@ SQL
     # in the sales invoice case rounding errors only have to be corrected for
     # income accounts, it is enough to add the total rounding error to one of
     # the income accounts, with the one assigned to the last row being used
-    # (lastinventoryaccno)
+    # (last_inventory_accno_tax_id_key)
 
     # in the purchase invoice case rounding errors may be split between
     # inventory accounts and expense accounts. After rounding, an error of 1
     # cent is introduced if the total rounding error exceeds 0.005. The total
     # error is made up of $invoicediff and $expensediff, however, so if both
     # values are below 0.005, but add up to a total >= 0.005, correcting
-    # lastinventoryaccno and lastexpenseaccno separately has no effect after
+    # last_inventory_accno_tax_id_key and last_expense_accno_tax_id_key separately has no effect after
     # rounding. This caused bug 1579. Therefore when the combined total exceeds
     # 0.005, but neither do individually, the account with the larger value
     # shall receive the total rounding error, and the next time it is rounded
     # the 1 cent correction will be introduced.
 
-    $form->{amount}{ $form->{id} }{$lastinventoryaccno} -= $invoicediff if $lastinventoryaccno;
-    $form->{amount}{ $form->{id} }{$lastexpenseaccno}   -= $expensediff if $lastexpenseaccno;
+    $form->{amount}{$last_inventory_accno_tax_id_key}{amount} -= $invoicediff if $last_inventory_accno_tax_id_key;
+    $form->{amount}{$last_expense_accno_tax_id_key}{amount}   -= $expensediff if $last_expense_accno_tax_id_key;
 
     if ( (abs($expensediff)+abs($invoicediff)) >= 0.005 and abs($expensediff) < 0.005 and abs($invoicediff) < 0.005 ) {
 
       # in total the rounding error adds up to 1 cent effectively, correct the
       # larger of the two numbers
 
-      if ( abs($form->{amount}{ $form->{id} }{$lastinventoryaccno}) > abs($form->{amount}{ $form->{id} }{$lastexpenseaccno}) ) {
+      if ( abs($form->{amount}{$last_inventory_accno_tax_id_key}{amount}) > abs($form->{amount}{$last_expense_accno_tax_id_key}{amount}) ) {
         # $invoicediff has already been deducted, now also deduct expensediff
-        $form->{amount}{ $form->{id} }{$lastinventoryaccno}   -= $expensediff;
+        $form->{amount}{$last_inventory_accno_tax_id_key}{amount} -= $expensediff;
       } else {
         # expensediff has already been deducted, now also deduct invoicediff
-        $form->{amount}{ $form->{id} }{$lastexpenseaccno}   -= $invoicediff;
+        $form->{amount}{$last_expense_accno_tax_id_key}{amount}   -= $invoicediff;
       };
     };
 
@@ -529,57 +581,66 @@ SQL
     $netamount = $amount;
 
     foreach my $item (split / /, $form->{taxaccounts}) {
-      $form->{amount}{ $form->{id} }{$item}  = $form->round_amount($form->{amount}{ $form->{id} }{$item}, 2);
-      $amount                                = $form->round_amount( $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate} * -1, 2);
-      $paiddiff                             += $amount - $form->{amount}{ $form->{id} }{$item} * $form->{exchangerate} * -1;
-      $form->{amount}{ $form->{id} }{$item}  = $form->round_amount($amount * -1, 2);
-      $amount                                = $form->{amount}{ $form->{id} }{$item} * -1;
-      $tax                                  += $amount;
+      my $key = $item . "_" . $form->{"${item}_tax_id"};
+      $form->{amount}{$key}{amount}  = $form->round_amount($form->{amount}{$key}{amount}, 2);
+      $amount                        = $form->round_amount( $form->{amount}{$key}{amount} * $form->{exchangerate} * -1, 2);
+      $paiddiff                     += $amount - $form->{amount}{$key}{amount} * $form->{exchangerate} * -1;
+      $form->{amount}{$key}{amount}  = $form->round_amount($amount * -1, 2);
+      $amount                        = $form->{amount}{$key}{amount} * -1;
+      $tax                          += $amount;
     }
   }
 
-  $form->{amount}{ $form->{id} }{ $form->{AP} } = $netamount + $tax;
-
-
-  $form->{paid} = $form->round_amount($form->{paid} * $form->{exchangerate} + $paiddiff, 2) if $form->{paid} != 0;
-
-# update exchangerate
-
-  $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate}, 0, $form->{exchangerate})
-    if ($form->{currency} ne $defaultcurrency) && !$exchangerate;
-
-# record acc_trans transactions
+  # record acc_trans transactions
   my $taxdate = $form->{tax_point} || $form->{deliverydate} || $form->{invdate};
-  foreach my $trans_id (keys %{ $form->{amount} }) {
-    foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
-      $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
+  foreach my $accno_tax_id_key (keys %{ $form->{amount} }) {
+    $form->{amount}{$accno_tax_id_key}{amount} = $form->round_amount($form->{amount}{$accno_tax_id_key}{amount}, 2);
+
+
+    next if $payments_only || !$form->{amount}{$accno_tax_id_key}{amount};
+    my $amount = $form->{amount}{$accno_tax_id_key}{amount};
+    my $accno  = $form->{amount}{$accno_tax_id_key}{accno};
+    my $tax_id = $form->{amount}{$accno_tax_id_key}{tax_id};
+
+    $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id, tax_id, chart_link)
+                VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
+                (SELECT taxkey
+                 FROM tax
+                 WHERE id = ?),
+                ?,
+                ?,
+                (SELECT link FROM chart WHERE accno = ?))|;
+    @values = (conv_i($form->{id}), $accno, $amount,
+               conv_date($form->{invdate}), $tax_id, $project_id, $tax_id, $accno);
+    do_query($form, $dbh, $query, @values);
+  }
 
+  my $ap_amount = $netamount + $tax;
+  my $ap_accno  = $form->{AP};
+  $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id, tax_id, chart_link)
+              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
+              (SELECT taxkey_id
+               FROM taxkeys
+               WHERE chart_id= (SELECT id
+                                FROM chart
+                                WHERE accno = ?)
+               AND startdate <= ?
+               ORDER BY startdate DESC LIMIT 1),
+              ?,
+              (SELECT tax_id
+               FROM taxkeys
+               WHERE chart_id= (SELECT id
+                                FROM chart
+                                WHERE accno = ?)
+               AND startdate <= ?
+               ORDER BY startdate DESC LIMIT 1),
+              (SELECT link FROM chart WHERE accno = ?))|;
+  @values = (conv_i($form->{id}), $ap_accno, $ap_amount,
+             conv_date($form->{invdate}), $ap_accno, conv_date($taxdate), $project_id, $ap_accno, conv_date($taxdate), $ap_accno);
+  do_query($form, $dbh, $query, @values) unless $payments_only;
 
-      next if $payments_only || !$form->{amount}{$trans_id}{$accno};
 
-      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id, tax_id, chart_link)
-                  VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
-                  (SELECT taxkey_id
-                   FROM taxkeys
-                   WHERE chart_id= (SELECT id
-                                    FROM chart
-                                    WHERE accno = ?)
-                   AND startdate <= ?
-                   ORDER BY startdate DESC LIMIT 1),
-                  ?,
-                  (SELECT tax_id
-                   FROM taxkeys
-                   WHERE chart_id= (SELECT id
-                                    FROM chart
-                                    WHERE accno = ?)
-                   AND startdate <= ?
-                   ORDER BY startdate DESC LIMIT 1),
-                  (SELECT link FROM chart WHERE accno = ?))|;
-      @values = ($trans_id, $accno, $form->{amount}{$trans_id}{$accno},
-                 conv_date($form->{invdate}), $accno, conv_date($taxdate), $project_id, $accno, conv_date($taxdate), $accno);
-      do_query($form, $dbh, $query, @values);
-    }
-  }
+  $form->{paid} = $form->round_amount($form->{paid} * $form->{exchangerate} + $paiddiff, 2) if $form->{paid} != 0;
 
   # deduct payment differences from paiddiff
   for my $i (1 .. $form->{paidaccounts}) {
@@ -591,7 +652,7 @@ SQL
 
   # force AP entry if 0
 
-  $form->{amount}{ $form->{id} }{ $form->{AP} } = $form->{paid} if $form->{amount}{$form->{id}}{$form->{AP}} == 0;
+  $ap_amount = $form->{paid} if $ap_amount == 0;
 
   my %already_cleared = %{ $params{already_cleared} // {} };
 
@@ -619,7 +680,7 @@ SQL
                     :                                                                                'f';
 
     # record AP
-    if ($form->{amount}{ $form->{id} }{ $form->{AP} } != 0) {
+    if ($ap_amount != 0) {
       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id, cleared, tax_id, chart_link)
                   VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
                           (SELECT taxkey_id
@@ -691,7 +752,8 @@ SQL
 
     $paiddiff = 0;
 
-    # update exchange rate
+    # update exchange rate for PAYMENTS
+    $form->{script} = 'ir.pl';
     $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, 0, $form->{"exchangerate_$i"})
       if ($form->{currency} ne $defaultcurrency) && !$exchangerate;
   }
@@ -779,7 +841,8 @@ SQL
   $form->add_shipto($dbh, $form->{id}, "AP");
 
   # delete zero entries
-  do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE amount = 0|);
+  do_query($form, $dbh, qq|DELETE FROM acc_trans WHERE amount = 0 AND trans_id = ?|, $form->{id});
+
 
   Common::webdav_folder($form);
 
@@ -845,6 +908,9 @@ SQL
     }
   }
 
+  $validity_token->delete if $validity_token;
+  delete $form->{form_validity_token};
+
   return 1;
 }
 
@@ -939,12 +1005,6 @@ sub delete_invoice {
 
     my @values = (conv_i($form->{id}));
 
-    # delete zero entries
-    # wtf? use case for this?
-    $query = qq|DELETE FROM acc_trans WHERE amount = 0|;
-    do_query($form, $dbh, $query);
-
-
     my @queries = (
       qq|DELETE FROM invoice WHERE trans_id = ?|,
       qq|DELETE FROM ap WHERE id = ?|,
@@ -1014,7 +1074,7 @@ sub retrieve_invoice {
   $form->{mtime} = $form->{itime} if !$form->{mtime};
   $form->{lastmtime} = $form->{mtime};
 
-  $form->{exchangerate}  = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "sell");
+  ($form->{exchangerate}, $form->{record_forex}) = $form->check_exchangerate($myconfig, $form->{currency}, $form->{invdate}, "sell", conv_i($form->{id}), 'ap');
 
   # get shipto
   $query = qq|SELECT * FROM shipto WHERE (trans_id = ?) AND (module = 'AP')|;
@@ -1039,6 +1099,7 @@ sub retrieve_invoice {
         i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount, i.active_price_source, i.active_discount_source,
         p.partnumber, p.part_type, pr.projectnumber, pg.partsgroup
         ,p.classification_id
+        ,i.expense_chart_id, i.inventory_chart_id, i.tax_id, i.tax_chart_type
 
         FROM invoice i
         JOIN parts p ON (i.parts_id = p.id)
@@ -1072,27 +1133,40 @@ sub retrieve_invoice {
     }
 
     # get tax rates and description
-    my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
-    $query =
-      qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id,
-                c.accno as taxnumber   -- taxnumber is same as accno, but still accessed as taxnumber in code
-         FROM tax t
-         LEFT JOIN chart c ON (c.id = t.chart_id)
-         WHERE t.id in
-           (SELECT tk.tax_id FROM taxkeys tk
-            WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
-              AND (startdate <= $transdate)
-            ORDER BY startdate DESC
-            LIMIT 1)
-         ORDER BY c.accno|;
-    my $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
-    $ref->{taxaccounts} = "";
+    my $get_default_tax = $ref->{tax_id} eq '' ? 1 : 0;
+    my $stw;
+    if ($get_default_tax) {
+      my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
+      my $query =
+        qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id,
+                  c.accno as taxnumber   -- taxnumber is same as accno, but still accessed as taxnumber in code
+           FROM tax t
+           LEFT JOIN chart c ON (c.id = t.chart_id)
+           WHERE t.id in
+             (SELECT tk.tax_id FROM taxkeys tk
+              WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
+                AND (startdate <= $transdate)
+              ORDER BY startdate DESC
+              LIMIT 1)
+           ORDER BY c.accno|;
+      $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
+
+    } else { # use saved tax_id
+      my $query =
+        qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id,
+                  c.accno as taxnumber   -- taxnumber is same as accno, but still accessed as taxnumber in code
+           FROM tax t
+           LEFT JOIN chart c ON (c.id = t.chart_id)
+           WHERE t.id = ?
+           ORDER BY c.accno|;
+
+      $stw = prepare_execute_query($form, $dbh, $query, $ref->{tax_id});
+    }
 
-    my $i = 0;
+    $ref->{taxaccounts} = "";
     while (my $ptr = $stw->fetchrow_hashref("NAME_lc")) {
       if (($ptr->{accno} eq "") && ($ptr->{rate} == 0)) {
-        $i++;
-        $ptr->{accno} = $i;
+        $ptr->{accno} = "NO_ACCNO_" . $ptr->{tax_id};
       }
 
       $ref->{taxaccounts} .= "$ptr->{accno} ";
@@ -1132,7 +1206,7 @@ sub get_vendor {
   $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
 
   my $vid = conv_i($params->{vendor_id});
-  my $vnr = conv_i($params->{vendornumber});
+  my $vnr = $params->{vendornumber};
 
   my $duedate =
     ($params->{invdate})
@@ -1145,8 +1219,7 @@ sub get_vendor {
   if ($vid) {
     $where .= 'AND v.id = ?';
     push @values, $vid;
-  }
-  if ($vnr) {
+  } elsif ($vnr) {
     $where .= 'AND v.vendornumber = ?';
     push @values, $vnr;
   }
@@ -1164,6 +1237,8 @@ sub get_vendor {
        LEFT JOIN currencies cu    ON (v.currency_id = cu.id)
        WHERE 1=1 $where|;
   my $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
+  die t8("Cannot find a single vendor. Maybe there is no vendor yet?") unless $ref;
+
   map { $params->{$_} = $ref->{$_} } keys %$ref;
 
   # use vendor currency
@@ -1181,7 +1256,7 @@ sub get_vendor {
                  WHERE (e.currency_id = o.currency_id)
                    AND (e.transdate = o.transdate)) AS exch
               FROM oe o
-              WHERE (o.vendor_id = ?) AND (o.quotation = '0') AND (o.closed = '0')|;
+              WHERE (o.vendor_id = ?) AND (o.record_type = 'purchase_order') AND (o.closed = '0')|;
   my $sth = prepare_execute_query($form, $dbh, $query, $vid);
   while (my ($amount, $exch) = $sth->fetchrow_array()) {
     $exch = 1 unless $exch;