Copy & Paste-Fehler
[kivitendo-erp.git] / SL / IS.pm
index 205cbcb..78c0652 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
 
 package IS;
 
-use Data::Dumper;
 use SL::AM;
 use SL::Common;
 use SL::DBUtils;
+use SL::MoreCommon;
 
 sub invoice_details {
   $main::lxdebug->enter_sub();
@@ -421,7 +421,7 @@ sub customer_details {
   # get contact id, set it if nessessary
   $form->{cp_id} *= 1;
 
-  my @values;
+  my @values =  (conv_i($form->{customer_id}));
 
   my $where = "";
   if ($form->{cp_id}) {
@@ -438,7 +438,6 @@ sub customer_details {
        WHERE (ct.id = ?) $where
        ORDER BY cp.cp_id
        LIMIT 1|;
-  push(@values, conv_i($form->{customer_id}));
   my $ref = selectfirst_hashref_query($form, $dbh, $query, @values);
 
   # remove id and taxincluded before copy back
@@ -482,16 +481,15 @@ sub customer_details {
 sub post_invoice {
   $main::lxdebug->enter_sub();
 
-  my ($self, $myconfig, $form) = @_;
+  my ($self, $myconfig, $form, $provided_dbh, $payments_only) = @_;
 
   # connect to database, turn off autocommit
-  my $dbh = $form->dbconnect_noauto($myconfig);
+  my $dbh = $provided_dbh ? $provided_dbh : $form->dbconnect_noauto($myconfig);
 
-  my ($query, $sth, $null, $project_id, $deliverydate, @values);
+  my ($query, $sth, $null, $project_id, @values);
   my $exchangerate = 0;
 
-  ($null, $form->{employee_id}) = split(/--/, $form->{employee});
-  unless ($form->{employee_id}) {
+  if (!$form->{employee_id}) {
     $form->get_employee($dbh);
   }
 
@@ -499,28 +497,32 @@ sub post_invoice {
 
   my $all_units = AM->retrieve_units($myconfig, $form);
 
-  if ($form->{id}) {
+  if (!$payments_only) {
+    if ($form->{id}) {
+      &reverse_invoice($dbh, $form);
 
-    &reverse_invoice($dbh, $form);
-
-  } else {
-    $query = qq|SELECT nextval('glid')|;
-    ($form->{"id"}) = selectrow_query($form, $dbh, $query);
+    } else {
+      $query = qq|SELECT nextval('glid')|;
+      ($form->{"id"}) = selectrow_query($form, $dbh, $query);
 
-    $query = qq|INSERT INTO ar (id, invnumber) VALUES (?, ?)|;
-    do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"});
+      $query = qq|INSERT INTO ar (id, invnumber) VALUES (?, ?)|;
+      do_query($form, $dbh, $query, $form->{"id"}, $form->{"id"});
 
-    if (!$form->{invnumber}) {
-      $form->{invnumber} =
-        $form->update_defaults($myconfig, $form->{type} eq "credit_note" ?
-                               "cnnumber" : "invnumber", $dbh);
+      if (!$form->{invnumber}) {
+        $form->{invnumber} =
+          $form->update_defaults($myconfig, $form->{type} eq "credit_note" ?
+                                 "cnnumber" : "invnumber", $dbh);
+      }
     }
   }
 
   my ($netamount, $invoicediff) = (0, 0);
   my ($amount, $linetotal, $lastincomeaccno);
 
-  if ($form->{currency} eq $form->{defaultcurrency}) {
+  my ($currencies)    = selectfirst_array_query($form, $dbh, qq|SELECT curr FROM defaults|);
+  my $defaultcurrency = (split m/:/, $currencies)[0];
+
+  if ($form->{currency} eq $defaultcurrency) {
     $form->{exchangerate} = 1;
   } else {
     $exchangerate =
@@ -640,6 +642,8 @@ sub post_invoice {
         $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate},
                             $decimalplaces);
 
+      next if $payments_only;
+
       if ($form->{"inventory_accno_$i"} || $form->{"assembly_$i"}) {
 
         # adjust parts onhand quantity
@@ -769,7 +773,7 @@ sub post_invoice {
   $form->{amount}{ $form->{id} }{ $form->{AR} } *= -1;
 
   # update exchangerate
-  if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+  if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
     $form->update_exchangerate($dbh, $form->{currency}, $form->{invdate},
                                $form->{exchangerate}, 0);
   }
@@ -779,11 +783,10 @@ sub post_invoice {
   foreach my $trans_id (keys %{ $form->{amount} }) {
     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
       next unless ($form->{expense_inventory} =~ /$accno/);
-      if (
-          ($form->{amount}{$trans_id}{$accno} =
-           $form->round_amount($form->{amount}{$trans_id}{$accno}, 2)
-          ) != 0
-        ) {
+
+      $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
+
+      if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
         $query =
           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
@@ -795,11 +798,9 @@ sub post_invoice {
     }
 
     foreach my $accno (keys %{ $form->{amount}{$trans_id} }) {
-      if (
-          ($form->{amount}{$trans_id}{$accno} =
-           $form->round_amount($form->{amount}{$trans_id}{$accno}, 2)
-          ) != 0
-        ) {
+      $form->{amount}{$trans_id}{$accno} = $form->round_amount($form->{amount}{$trans_id}{$accno}, 2);
+
+      if (!$payments_only && ($form->{amount}{$trans_id}{$accno} != 0)) {
         $query =
           qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, project_id)
              VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?,
@@ -832,7 +833,7 @@ sub post_invoice {
 
       $exchangerate = 0;
 
-      if ($form->{currency} eq $form->{defaultcurrency}) {
+      if ($form->{currency} eq $defaultcurrency) {
         $form->{"exchangerate_$i"} = 1;
       } else {
         $exchangerate =
@@ -886,7 +887,7 @@ sub post_invoice {
       $diff = 0;
 
       # update exchange rate
-      if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+      if (($form->{currency} ne $defaultcurrency) && !$exchangerate) {
         $form->update_exchangerate($dbh, $form->{currency},
                                    $form->{"datepaid_$i"},
                                    $form->{"exchangerate_$i"}, 0);
@@ -894,6 +895,19 @@ sub post_invoice {
     }
   }
 
+  if ($payments_only) {
+    $query = qq|UPDATE ar SET paid = ?, datepaid = ? WHERE id = ?|;
+    do_query($form, $dbh, $query,  $form->{paid}, $form->{paid} ? conv_date($form->{datepaid}) : undef, conv_i($form->{id}));
+
+    if (!$provided_dbh) {
+      $dbh->commit();
+      $dbh->disconnect();
+    }
+
+    $main::lxdebug->leave_sub();
+    return;
+  }
+
   # record exchange rate differences and gains/losses
   foreach my $accno (keys %{ $form->{fx} }) {
     foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
@@ -915,11 +929,6 @@ sub post_invoice {
 
   $amount = $netamount + $tax;
 
-  # set values which could be empty to 0
-  my $datepaid = conv_date($form->{paid});
-  my $duedate  = conv_date($form->{duedate});
-  $deliverydate = conv_date($form->{deliverydate});
-
   # fill in subject if there is none
   $form->{subject} = qq|$form->{label} $form->{invnumber}|
     unless $form->{subject};
@@ -974,7 +983,8 @@ Message: $form->{message}\r| if $form->{message};
                 salesman_id = ?,
                 storno = ?,
                 globalproject_id = ?,
-                cp_id = ?
+                cp_id = ?,
+                transaction_description = ?
               WHERE id = ?|;
   @values = ($form->{"invnumber"}, $form->{"ordnumber"}, $form->{"quonumber"}, $form->{"cusordnumber"},
              conv_date($form->{"invdate"}), conv_date($form->{"orddate"}), conv_date($form->{"quodate"}),
@@ -988,10 +998,18 @@ Message: $form->{message}\r| if $form->{message};
              conv_i($form->{"delivery_customer_id"}), conv_i($form->{"delivery_vendor_id"}),
              conv_i($form->{"employee_id"}), conv_i($form->{"salesman_id"}),
              $form->{"storno"} ? 't' : 'f', conv_i($form->{"globalproject_id"}),
-             conv_i($form->{"cp_id"}),
+             conv_i($form->{"cp_id"}), $form->{transaction_description},
              conv_i($form->{"id"}));
   do_query($form, $dbh, $query, @values);
-
+  
+  if($form->{"formname"} eq "credit_note") {
+    for my $i (1 .. $form->{paidaccounts}) {
+      $query = qq|UPDATE parts SET onhand = onhand - ? WHERE id = ?|;
+      @values = (conv_i($form->{"qty_$i"}), conv_i($form->{"id_$i"}));
+      do_query($form, $dbh, $query, @values);
+    }
+  }
+  
   if ($form->{storno}) {
     $query =
       qq!UPDATE ar SET
@@ -1016,125 +1034,122 @@ Message: $form->{message}\r| if $form->{message};
 
   Common::webdav_folder($form) if ($main::webdav);
 
-  my $rc = $dbh->commit;
-  $dbh->disconnect;
+  my $rc = 1;
+  if (!$provided_dbh) {
+    $dbh->commit();
+    $dbh->disconnect();
+  }
 
   $main::lxdebug->leave_sub();
 
   return $rc;
 }
 
-sub post_payment {
-  $main::lxdebug->enter_sub() and my ($self, $myconfig, $form, $locale) = @_;
+sub _delete_payments {
+  $main::lxdebug->enter_sub();
 
-  # connect to database, turn off autocommit
-  my $dbh = $form->dbconnect_noauto($myconfig);
+  my ($self, $form, $dbh) = @_;
 
-  $form->{datepaid} = $form->{invdate};
+  my @delete_oids;
 
-  # total payments, don't move we need it here
-  for my $i ( 1 .. $form->{paidaccounts} ) {
-    $form->{"paid_$i"}  = $form->parse_amount($myconfig, $form->{"paid_$i"});
-    $form->{"paid_$i"} *= -1                     if $form->{type} eq "credit_note";
-    $form->{"paid"}    += $form->{"paid_$i"};
-    $form->{"datepaid"} = $form->{"datepaid_$i"} if $form->{"datepaid_$i"};
+  # Delete old payment entries from acc_trans.
+  my $query =
+    qq|SELECT oid
+       FROM acc_trans
+       WHERE (trans_id = ?) AND fx_transaction
+
+       UNION
+
+       SELECT at.oid
+       FROM acc_trans at
+       LEFT JOIN chart c ON (at.chart_id = c.id)
+       WHERE (trans_id = ?) AND (c.link LIKE '%AR_paid%')|;
+  push @delete_oids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}), conv_i($form->{id}));
+
+  $query =
+    qq|SELECT at.oid
+       FROM acc_trans at
+       LEFT JOIN chart c ON (at.chart_id = c.id)
+       WHERE (trans_id = ?)
+         AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
+       ORDER BY at.oid
+       OFFSET 1|;
+  push @delete_oids, selectall_array_query($form, $dbh, $query, conv_i($form->{id}));
+
+  if (@delete_oids) {
+    $query = qq|DELETE FROM acc_trans WHERE oid IN (| . join(", ", @delete_oids) . qq|)|;
+    do_query($form, $dbh, $query);
   }
 
-  $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "buy");
+  $main::lxdebug->leave_sub();
+}
 
-  # record payments and offsetting AR
-  for my $i (1 .. $form->{paidaccounts}) {
-    if ($form->{"paid_$i"}) {
+sub post_payment {
+  $main::lxdebug->enter_sub();
 
-      my ($accno) = split /--/, $form->{"AR_paid_$i"};
-      $form->{"datepaid_$i"} = $form->{invdate} unless ($form->{"datepaid_$i"});
-      $form->{datepaid} = $form->{"datepaid_$i"};
+  my ($self, $myconfig, $form, $locale) = @_;
 
-      $exchangerate = 0;
-      if (($form->{currency} eq $form->{defaultcurrency}) || ($form->{defaultcurrency} eq "")) {
-        $form->{"exchangerate_$i"} = 1;
-      } else {
-        $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
-        $form->{"exchangerate_$i"} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
-      }
+  # connect to database, turn off autocommit
+  my $dbh = $form->dbconnect_noauto($myconfig);
 
-      # record AR
-      $amount = $form->round_amount($form->{"paid_$i"} * $form->{"exchangerate"}, 2);
+  my (%payments, $old_form, $row, $item, $query, %keep_vars);
 
-      $query =
-        qq|DELETE FROM acc_trans
-           WHERE (trans_id = ?)
-             AND (chart_id = (SELECT id FROM chart WHERE accno = ?))
-             AND (amount = ?) AND (transdate = ?)|;
-      do_query($form, $dbh, $query, $form->{id}, $form->{AR}, $amount, conv_date($form->{"datepaid_$i"}));
-      $query =
-        qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, project_id, taxkey)
-           VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?,
-                   (SELECT taxkey_id FROM chart WHERE accno = ?))|;
-      do_query($form, $dbh, $query, $form->{id}, $form->{AR}, $amount, $form->{"datepaid_$i"}, conv_i($form->{"globalproject_id"}), $accno);
+  $old_form = save_form();
 
-      # record payment
-      $form->{"paid_$i"} *= -1;
+  # Delete all entries in acc_trans from prior payments.
+  $self->_delete_payments($form, $dbh);
 
-      $query =
-        qq|DELETE FROM acc_trans
-           WHERE (trans_id = ?)
-             AND (chart_id = (SELECT id FROM chart WHERE accno = ?))
-             AND (amount = ?) AND (transdate = ?) AND (source = ?) AND (memo = ?)|;
-      do_query($form, $dbh, $query, $form->{id}, $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"}, $form->{"source_$i"}, $form->{"memo_$i"});
+  # Save the new payments the user made before cleaning up $form.
+  map { $payments{$_} = $form->{$_} } grep m/^datepaid_\d+$|^memo_\d+$|^source_\d+$|^exchangerate_\d+$|^paid_\d+$|^AR_paid_\d+$|^paidaccounts$/, keys %{ $form };
 
-      $query =
-        qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, project_id, taxkey)
-           VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?, ?,
-                   (SELECT taxkey_id FROM chart WHERE accno = ?))|;
-      do_query($form, $dbh, $query, $form->{id}, $accno, $form->{"paid_$i"}, $form->{"datepaid_$i"}, $form->{"source_$i"}, $form->{"memo_$i"},
-               conv_i($form->{"globalproject_id"}), $accno);
+  # Clean up $form so that old content won't tamper the results.
+  %keep_vars = map { $_, 1 } qw(login password id);
+  map { delete $form->{$_} unless $keep_vars{$_} } keys %{ $form };
 
-      # gain/loss
-      $amount = $form->{"paid_$i"} * $form->{exchangerate} - $form->{"paid_$i"} * $form->{"exchangerate_$i"};
-      $form->{fx}{ $form->{($amount > 0 ? 'fxgain_accno' : 'fxloss_accno')} }{ $form->{"datepaid_$i"} } += $amount;
+  # Retrieve the invoice from the database.
+  $self->retrieve_invoice($myconfig, $form);
 
-      $diff = 0;
+  # Set up the content of $form in the way that IS::post_invoice() expects.
+  $form->{exchangerate} = $form->format_amount($myconfig, $form->{exchangerate});
 
-      # update exchange rate
-      if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
-        $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, $form->{"exchangerate_$i"}, 0);
-      }
+  for $row (1 .. scalar @{ $form->{invoice_details} }) {
+    $item = $form->{invoice_details}->[$row - 1];
 
-    }
+    map { $item->{$_} = $form->format_amount($myconfig, $item->{$_}) } qw(qty sellprice discount);
+
+    map { $form->{"${_}_${row}"} = $item->{$_} } keys %{ $item };
   }
 
-  # record exchange rate differences and gains/losses
-  foreach my $accno (keys %{ $form->{fx} }) {
-    foreach my $transdate (keys %{ $form->{fx}{$accno} }) {
+  $form->{rowcount} = scalar @{ $form->{invoice_details} };
 
-      if ($form->{fx}{$accno}{$transdate} = $form->round_amount($form->{fx}{$accno}{$transdate}, 2)) { # '=' is no typo, it's an assignment
-        $query =
-          qq|DELETE FROM acc_trans
-             WHERE (trans_id = ?)
-               AND (chart_id = (SELECT c.id FROM chart c WHERE c.accno = ?))
-               AND (amount = ?) AND (transdate = ?) AND (cleared = ?) AND (fx_transaction = ?)|;
-        do_query($form, $dbh, $query, $form->{id}, $accno, $form->{fx}{$accno}{$transdate}, $transdate, 0, 1);
-        $query =
-          qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, cleared, fx_transaction, project_id, taxkey)
-             VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, ?, ?, ?,
-                     (SELECT taxkey_id FROM chart WHERE accno = ?))|;
-        do_query($form, $dbh, $query, $form->{id}, $accno, $form->{fx}{$accno}{$transdate}, $transdate, 0, 1, conv_i($form->{"globalproject_id"}), $accno);
-      }
+  delete @{$form}{qw(invoice_details paidaccounts storno paid)};
 
-    }
-  }
+  # Restore the payment options from the user input.
+  map { $form->{$_} = $payments{$_} } keys %payments;
 
-  # save AR record
-  delete $form->{datepaid} unless $form->{paid};
+  # Get the AR accno (which is normally done by Form::create_links()).
+  $query =
+    qq|SELECT c.accno
+       FROM acc_trans at
+       LEFT JOIN chart c ON (at.chart_id = c.id)
+       WHERE (trans_id = ?)
+         AND ((c.link = 'AR') OR (c.link LIKE '%:AR') OR (c.link LIKE 'AR:%'))
+       ORDER BY at.oid
+       LIMIT 1|;
 
-  my $query = qq|UPDATE ar SET paid = ?, datepaid = ? WHERE id = ?|;
-  do_query($form, $dbh, $query, $form->{"paid"}, conv_date($form->{"datepaid"}), conv_i($form->{"id"}));
+  ($form->{AR}) = selectfirst_array_query($form, $dbh, $query, conv_i($form->{id}));
 
-  my $rc = $dbh->commit;
-  $dbh->disconnect;
+  # Post the new payments.
+  $self->post_invoice($myconfig, $form, $dbh, 1);
+
+  restore_form($old_form);
+
+  my $rc = $dbh->commit();
+  $dbh->disconnect();
 
-  $main::lxdebug->leave_sub() and return $rc;
+  $main::lxdebug->leave_sub();
+
+  return $rc;
 }
 
 sub process_assembly {
@@ -1369,16 +1384,11 @@ sub retrieve_invoice {
 
   $query =
     qq|SELECT
-         (SELECT c.accno FROM chart c
-          WHERE d.inventory_accno_id = c.id) AS inventory_accno,
-         (SELECT c.accno FROM chart c
-          WHERE d.income_accno_id = c.id) AS income_accno,
-         (SELECT c.accno FROM chart c
-          WHERE d.expense_accno_id = c.id) AS expense_accno,
-         (SELECT c.accno FROM chart c
-          WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
-         (SELECT c.accno FROM chart c
-          WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
+         (SELECT c.accno FROM chart c WHERE d.inventory_accno_id = c.id) AS inventory_accno,
+         (SELECT c.accno FROM chart c WHERE d.income_accno_id = c.id)    AS income_accno,
+         (SELECT c.accno FROM chart c WHERE d.expense_accno_id = c.id)   AS expense_accno,
+         (SELECT c.accno FROM chart c WHERE d.fxgain_accno_id = c.id)    AS fxgain_accno,
+         (SELECT c.accno FROM chart c WHERE d.fxloss_accno_id = c.id)    AS fxloss_accno,
          d.curr AS currencies
          ${query_transdate}
        FROM defaults d|;
@@ -1399,6 +1409,7 @@ sub retrieve_invoice {
            a.duedate, a.taxincluded, a.curr AS currency, a.shipto_id, a.cp_id,
            a.employee_id, a.salesman_id, a.payment_id,
            a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
+           a.transaction_description,
            e.name AS employee
          FROM ar a
          LEFT JOIN employee e ON (e.id = a.employee_id)
@@ -1528,7 +1539,7 @@ sub retrieve_invoice {
              (SELECT tk.tax_id
               FROM taxkeys tk
               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?)
-                AND startdate <= $transdate
+                AND startdate <= date($transdate)
               ORDER BY startdate DESC
               LIMIT 1)
            ORDER BY c.accno|;
@@ -1715,7 +1726,7 @@ sub get_customer {
             qq|SELECT tk.tax_id, t.rate
                FROM taxkeys tk
                LEFT JOIN tax t ON tk.tax_id = t.id
-               WHERE (tk.chart_id = ?) AND (startdate <= ?)
+               WHERE (tk.chart_id = ?) AND (startdate <= date(?))
                ORDER BY tk.startdate DESC
                LIMIT 1|;
           my ($tax_id, $rate) =
@@ -1872,7 +1883,7 @@ sub retrieve_item {
             ORDER BY startdate DESC
             LIMIT 1)
          ORDER BY c.accno|;
-    @values = ($accno_id, $transdate);
+    @values = ($accno_id, $transdate eq "current_date" ? "now" : $transdate);
     $stw = $dbh->prepare($query);
     $stw->execute(@values) || $form->dberror($query);
 
@@ -2135,6 +2146,29 @@ sub has_storno {
 
   my $dbh = $form->dbconnect($myconfig);
 
+  my $query = qq|SELECT storno FROM $table WHERE storno_id = ?|;
+  my ($result) = selectrow_query($form, $dbh, $query, $form->{id});
+
+  $dbh->disconnect();
+
+  $main::lxdebug->leave_sub();
+
+  return $result;
+}
+
+sub is_storno {
+  $main::lxdebug->enter_sub();
+
+  my ($self, $myconfig, $form, $table) = @_;
+
+  $main::lxdebug->leave_sub() and return 0 unless ($form->{id});
+
+  # make sure there's no funny stuff in $table
+  # ToDO: die when this happens and throw an error
+  $main::lxdebug->leave_sub() and return 0 if ($table =~ /\W/);
+
+  my $dbh = $form->dbconnect($myconfig);
+
   my $query = qq|SELECT storno FROM $table WHERE id = ?|;
   my ($result) = selectrow_query($form, $dbh, $query, $form->{id});