commit 247a26dc4b5b0d73c03fc6f05fb17daace0835d9
Author: Moritz Bunkus <m.bunkus@linet.de>
Date:   Tue Jul 8 10:48:30 2008 +0000

    Beim Umwandeln von Aufträgen in Rechnungen nicht sofort den Auftrag schließen. Beim Buchen von Rechnungen die Aufträge schließen, aus denen die Rechnung erzeugt wurde (auch mit Umweg über Lieferscheine), sofern der Auftrag damit vollständig abgerechnet wurde.

diff --git a/SL/ARAP.pm b/SL/ARAP.pm
new file mode 100644
index 000000000..299c32f16
--- /dev/null
+++ b/SL/ARAP.pm
@@ -0,0 +1,168 @@
+package ARAP;
+
+use SL::AM;
+use SL::Common;
+use SL::DBUtils;
+use SL::MoreCommon;
+use Data::Dumper;
+
+sub close_orders_if_billed {
+  $main::lxdebug->enter_sub();
+
+  my $self   = shift;
+  my %params = @_;
+
+  Common::check_params(\%params, qw(arap_id table));
+
+  my $myconfig  = \%main::myconfig;
+  my $form      = $main::form;
+
+  my $dbh       = $params{dbh} || $form->get_standard_dbh($myconfig);
+
+  # First, find all order IDs from which this invoice has been
+  # created. Either directly by a conversion from an order to this invoice
+  # or indirectly from an order to one or more delivery orders and
+  # from those to this invoice.
+
+  # Direct conversion "order -> invoice":
+  my @links     = RecordLinks->get_links('dbh'        => $dbh,
+                                         'from_table' => 'oe',
+                                         'to_table'   => $params{table},
+                                         'to_id'      => $params{arap_id});
+
+  my %oe_id_map = map { $_->{from_id} => 1 } @links;
+
+  # Indirect conversion "order -> delivery orders -> invoice":
+  my @do_links  = RecordLinks->get_links('dbh'        => $dbh,
+                                         'from_table' => 'delivery_orders',
+                                         'to_table'   => $params{table},
+                                         'to_id'      => $params{arap_id});
+
+  foreach my $do_link (@do_links) {
+    @links      = RecordLinks->get_links('dbh'        => $dbh,
+                                         'from_table' => 'oe',
+                                         'to_table'   => 'delivery_orders',
+                                         'to_id'      => $do_link->{from_id});
+
+    map { $oe_id_map{$_->{from_id}} = 1 } @links;
+  }
+
+  my @oe_ids = keys %oe_id_map;
+
+#   $main::lxdebug->dump(0, "oe_ids", \@oe_ids);
+
+  # No orders found? Nothing to do then, so let's return.
+  return $main::lxdebug->leave_sub() if (!scalar @oe_ids);
+
+  my $all_units = AM->retrieve_all_units();
+
+  my $qtyfactor = $params{table} eq 'ap' ? '* -1' : '';
+  my $q_billed  = qq|SELECT i.parts_id, i.qty ${qtyfactor} AS qty, i.unit, p.unit AS partunit
+                     FROM invoice i
+                     LEFT JOIN parts p ON (i.parts_id = p.id)
+                     WHERE i.trans_id = ?|;
+  my $h_billed  = prepare_query($form, $dbh, $q_billed);
+
+  my $q_ordered = qq|SELECT oi.parts_id, oi.qty, oi.unit, p.unit AS partunit
+                      FROM orderitems oi
+                      LEFT JOIN parts p ON (oi.parts_id = p.id)
+                      WHERE oi.trans_id = ?|;
+  my $h_ordered = prepare_query($form, $dbh, $q_ordered);
+
+  my @close_oe_ids;
+
+  # Interate over each order and look up all invoices created for
+  # said order. Again consider both direct conversions and indirect
+  # conversions via delivery orders.
+  foreach my $oe_id (@oe_ids) {
+    # Direct conversions "order -> invoice":
+    @links          = RecordLinks->get_links('dbh'        => $dbh,
+                                             'from_table' => 'oe',
+                                             'from_id'    => $oe_id,
+                                             'to_table'   => $params{table},);
+
+    my %arap_id_map = map { $_->{to_id} => 1 } @links;
+
+    # Indirect conversions "order -> delivery orders -> invoice":
+    @do_links       = RecordLinks->get_links('dbh'        => $dbh,
+                                             'from_table' => 'oe',
+                                             'from_id'    => $oe_id,
+                                             'to_table'   => 'delivery_orders',);
+    foreach my $do_link (@do_links) {
+      @links        = RecordLinks->get_links('dbh'        => $dbh,
+                                             'from_table' => 'delivery_orders',
+                                             'from_id'    => $do_link->{to_id},
+                                             'to_table'   => $params{table},);
+
+      map { $arap_id_map{$_->{to_id}} = 1 } @links;
+    }
+
+    my @arap_ids = keys %arap_id_map;
+
+#     $main::lxdebug->dump(0, "for $oe_id arap_ids", \@arap_ids);
+
+    next if (!scalar @arap_ids);
+
+    # Retrieve all positions for this order. Calculate the ordered quantity for each position.
+    my %ordered = ();
+
+    do_statement($form, $h_ordered, $q_ordered, $oe_id);
+
+    while (my $ref = $h_ordered->fetchrow_hashref()) {
+      $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
+
+      if ($ordered{$ref->{parts_id}}) {
+        $ordered{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
+      } else {
+        $ordered{$ref->{parts_id}}             = $ref;
+      }
+    }
+
+    # Retrieve all positions for all invoices that have been created from this order.
+    my %billed  = ();
+
+    foreach my $arap_id (@arap_ids) {
+      do_statement($form, $h_billed, $q_billed, $arap_id);
+
+      while (my $ref = $h_billed->fetchrow_hashref()) {
+        $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
+
+        if ($billed{$ref->{parts_id}}) {
+          $billed{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
+        } else {
+          $billed{$ref->{parts_id}}             = $ref;
+        }
+      }
+    }
+
+    # Check all ordered positions. If all positions have been billed completely then this order can be closed.
+    my $all_billed = 1;
+    foreach my $part (values %ordered) {
+      if (!$billed{$part->{parts_id}} || ($billed{$part->{parts_id}}->{baseqty} < $part->{baseqty})) {
+        $all_billed = 0;
+        last;
+      }
+    }
+
+#     $main::lxdebug->message(0, "all_billed $all_billed");
+#     $main::lxdebug->dump(0, "ordered", \%ordered);
+#     $main::lxdebug->dump(0, "billed", \%billed);
+
+    push @close_oe_ids, $oe_id if ($all_billed);
+  }
+
+  $h_billed->finish();
+  $h_ordered->finish();
+
+  # Close orders that have been billed fully.
+  if (scalar @close_oe_ids) {
+    my $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_oe_ids) . qq|)|;
+    do_query($form, $dbh, $query, @close_oe_ids);
+
+    $dbh->commit() unless ($params{dbh});
+  }
+
+  $main::lxdebug->leave_sub();
+}
+
+1;
diff --git a/SL/IR.pm b/SL/IR.pm
index f1c9197ee..936cc98fe 100644
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -35,6 +35,7 @@
 package IR;
 
 use SL::AM;
+use SL::ARAP;
 use SL::Common;
 use SL::DBUtils;
 use SL::DO;
@@ -562,6 +563,11 @@ sub post_invoice {
                               'to_id'      => $form->{id},
       );
   }
+  delete $form->{convert_from_do_ids};
+
+  ARAP->close_orders_if_billed('dbh'     => $dbh,
+                               'arap_id' => $form->{id},
+                               'table'   => 'ap',);
 
   my $rc = 1;
   if (!$provided_dbh) {
diff --git a/SL/IS.pm b/SL/IS.pm
index c4e2a7fbd..bf52d5223 100644
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -37,6 +37,7 @@ package IS;
 use List::Util qw(max);
 
 use SL::AM;
+use SL::ARAP;
 use SL::CVar;
 use SL::Common;
 use SL::DBUtils;
@@ -994,6 +995,11 @@ sub post_invoice {
                               'to_id'      => $form->{id},
       );
   }
+  delete $form->{convert_from_do_ids};
+
+  ARAP->close_orders_if_billed('dbh'     => $dbh,
+                               'arap_id' => $form->{id},
+                               'table'   => 'ar',);
 
   my $rc = 1;
   if (!$provided_dbh) {
diff --git a/bin/mozilla/oe.pl b/bin/mozilla/oe.pl
index 322706ac1..44690ddb6 100644
--- a/bin/mozilla/oe.pl
+++ b/bin/mozilla/oe.pl
@@ -1446,18 +1446,6 @@ sub invoice {
     }
   }
 
-  # close orders/quotations
-  $form->{closed} = 1;
-
-  # save order if one ordnumber has been given
-  # if not it's most likely a collective order, which can't be saved back
-  # so they just have to be closed
-  if (($form->{ordnumber} ne '') || ($form->{quonumber} ne '')) {
-    OE->close_order(\%myconfig, \%$form) if ($form->{id});
-  } else {
-    OE->close_orders(\%myconfig, \%$form);
-  }
-
   $form->{convert_from_oe_ids} = $form->{id};
   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
