11 sub close_orders_if_billed {
 
  12   $main::lxdebug->enter_sub();
 
  17   Common::check_params(\%params, qw(arap_id table));
 
  19   my $myconfig  = \%main::myconfig;
 
  20   my $form      = $main::form;
 
  22   my $dbh       = $params{dbh} || $form->get_standard_dbh($myconfig);
 
  24   # First, find all order IDs from which this invoice has been
 
  25   # created. Either directly by a conversion from an order to this invoice
 
  26   # or indirectly from an order to one or more delivery orders and
 
  27   # from those to this invoice.
 
  29   # Direct conversion "order -> invoice":
 
  30   my @links     = RecordLinks->get_links('dbh'        => $dbh,
 
  32                                          'to_table'   => $params{table},
 
  33                                          'to_id'      => $params{arap_id});
 
  35   my %oe_id_map = map { $_->{from_id} => 1 } @links;
 
  37   # Indirect conversion "order -> delivery orders -> invoice":
 
  38   my @do_links  = RecordLinks->get_links('dbh'        => $dbh,
 
  39                                          'from_table' => 'delivery_orders',
 
  40                                          'to_table'   => $params{table},
 
  41                                          'to_id'      => $params{arap_id});
 
  43   foreach my $do_link (@do_links) {
 
  44     @links      = RecordLinks->get_links('dbh'        => $dbh,
 
  46                                          'to_table'   => 'delivery_orders',
 
  47                                          'to_id'      => $do_link->{from_id});
 
  49     map { $oe_id_map{$_->{from_id}} = 1 } @links;
 
  52   my @oe_ids = keys %oe_id_map;
 
  54 #   $main::lxdebug->dump(0, "oe_ids", \@oe_ids);
 
  56   # No orders found? Nothing to do then, so let's return.
 
  57   return $main::lxdebug->leave_sub() if (!scalar @oe_ids);
 
  59   my $all_units = AM->retrieve_all_units();
 
  61   my $qtyfactor = $params{table} eq 'ap' ? '* -1' : '';
 
  62   my $q_billed  = qq|SELECT i.parts_id, i.qty ${qtyfactor} AS qty, i.unit, p.unit AS partunit
 
  64                      LEFT JOIN parts p ON (i.parts_id = p.id)
 
  65                      WHERE i.trans_id = ?|;
 
  66   my $h_billed  = prepare_query($form, $dbh, $q_billed);
 
  68   my $q_ordered = qq|SELECT oi.parts_id, oi.qty, oi.unit, p.unit AS partunit
 
  70                       LEFT JOIN parts p ON (oi.parts_id = p.id)
 
  71                       WHERE oi.trans_id = ?|;
 
  72   my $h_ordered = prepare_query($form, $dbh, $q_ordered);
 
  76   # Interate over each order and look up all invoices created for
 
  77   # said order. Again consider both direct conversions and indirect
 
  78   # conversions via delivery orders.
 
  79   foreach my $oe_id (@oe_ids) {
 
  80     # Direct conversions "order -> invoice":
 
  81     @links          = RecordLinks->get_links('dbh'        => $dbh,
 
  84                                              'to_table'   => $params{table},);
 
  86     my %arap_id_map = map { $_->{to_id} => 1 } @links;
 
  88     # Indirect conversions "order -> delivery orders -> invoice":
 
  89     @do_links       = RecordLinks->get_links('dbh'        => $dbh,
 
  92                                              'to_table'   => 'delivery_orders',);
 
  93     foreach my $do_link (@do_links) {
 
  94       @links        = RecordLinks->get_links('dbh'        => $dbh,
 
  95                                              'from_table' => 'delivery_orders',
 
  96                                              'from_id'    => $do_link->{to_id},
 
  97                                              'to_table'   => $params{table},);
 
  99       map { $arap_id_map{$_->{to_id}} = 1 } @links;
 
 102     my @arap_ids = keys %arap_id_map;
 
 104 #     $main::lxdebug->dump(0, "for $oe_id arap_ids", \@arap_ids);
 
 106     next if (!scalar @arap_ids);
 
 108     # Retrieve all positions for this order. Calculate the ordered quantity for each position.
 
 111     do_statement($form, $h_ordered, $q_ordered, $oe_id);
 
 113     while (my $ref = $h_ordered->fetchrow_hashref()) {
 
 114       $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
 
 116       if ($ordered{$ref->{parts_id}}) {
 
 117         $ordered{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
 
 119         $ordered{$ref->{parts_id}}             = $ref;
 
 123     # Retrieve all positions for all invoices that have been created from this order.
 
 126     foreach my $arap_id (@arap_ids) {
 
 127       do_statement($form, $h_billed, $q_billed, $arap_id);
 
 129       while (my $ref = $h_billed->fetchrow_hashref()) {
 
 130         $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
 
 132         if ($billed{$ref->{parts_id}}) {
 
 133           $billed{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
 
 135           $billed{$ref->{parts_id}}             = $ref;
 
 140     # Check all ordered positions. If all positions have been billed completely then this order can be closed.
 
 142     foreach my $part (values %ordered) {
 
 143       if (!$billed{$part->{parts_id}} || ($billed{$part->{parts_id}}->{baseqty} < $part->{baseqty})) {
 
 149 #     $main::lxdebug->message(0, "all_billed $all_billed");
 
 150 #     $main::lxdebug->dump(0, "ordered", \%ordered);
 
 151 #     $main::lxdebug->dump(0, "billed", \%billed);
 
 153     push @close_oe_ids, $oe_id if ($all_billed);
 
 157   $h_ordered->finish();
 
 159   # Close orders that have been billed fully.
 
 160   if (scalar @close_oe_ids) {
 
 161     my $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_oe_ids) . qq|)|;
 
 162     do_query($form, $dbh, $query, @close_oe_ids);
 
 164     $dbh->commit() unless ($params{dbh});
 
 167   $main::lxdebug->leave_sub();