Debugmodi umgeschrieben auf das viel schoenere shiftingformat.
[kivitendo-erp.git] / SL / IS.pm
index 97f6fef..b47944b 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();
@@ -481,45 +481,50 @@ 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);
   }
+  
+  $form->{defaultcurrency} = $form->get_default_currency($myconfig);
 
   ($null, $form->{department_id}) = split(/--/, $form->{department});
 
   my $all_units = AM->retrieve_units($myconfig, $form);
 
-  if ($form->{id}) {
-
-    &reverse_invoice($dbh, $form);
+  if (!$payments_only) {
+    if ($form->{id}) {
+      &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 =
@@ -639,6 +644,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
@@ -768,7 +775,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);
   }
@@ -778,11 +785,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 = ?), ?, ?,
@@ -794,11 +800,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 = ?), ?, ?,
@@ -831,7 +835,7 @@ sub post_invoice {
 
       $exchangerate = 0;
 
-      if ($form->{currency} eq $form->{defaultcurrency}) {
+      if ($form->{currency} eq $defaultcurrency) {
         $form->{"exchangerate_$i"} = 1;
       } else {
         $exchangerate =
@@ -885,7 +889,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);
@@ -893,6 +897,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} }) {
@@ -914,11 +931,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};
@@ -1024,125 +1036,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);
 
-  $main::lxdebug->leave_sub() and return $rc;
+  my $rc = $dbh->commit();
+  $dbh->disconnect();
+
+  $main::lxdebug->leave_sub();
+
+  return $rc;
 }
 
 sub process_assembly {
@@ -1785,7 +1794,7 @@ sub retrieve_item {
   my $query =
     qq|SELECT
          p.id, p.partnumber, p.description, p.sellprice,
-         p.listprice, p.inventory_accno_id,
+         p.listprice, p.inventory_accno_id, p.lastcost,
 
          c1.accno AS inventory_accno,
          c1.new_chart_id AS inventory_new_chart,