Beim Umwandeln von Aufträgen in Rechnungen nicht sofort den Auftrag schließen. Beim...
[kivitendo-erp.git] / SL / ARAP.pm
1 package ARAP;
2
3 use SL::AM;
4 use SL::Common;
5 use SL::DBUtils;
6 use SL::MoreCommon;
7 use Data::Dumper;
8
9 sub close_orders_if_billed {
10   $main::lxdebug->enter_sub();
11
12   my $self   = shift;
13   my %params = @_;
14
15   Common::check_params(\%params, qw(arap_id table));
16
17   my $myconfig  = \%main::myconfig;
18   my $form      = $main::form;
19
20   my $dbh       = $params{dbh} || $form->get_standard_dbh($myconfig);
21
22   # First, find all order IDs from which this invoice has been
23   # created. Either directly by a conversion from an order to this invoice
24   # or indirectly from an order to one or more delivery orders and
25   # from those to this invoice.
26
27   # Direct conversion "order -> invoice":
28   my @links     = RecordLinks->get_links('dbh'        => $dbh,
29                                          'from_table' => 'oe',
30                                          'to_table'   => $params{table},
31                                          'to_id'      => $params{arap_id});
32
33   my %oe_id_map = map { $_->{from_id} => 1 } @links;
34
35   # Indirect conversion "order -> delivery orders -> invoice":
36   my @do_links  = RecordLinks->get_links('dbh'        => $dbh,
37                                          'from_table' => 'delivery_orders',
38                                          'to_table'   => $params{table},
39                                          'to_id'      => $params{arap_id});
40
41   foreach my $do_link (@do_links) {
42     @links      = RecordLinks->get_links('dbh'        => $dbh,
43                                          'from_table' => 'oe',
44                                          'to_table'   => 'delivery_orders',
45                                          'to_id'      => $do_link->{from_id});
46
47     map { $oe_id_map{$_->{from_id}} = 1 } @links;
48   }
49
50   my @oe_ids = keys %oe_id_map;
51
52 #   $main::lxdebug->dump(0, "oe_ids", \@oe_ids);
53
54   # No orders found? Nothing to do then, so let's return.
55   return $main::lxdebug->leave_sub() if (!scalar @oe_ids);
56
57   my $all_units = AM->retrieve_all_units();
58
59   my $qtyfactor = $params{table} eq 'ap' ? '* -1' : '';
60   my $q_billed  = qq|SELECT i.parts_id, i.qty ${qtyfactor} AS qty, i.unit, p.unit AS partunit
61                      FROM invoice i
62                      LEFT JOIN parts p ON (i.parts_id = p.id)
63                      WHERE i.trans_id = ?|;
64   my $h_billed  = prepare_query($form, $dbh, $q_billed);
65
66   my $q_ordered = qq|SELECT oi.parts_id, oi.qty, oi.unit, p.unit AS partunit
67                       FROM orderitems oi
68                       LEFT JOIN parts p ON (oi.parts_id = p.id)
69                       WHERE oi.trans_id = ?|;
70   my $h_ordered = prepare_query($form, $dbh, $q_ordered);
71
72   my @close_oe_ids;
73
74   # Interate over each order and look up all invoices created for
75   # said order. Again consider both direct conversions and indirect
76   # conversions via delivery orders.
77   foreach my $oe_id (@oe_ids) {
78     # Direct conversions "order -> invoice":
79     @links          = RecordLinks->get_links('dbh'        => $dbh,
80                                              'from_table' => 'oe',
81                                              'from_id'    => $oe_id,
82                                              'to_table'   => $params{table},);
83
84     my %arap_id_map = map { $_->{to_id} => 1 } @links;
85
86     # Indirect conversions "order -> delivery orders -> invoice":
87     @do_links       = RecordLinks->get_links('dbh'        => $dbh,
88                                              'from_table' => 'oe',
89                                              'from_id'    => $oe_id,
90                                              'to_table'   => 'delivery_orders',);
91     foreach my $do_link (@do_links) {
92       @links        = RecordLinks->get_links('dbh'        => $dbh,
93                                              'from_table' => 'delivery_orders',
94                                              'from_id'    => $do_link->{to_id},
95                                              'to_table'   => $params{table},);
96
97       map { $arap_id_map{$_->{to_id}} = 1 } @links;
98     }
99
100     my @arap_ids = keys %arap_id_map;
101
102 #     $main::lxdebug->dump(0, "for $oe_id arap_ids", \@arap_ids);
103
104     next if (!scalar @arap_ids);
105
106     # Retrieve all positions for this order. Calculate the ordered quantity for each position.
107     my %ordered = ();
108
109     do_statement($form, $h_ordered, $q_ordered, $oe_id);
110
111     while (my $ref = $h_ordered->fetchrow_hashref()) {
112       $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
113
114       if ($ordered{$ref->{parts_id}}) {
115         $ordered{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
116       } else {
117         $ordered{$ref->{parts_id}}             = $ref;
118       }
119     }
120
121     # Retrieve all positions for all invoices that have been created from this order.
122     my %billed  = ();
123
124     foreach my $arap_id (@arap_ids) {
125       do_statement($form, $h_billed, $q_billed, $arap_id);
126
127       while (my $ref = $h_billed->fetchrow_hashref()) {
128         $ref->{baseqty} = $ref->{qty} * $all_units->{$ref->{unit}}->{factor} / $all_units->{$ref->{partunit}}->{factor};
129
130         if ($billed{$ref->{parts_id}}) {
131           $billed{$ref->{parts_id}}->{baseqty} += $ref->{baseqty};
132         } else {
133           $billed{$ref->{parts_id}}             = $ref;
134         }
135       }
136     }
137
138     # Check all ordered positions. If all positions have been billed completely then this order can be closed.
139     my $all_billed = 1;
140     foreach my $part (values %ordered) {
141       if (!$billed{$part->{parts_id}} || ($billed{$part->{parts_id}}->{baseqty} < $part->{baseqty})) {
142         $all_billed = 0;
143         last;
144       }
145     }
146
147 #     $main::lxdebug->message(0, "all_billed $all_billed");
148 #     $main::lxdebug->dump(0, "ordered", \%ordered);
149 #     $main::lxdebug->dump(0, "billed", \%billed);
150
151     push @close_oe_ids, $oe_id if ($all_billed);
152   }
153
154   $h_billed->finish();
155   $h_ordered->finish();
156
157   # Close orders that have been billed fully.
158   if (scalar @close_oe_ids) {
159     my $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_oe_ids) . qq|)|;
160     do_query($form, $dbh, $query, @close_oe_ids);
161
162     $dbh->commit() unless ($params{dbh});
163   }
164
165   $main::lxdebug->leave_sub();
166 }
167
168 1;