--- /dev/null
+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;