Merge von 740 aus unstable: Bugfix 179
[kivitendo-erp.git] / SL / IC.pm
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #  Contributors:
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
30 #
31 # Inventory Control backend
32 #
33 #======================================================================
34
35 package IC;
36 use Data::Dumper;
37 sub get_part {
38   $main::lxdebug->enter_sub();
39
40   my ($self, $myconfig, $form) = @_;
41
42   # connect to db
43   my $dbh = $form->dbconnect($myconfig);
44
45   my $query = qq|SELECT p.*,
46                  c1.accno AS inventory_accno,
47                  c2.accno AS income_accno,
48                  c3.accno AS expense_accno,
49                  pg.partsgroup
50                  FROM parts p
51                  LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
52                  LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
53                  LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
54                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
55                  WHERE p.id = $form->{id}|;
56   my $sth = $dbh->prepare($query);
57   $sth->execute || $form->dberror($query);
58   my $ref = $sth->fetchrow_hashref(NAME_lc);
59
60   # copy to $form variables
61   map { $form->{$_} = $ref->{$_} } (keys %{$ref});
62
63   $sth->finish;
64
65   my %oid = ('Pg'     => 'a.oid',
66              'Oracle' => 'a.rowid');
67
68   # part or service item
69   $form->{item} = ($form->{inventory_accno}) ? 'part' : 'service';
70   if ($form->{assembly}) {
71     $form->{item} = 'assembly';
72
73     # retrieve assembly items
74     $query = qq|SELECT p.id, p.partnumber, p.description,
75                 p.sellprice, p.weight, a.qty, a.bom, p.unit,
76                 pg.partsgroup
77                 FROM parts p
78                 JOIN assembly a ON (a.parts_id = p.id)
79                 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
80                 WHERE a.id = $form->{id}
81                 ORDER BY $oid{$myconfig->{dbdriver}}|;
82
83     $sth = $dbh->prepare($query);
84     $sth->execute || $form->dberror($query);
85
86     $form->{assembly_rows} = 0;
87     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
88       $form->{assembly_rows}++;
89       foreach my $key (keys %{$ref}) {
90         $form->{"${key}_$form->{assembly_rows}"} = $ref->{$key};
91       }
92     }
93     $sth->finish;
94
95   }
96
97   # setup accno hash for <option checked> {amount} is used in create_links
98   $form->{amount}{IC}         = $form->{inventory_accno};
99   $form->{amount}{IC_income}  = $form->{income_accno};
100   $form->{amount}{IC_sale}    = $form->{income_accno};
101   $form->{amount}{IC_expense} = $form->{expense_accno};
102   $form->{amount}{IC_cogs}    = $form->{expense_accno};
103
104   # get prices
105   $query =
106     qq|SELECT p.parts_id, p.pricegroup_id, p.price, (SELECT pg.pricegroup FROM pricegroup pg WHERE pg.id=p.pricegroup_id) AS pricegroup FROM prices p
107               WHERE parts_id = $form->{id}
108               ORDER by pricegroup|;
109
110   $sth = $dbh->prepare($query);
111   $sth->execute || $form->dberror($query);
112
113   @pricegroups          = ();
114   @pricegroups_not_used = ();
115
116   #for pricegroups
117   my $i = 1;
118   while (
119          ($form->{"klass_$i"}, $form->{"pricegroup_id_$i"},
120           $form->{"price_$i"}, $form->{"pricegroup_$i"})
121          = $sth->fetchrow_array
122     ) {
123     $form->{"price_$i"} = $form->round_amount($form->{"price_$i"}, 5);
124     $form->{"price_$i"} =
125       $form->format_amount($myconfig, $form->{"price_$i"}, 5);
126     push @pricegroups, $form->{"pricegroup_id_$i"};
127     $i++;
128   }
129
130   $sth->finish;
131
132   # get pricegroups
133   $query = qq|SELECT p.id, p.pricegroup FROM pricegroup p|;
134
135   $pkq = $dbh->prepare($query);
136   $pkq->execute || $form->dberror($query);
137   while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
138     push @{ $form->{PRICEGROUPS} }, $pkr;
139   }
140   $pkq->finish;
141
142   #find not used pricegroups
143   while ($tmp = pop @{ $form->{PRICEGROUPS} }) {
144     my $insert = 0;
145     foreach $item (@pricegroups) {
146       if ($item eq $tmp->{id}) {
147
148         #drop
149         $insert = 1;
150       }
151     }
152     if ($insert == 0) {
153       push @pricegroups_not_used, $tmp;
154     }
155   }
156
157   # if not used pricegroups are avaible
158   if (@pricegroups_not_used) {
159
160     foreach $name (@pricegroups_not_used) {
161       $form->{"klass_$i"} = "$name->{id}";
162       $form->{"price_$i"} = $form->round_amount($form->{sellprice}, 5);
163       $form->{"price_$i"} =
164         $form->format_amount($myconfig, $form->{"price_$i"}, 5);
165       $form->{"pricegroup_id_$i"} = "$name->{id}";
166       $form->{"pricegroup_$i"}    = "$name->{pricegroup}\n";
167       $i++;
168     }
169   }
170
171   #correct rows
172   $form->{price_rows} = $i - 1;
173
174   unless ($form->{item} eq 'service') {
175
176     # get makes
177     if ($form->{makemodel}) {
178       $query = qq|SELECT m.make, m.model FROM makemodel m
179                   WHERE m.parts_id = $form->{id}|;
180
181       $sth = $dbh->prepare($query);
182       $sth->execute || $form->dberror($query);
183
184       my $i = 1;
185       while (($form->{"make_$i"}, $form->{"model_$i"}) = $sth->fetchrow_array)
186       {
187         $i++;
188       }
189       $sth->finish;
190       $form->{makemodel_rows} = $i - 1;
191
192     }
193   }
194
195   # now get accno for taxes
196   $query = qq|SELECT c.accno
197               FROM chart c, partstax pt
198               WHERE pt.chart_id = c.id
199               AND pt.parts_id = $form->{id}|;
200
201   $sth = $dbh->prepare($query);
202   $sth->execute || $form->dberror($query);
203
204   while (($key) = $sth->fetchrow_array) {
205     $form->{amount}{$key} = $key;
206   }
207
208   $sth->finish;
209
210   # is it an orphan
211   $query = qq|SELECT i.parts_id
212               FROM invoice i
213               WHERE i.parts_id = $form->{id}
214             UNION
215               SELECT o.parts_id
216               FROM orderitems o
217               WHERE o.parts_id = $form->{id}
218             UNION
219               SELECT a.parts_id
220               FROM assembly a
221               WHERE a.parts_id = $form->{id}|;
222   $sth = $dbh->prepare($query);
223   $sth->execute || $form->dberror($query);
224
225   ($form->{orphaned}) = $sth->fetchrow_array;
226   $form->{orphaned} = !$form->{orphaned};
227   $sth->finish;
228
229   $dbh->disconnect;
230
231   $main::lxdebug->leave_sub();
232 }
233
234 sub get_pricegroups {
235   $main::lxdebug->enter_sub();
236
237   my ($self, $myconfig, $form) = @_;
238   my $dbh = $form->dbconnect($myconfig);
239   my $i = 1;
240   my @pricegroups_not_used = ();
241
242   # get pricegroups
243   my $query = qq|SELECT p.id, p.pricegroup FROM pricegroup p|;
244
245   my $pkq = $dbh->prepare($query);
246   $pkq->execute || $form->dberror($query);
247   while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
248     push @{ $form->{PRICEGROUPS} }, $pkr;
249   }
250   $pkq->finish;
251
252   #find not used pricegroups
253   while ($tmp = pop @{ $form->{PRICEGROUPS} }) {
254     push @pricegroups_not_used, $tmp;
255   }
256
257   # if not used pricegroups are avaible
258   if (@pricegroups_not_used) {
259
260     foreach $name (@pricegroups_not_used) {
261       $form->{"klass_$i"} = "$name->{id}";
262       $form->{"price_$i"} = $form->round_amount($form->{sellprice}, 5);
263       $form->{"price_$i"} =
264         $form->format_amount($myconfig, $form->{"price_$i"}, 5);
265       $form->{"pricegroup_id_$i"} = "$name->{id}";
266       $form->{"pricegroup_$i"}    = "$name->{pricegroup}\n";
267       $i++;
268     }
269   }
270
271   #correct rows
272   $form->{price_rows} = $i - 1;
273
274   $dbh->disconnect;
275
276   $main::lxdebug->leave_sub();
277 }
278
279
280 sub save {
281   $main::lxdebug->enter_sub();
282
283   my ($self, $myconfig, $form) = @_;
284
285   if ($form->{eur} && ($form->{item} ne 'service')) {
286     $form->{IC} = $form->{IC_expense};
287   }
288
289   ($form->{inventory_accno}) = split(/--/, $form->{IC});
290   ($form->{expense_accno})   = split(/--/, $form->{IC_expense});
291   ($form->{income_accno})    = split(/--/, $form->{IC_income});
292
293   # connect to database, turn off AutoCommit
294   my $dbh = $form->dbconnect_noauto($myconfig);
295
296   # save the part
297   # make up a unique handle and store in partnumber field
298   # then retrieve the record based on the unique handle to get the id
299   # replace the partnumber field with the actual variable
300   # add records for makemodel
301
302   # if there is a $form->{id} then replace the old entry
303   # delete all makemodel entries and add the new ones
304
305   # escape '
306   map { $form->{$_} =~ s/\'/\'\'/g } qw(partnumber description notes unit);
307
308   # undo amount formatting
309   map { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
310     qw(rop weight listprice sellprice gv lastcost stock);
311
312   # set date to NULL if nothing entered
313   $form->{priceupdate} =
314     ($form->{priceupdate}) ? qq|'$form->{priceupdate}'| : "NULL";
315
316   $form->{makemodel} = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
317
318   $form->{alternate} = 0;
319   $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
320   $form->{obsolete} *= 1;
321   $form->{shop}     *= 1;
322   $form->{onhand}   *= 1;
323   $form->{ve}       *= 1;
324   $form->{ge}       *= 1;
325
326   my ($query, $sth);
327
328   if ($form->{id}) {
329
330     # get old price
331     $query = qq|SELECT p.sellprice, p.weight
332                 FROM parts p
333                 WHERE p.id = $form->{id}|;
334     $sth = $dbh->prepare($query);
335     $sth->execute || $form->dberror($query);
336     my ($sellprice, $weight) = $sth->fetchrow_array;
337     $sth->finish;
338
339     # if item is part of an assembly adjust all assemblies
340     $query = qq|SELECT a.id, a.qty
341                 FROM assembly a
342                 WHERE a.parts_id = $form->{id}|;
343     $sth = $dbh->prepare($query);
344     $sth->execute || $form->dberror($query);
345     while (my ($id, $qty) = $sth->fetchrow_array) {
346       &update_assembly($dbh, $form, $id, $qty, $sellprice * 1, $weight * 1);
347     }
348     $sth->finish;
349
350     if ($form->{item} ne 'service') {
351
352       # delete makemodel records
353       $query = qq|DELETE FROM makemodel
354                   WHERE parts_id = $form->{id}|;
355       $dbh->do($query) || $form->dberror($query);
356     }
357
358     if ($form->{item} eq 'assembly') {
359       if ($form->{onhand} != 0) {
360         &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
361       }
362
363       # delete assembly records
364       $query = qq|DELETE FROM assembly
365                   WHERE id = $form->{id}|;
366       $dbh->do($query) || $form->dberror($query);
367
368       $form->{onhand} += $form->{stock};
369     }
370
371     # delete tax records
372     $query = qq|DELETE FROM partstax
373                 WHERE parts_id = $form->{id}|;
374     $dbh->do($query) || $form->dberror($query);
375
376   } else {
377     my $uid = rand() . time;
378     $uid .= $form->{login};
379
380     $query = qq|SELECT p.id FROM parts p
381                 WHERE p.partnumber = '$form->{partnumber}'|;
382     $sth = $dbh->prepare($query);
383     $sth->execute || $form->dberror($query);
384     ($form->{id}) = $sth->fetchrow_array;
385     $sth->finish;
386
387     if ($form->{id} ne "") {
388       $main::lxdebug->leave_sub();
389       return 3;
390     }
391     $query = qq|INSERT INTO parts (partnumber, description)
392                 VALUES ('$uid', 'dummy')|;
393     $dbh->do($query) || $form->dberror($query);
394
395     $query = qq|SELECT p.id FROM parts p
396                 WHERE p.partnumber = '$uid'|;
397     $sth = $dbh->prepare($query);
398     $sth->execute || $form->dberror($query);
399
400     ($form->{id}) = $sth->fetchrow_array;
401     $sth->finish;
402
403     $form->{orphaned} = 1;
404     $form->{onhand} = $form->{stock} if $form->{item} eq 'assembly';
405     if ($form->{partnumber} eq "" && $form->{inventory_accno} eq "") {
406       $form->{partnumber} = $form->update_defaults($myconfig, "servicenumber");
407     }
408     if ($form->{partnumber} eq "" && $form->{inventory_accno} ne "") {
409       $form->{partnumber} = $form->update_defaults($myconfig, "articlenumber");
410     }
411
412   }
413   my $partsgroup_id = 0;
414
415   if ($form->{partsgroup}) {
416     ($partsgroup, $partsgroup_id) = split /--/, $form->{partsgroup};
417   }
418
419   $query = qq|UPDATE parts SET
420               partnumber = '$form->{partnumber}',
421               description = '$form->{description}',
422               makemodel = '$form->{makemodel}',
423               alternate = '$form->{alternate}',
424               assembly = '$form->{assembly}',
425               listprice = $form->{listprice},
426               sellprice = $form->{sellprice},
427               lastcost = $form->{lastcost},
428               weight = $form->{weight},
429               priceupdate = $form->{priceupdate},
430               unit = '$form->{unit}',
431               notes = '$form->{notes}',
432               rop = $form->{rop},
433               bin = '$form->{bin}',
434               inventory_accno_id = (SELECT c.id FROM chart c
435                                     WHERE c.accno = '$form->{inventory_accno}'),
436               income_accno_id = (SELECT c.id FROM chart c
437                                  WHERE c.accno = '$form->{income_accno}'),
438               expense_accno_id = (SELECT c.id FROM chart c
439                                   WHERE c.accno = '$form->{expense_accno}'),
440               obsolete = '$form->{obsolete}',
441               image = '$form->{image}',
442               drawing = '$form->{drawing}',
443               shop = '$form->{shop}',
444               ve = '$form->{ve}',
445               gv = '$form->{gv}',
446               microfiche = '$form->{microfiche}',
447               partsgroup_id = $partsgroup_id
448               WHERE id = $form->{id}|;
449   $dbh->do($query) || $form->dberror($query);
450
451   # delete price records
452   $query = qq|DELETE FROM prices
453               WHERE parts_id = $form->{id}|;
454   $dbh->do($query) || $form->dberror($query);
455
456   # insert price records only if different to sellprice
457   for my $i (1 .. $form->{price_rows}) {
458     if ($form->{"price_$i"} eq "0") {
459        $form->{"price_$i"} = $form->{sellprice};
460     }
461     if ((   $form->{"price_$i"} 
462         || $form->{"klass_$i"}
463         || $form->{"pricegroup_id_$i"}) and $form->{"price_$i"} != $form->{sellprice}) {
464       $klass = $form->parse_amount($myconfig, $form->{"klass_$i"});
465       $price = $form->parse_amount($myconfig, $form->{"price_$i"});
466       $pricegroup_id =
467         $form->parse_amount($myconfig, $form->{"pricegroup_id_$i"});
468       $query = qq|INSERT INTO prices (parts_id, pricegroup_id, price)
469                   VALUES($form->{id},$pricegroup_id,$price)|;
470       $dbh->do($query) || $form->dberror($query);
471     }
472   }
473
474   # insert makemodel records
475   unless ($form->{item} eq 'service') {
476     for my $i (1 .. $form->{makemodel_rows}) {
477       if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
478         map { $form->{"${_}_$i"} =~ s/\'/\'\'/g } qw(make model);
479
480         $query = qq|INSERT INTO makemodel (parts_id, make, model)
481                     VALUES ($form->{id},
482                     '$form->{"make_$i"}', '$form->{"model_$i"}')|;
483         $dbh->do($query) || $form->dberror($query);
484       }
485     }
486   }
487
488   # insert taxes
489   foreach $item (split / /, $form->{taxaccounts}) {
490     if ($form->{"IC_tax_$item"}) {
491       $query = qq|INSERT INTO partstax (parts_id, chart_id)
492                   VALUES ($form->{id},
493                           (SELECT c.id
494                            FROM chart c
495                            WHERE c.accno = '$item'))|;
496       $dbh->do($query) || $form->dberror($query);
497     }
498   }
499
500   # add assembly records
501   if ($form->{item} eq 'assembly') {
502
503     for my $i (1 .. $form->{assembly_rows}) {
504       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
505
506       if ($form->{"qty_$i"} != 0) {
507         $form->{"bom_$i"} *= 1;
508         $query = qq|INSERT INTO assembly (id, parts_id, qty, bom)
509                     VALUES ($form->{id}, $form->{"id_$i"},
510                     $form->{"qty_$i"}, '$form->{"bom_$i"}')|;
511         $dbh->do($query) || $form->dberror($query);
512       }
513     }
514
515     # adjust onhand for the parts
516     if ($form->{onhand} != 0) {
517       &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
518     }
519
520     @a = localtime;
521     $a[5] += 1900;
522     $a[4]++;
523     my $shippingdate = "$a[5]-$a[4]-$a[3]";
524
525     $form->get_employee($dbh);
526
527     # add inventory record
528     $query = qq|INSERT INTO inventory (warehouse_id, parts_id, qty,
529                 shippingdate, employee_id) VALUES (
530                 0, $form->{id}, $form->{stock}, '$shippingdate',
531                 $form->{employee_id})|;
532     $dbh->do($query) || $form->dberror($query);
533
534   }
535
536   #set expense_accno=inventory_accno if they are different => bilanz
537   $vendor_accno =
538     ($form->{expense_accno} != $form->{inventory_accno})
539     ? $form->{inventory_accno}
540     : $form->{expense_accno};
541
542   # get tax rates and description
543   $accno_id =
544     ($form->{vc} eq "customer") ? $form->{income_accno} : $vendor_accno;
545   $query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
546               FROM chart c, tax t
547               WHERE c.id=t.chart_id AND t.taxkey in (SELECT taxkey_id from chart where accno = '$accno_id')
548               ORDER BY c.accno|;
549   $stw = $dbh->prepare($query);
550
551   $stw->execute || $form->dberror($query);
552
553   $form->{taxaccount} = "";
554   while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
555
556     #    if ($customertax{$ref->{accno}}) {
557     $form->{taxaccount} .= "$ptr->{accno} ";
558     if (!($form->{taxaccount2} =~ /$ptr->{accno}/)) {
559       $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
560       $form->{"$ptr->{accno}_description"} = $ptr->{description};
561       $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
562       $form->{taxaccount2} .= " $ptr->{accno} ";
563     }
564
565   }
566
567   # commit
568   my $rc = $dbh->commit;
569   $dbh->disconnect;
570
571   $main::lxdebug->leave_sub();
572
573   return $rc;
574 }
575
576 sub update_assembly {
577   $main::lxdebug->enter_sub();
578
579   my ($dbh, $form, $id, $qty, $sellprice, $weight) = @_;
580
581   my $query = qq|SELECT a.id, a.qty
582                  FROM assembly a
583                  WHERE a.parts_id = $id|;
584   my $sth = $dbh->prepare($query);
585   $sth->execute || $form->dberror($query);
586
587   while (my ($pid, $aqty) = $sth->fetchrow_array) {
588     &update_assembly($dbh, $form, $pid, $aqty * $qty, $sellprice, $weight);
589   }
590   $sth->finish;
591
592   $query = qq|UPDATE parts
593               SET sellprice = sellprice +
594                   $qty * ($form->{sellprice} - $sellprice),
595                   weight = weight +
596                   $qty * ($form->{weight} - $weight)
597               WHERE id = $id|;
598   $dbh->do($query) || $form->dberror($query);
599
600   $main::lxdebug->leave_sub();
601 }
602
603 sub retrieve_assemblies {
604   $main::lxdebug->enter_sub();
605
606   my ($self, $myconfig, $form) = @_;
607
608   # connect to database
609   my $dbh = $form->dbconnect($myconfig);
610
611   my $where = '1 = 1';
612
613   if ($form->{partnumber}) {
614     my $partnumber = $form->like(lc $form->{partnumber});
615     $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
616   }
617
618   if ($form->{description}) {
619     my $description = $form->like(lc $form->{description});
620     $where .= " AND lower(p.description) LIKE '$description'";
621   }
622   $where .= " AND NOT p.obsolete = '1'";
623
624   # retrieve assembly items
625   my $query = qq|SELECT p.id, p.partnumber, p.description,
626                  p.bin, p.onhand, p.rop,
627                    (SELECT sum(p2.inventory_accno_id)
628                     FROM parts p2, assembly a
629                     WHERE p2.id = a.parts_id
630                     AND a.id = p.id) AS inventory
631                  FROM parts p
632                  WHERE $where
633                  AND assembly = '1'|;
634
635   my $sth = $dbh->prepare($query);
636   $sth->execute || $form->dberror($query);
637
638   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
639     push @{ $form->{assembly_items} }, $ref if $ref->{inventory};
640   }
641   $sth->finish;
642
643   $dbh->disconnect;
644
645   $main::lxdebug->leave_sub();
646 }
647
648 sub restock_assemblies {
649   $main::lxdebug->enter_sub();
650
651   my ($self, $myconfig, $form) = @_;
652
653   # connect to database
654   my $dbh = $form->dbconnect_noauto($myconfig);
655
656   for my $i (1 .. $form->{rowcount}) {
657
658     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
659
660     if ($form->{"qty_$i"} != 0) {
661       &adjust_inventory($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
662     }
663
664   }
665
666   my $rc = $dbh->commit;
667   $dbh->disconnect;
668
669   $main::lxdebug->leave_sub();
670
671   return $rc;
672 }
673
674 sub adjust_inventory {
675   $main::lxdebug->enter_sub();
676
677   my ($dbh, $form, $id, $qty) = @_;
678
679   my $query = qq|SELECT p.id, p.inventory_accno_id, p.assembly, a.qty
680                  FROM parts p, assembly a
681                  WHERE a.parts_id = p.id
682                  AND a.id = $id|;
683   my $sth = $dbh->prepare($query);
684   $sth->execute || $form->dberror($query);
685
686   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
687
688     my $allocate = $qty * $ref->{qty};
689
690     # is it a service item, then loop
691     $ref->{inventory_accno_id} *= 1;
692     next if (($ref->{inventory_accno_id} == 0) && !$ref->{assembly});
693
694     # adjust parts onhand
695     $form->update_balance($dbh, "parts", "onhand",
696                           qq|id = $ref->{id}|,
697                           $allocate * -1);
698   }
699
700   $sth->finish;
701
702   # update assembly
703   my $rc = $form->update_balance($dbh, "parts", "onhand", qq|id = $id|, $qty);
704
705   $main::lxdebug->leave_sub();
706
707   return $rc;
708 }
709
710 sub delete {
711   $main::lxdebug->enter_sub();
712
713   my ($self, $myconfig, $form) = @_;
714
715   # connect to database, turn off AutoCommit
716   my $dbh = $form->dbconnect_noauto($myconfig);
717
718   my $query = qq|DELETE FROM parts
719                  WHERE id = $form->{id}|;
720   $dbh->do($query) || $form->dberror($query);
721
722   $query = qq|DELETE FROM partstax
723               WHERE parts_id = $form->{id}|;
724   $dbh->do($query) || $form->dberror($query);
725
726   # check if it is a part, assembly or service
727   if ($form->{item} ne 'service') {
728     $query = qq|DELETE FROM makemodel
729                 WHERE parts_id = $form->{id}|;
730     $dbh->do($query) || $form->dberror($query);
731   }
732
733   if ($form->{item} eq 'assembly') {
734
735     # delete inventory
736     $query = qq|DELETE FROM inventory
737                 WHERE parts_id = $form->{id}|;
738     $dbh->do($query) || $form->dberror($query);
739
740     $query = qq|DELETE FROM assembly
741                 WHERE id = $form->{id}|;
742     $dbh->do($query) || $form->dberror($query);
743   }
744
745   if ($form->{item} eq 'alternate') {
746     $query = qq|DELETE FROM alternate
747                 WHERE id = $form->{id}|;
748     $dbh->do($query) || $form->dberror($query);
749   }
750
751   # commit
752   my $rc = $dbh->commit;
753   $dbh->disconnect;
754
755   $main::lxdebug->leave_sub();
756
757   return $rc;
758 }
759
760 sub assembly_item {
761   $main::lxdebug->enter_sub();
762
763   my ($self, $myconfig, $form) = @_;
764
765   my $i = $form->{assembly_rows};
766   my $var;
767   my $where = "1 = 1";
768
769   if ($form->{"partnumber_$i"}) {
770     $var = $form->like(lc $form->{"partnumber_$i"});
771     $where .= " AND lower(p.partnumber) LIKE '$var'";
772   }
773   if ($form->{"description_$i"}) {
774     $var = $form->like(lc $form->{"description_$i"});
775     $where .= " AND lower(p.description) LIKE '$var'";
776   }
777   if ($form->{"partsgroup_$i"}) {
778     $var = $form->like(lc $form->{"partsgroup_$i"});
779     $where .= " AND lower(pg.partsgroup) LIKE '$var'";
780   }
781
782   if ($form->{id}) {
783     $where .= " AND NOT p.id = $form->{id}";
784   }
785
786   if ($partnumber) {
787     $where .= " ORDER BY p.partnumber";
788   } else {
789     $where .= " ORDER BY p.description";
790   }
791
792   # connect to database
793   my $dbh = $form->dbconnect($myconfig);
794
795   my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
796                  p.weight, p.onhand, p.unit,
797                  pg.partsgroup
798                  FROM parts p
799                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
800                  WHERE $where|;
801   my $sth = $dbh->prepare($query);
802   $sth->execute || $form->dberror($query);
803
804   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
805     push @{ $form->{item_list} }, $ref;
806   }
807
808   $sth->finish;
809   $dbh->disconnect;
810
811   $main::lxdebug->leave_sub();
812 }
813
814 sub all_parts {
815   $main::lxdebug->enter_sub();
816
817   my ($self, $myconfig, $form) = @_;
818
819   my $where = '1 = 1';
820   my $var;
821
822   my $group;
823   my $limit;
824
825   foreach my $item (qw(partnumber drawing microfiche make model)) {
826     if ($form->{$item}) {
827       $var = $form->like(lc $form->{$item});
828
829       # make will build later Bugfix 145
830       if ($item ne 'make') {
831         $where .= " AND lower(p.$item) LIKE '$var'";
832       }
833     }
834   }
835
836   # special case for description
837   if ($form->{description}) {
838     unless (   $form->{bought}
839             || $form->{sold}
840             || $form->{onorder}
841             || $form->{ordered}
842             || $form->{rfq}
843             || $form->{quoted}) {
844       $var = $form->like(lc $form->{description});
845       $where .= " AND lower(p.description) LIKE '$var'";
846     }
847   }
848
849   # special case for serialnumber
850   if ($form->{l_serialnumber}) {
851     if ($form->{serialnumber}) {
852       $var = $form->like(lc $form->{serialnumber});
853       $where .= " AND lower(serialnumber) LIKE '$var'";
854     }
855   }
856
857   if ($form->{searchitems} eq 'part') {
858     $where .= " AND p.inventory_accno_id > 0";
859   }
860   if ($form->{searchitems} eq 'assembly') {
861     $form->{bought} = "";
862     $where .= " AND p.assembly = '1'";
863   }
864   if ($form->{searchitems} eq 'service') {
865     $where .= " AND p.inventory_accno_id IS NULL AND NOT p.assembly = '1'";
866
867     # irrelevant for services
868     $form->{make} = $form->{model} = "";
869   }
870
871   # items which were never bought, sold or on an order
872   if ($form->{itemstatus} eq 'orphaned') {
873     $form->{onhand}  = $form->{short}   = 0;
874     $form->{bought}  = $form->{sold}    = 0;
875     $form->{onorder} = $form->{ordered} = 0;
876     $form->{rfq}     = $form->{quoted}  = 0;
877
878     $form->{transdatefrom} = $form->{transdateto} = "";
879
880     $where .= " AND p.onhand = 0
881                 AND p.id NOT IN (SELECT p.id FROM parts p, invoice i
882                                  WHERE p.id = i.parts_id)
883                 AND p.id NOT IN (SELECT p.id FROM parts p, assembly a
884                                  WHERE p.id = a.parts_id)
885                 AND p.id NOT IN (SELECT p.id FROM parts p, orderitems o
886                                  WHERE p.id = o.parts_id)";
887   }
888
889   if ($form->{itemstatus} eq 'active') {
890     $where .= " AND p.obsolete = '0'";
891   }
892   if ($form->{itemstatus} eq 'obsolete') {
893     $where .= " AND p.obsolete = '1'";
894     $form->{onhand} = $form->{short} = 0;
895   }
896   if ($form->{itemstatus} eq 'onhand') {
897     $where .= " AND p.onhand > 0";
898   }
899   if ($form->{itemstatus} eq 'short') {
900     $where .= " AND p.onhand < p.rop";
901   }
902   if ($form->{make}) {
903     $var = $form->like(lc $form->{make});
904     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
905                            FROM makemodel m WHERE lower(m.make) LIKE '$var')";
906   }
907   if ($form->{model}) {
908     $var = $form->like(lc $form->{model});
909     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
910                            FROM makemodel m WHERE lower(m.model) LIKE '$var')";
911   }
912   if ($form->{partsgroup}) {
913     $var = $form->like(lc $form->{partsgroup});
914     $where .= " AND lower(pg.partsgroup) LIKE '$var'";
915   }
916   if ($form->{l_soldtotal}) {
917     $where .= " AND p.id=i.parts_id AND  i.qty >= 0";
918     $group =
919       " GROUP BY  p.id,p.partnumber,p.description,p.onhand,p.unit,p.bin, p.sellprice,p.listprice,p.lastcost,p.priceupdate,pg.partsgroup";
920   }
921   if ($form->{top100}) {
922     $limit = " LIMIT 100";
923   }
924
925   # tables revers?
926   if ($form->{revers} == 1) {
927     $form->{desc} = " DESC";
928   } else {
929     $form->{desc} = "";
930   }
931
932   # connect to database
933   my $dbh = $form->dbconnect($myconfig);
934
935   my $sortorder = $form->{sort};
936   $sortorder .= $form->{desc};
937   $sortorder = $form->{sort} if $form->{sort};
938
939   my $query = "";
940
941   if ($form->{l_soldtotal}) {
942     $form->{soldtotal} = 'soldtotal';
943     $query =
944       qq|SELECT p.id,p.partnumber,p.description,p.onhand,p.unit,p.bin,p.sellprice,p.listprice,
945                 p.lastcost,p.priceupdate,pg.partsgroup,sum(i.qty) as soldtotal FROM parts
946                 p LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id), invoice i
947                 WHERE $where
948                 $group
949                 ORDER BY $sortorder
950                 $limit|;
951   } else {
952     $query = qq|SELECT p.id, p.partnumber, p.description, p.onhand, p.unit,
953                  p.bin, p.sellprice, p.listprice, p.lastcost, p.rop, p.weight,
954                  p.priceupdate, p.image, p.drawing, p.microfiche,
955                  pg.partsgroup
956                  FROM parts p
957                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
958                  WHERE $where
959                  $group
960                  ORDER BY $sortorder|;
961   }
962
963   # rebuild query for bought and sold items
964   if (   $form->{bought}
965       || $form->{sold}
966       || $form->{onorder}
967       || $form->{ordered}
968       || $form->{rfq}
969       || $form->{quoted}) {
970
971     my @a = qw(partnumber description bin priceupdate name);
972
973     push @a, qw(invnumber serialnumber) if ($form->{bought} || $form->{sold});
974     push @a, "ordnumber" if ($form->{onorder} || $form->{ordered});
975     push @a, "quonumber" if ($form->{rfq}     || $form->{quoted});
976
977     my $union = "";
978     $query = "";
979
980     if ($form->{bought} || $form->{sold}) {
981
982       my $invwhere = "$where";
983       $invwhere .= " AND i.assemblyitem = '0'";
984       $invwhere .= " AND a.transdate >= '$form->{transdatefrom}'"
985         if $form->{transdatefrom};
986       $invwhere .= " AND a.transdate <= '$form->{transdateto}'"
987         if $form->{transdateto};
988
989       if ($form->{description}) {
990         $var = $form->like(lc $form->{description});
991         $invwhere .= " AND lower(i.description) LIKE '$var'";
992       }
993
994       my $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
995                     i.qty AS onhand, i.unit, p.bin, i.sellprice,
996                     p.listprice, p.lastcost, p.rop, p.weight,
997                     p.priceupdate, p.image, p.drawing, p.microfiche,
998                     pg.partsgroup,
999                     a.invnumber, a.ordnumber, a.quonumber, i.trans_id,
1000                     ct.name|;
1001
1002       if ($form->{bought}) {
1003         $query = qq|
1004                     SELECT $flds, 'ir' AS module, '' AS type,
1005                     1 AS exchangerate
1006                     FROM invoice i
1007                     JOIN parts p ON (p.id = i.parts_id)
1008                     JOIN ap a ON (a.id = i.trans_id)
1009                     JOIN vendor ct ON (a.vendor_id = ct.id)
1010                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1011                     WHERE $invwhere|;
1012         $union = "
1013                   UNION";
1014       }
1015
1016       if ($form->{sold}) {
1017         $query .= qq|$union
1018                      SELECT $flds, 'is' AS module, '' AS type,
1019                      1 As exchangerate
1020                      FROM invoice i
1021                      JOIN parts p ON (p.id = i.parts_id)
1022                      JOIN ar a ON (a.id = i.trans_id)
1023                      JOIN customer ct ON (a.customer_id = ct.id)
1024                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1025                      WHERE $invwhere|;
1026         $union = "
1027                   UNION";
1028       }
1029     }
1030
1031     if ($form->{onorder} || $form->{ordered}) {
1032       my $ordwhere = "$where
1033                      AND o.quotation = '0'";
1034       $ordwhere .= " AND o.transdate >= '$form->{transdatefrom}'"
1035         if $form->{transdatefrom};
1036       $ordwhere .= " AND o.transdate <= '$form->{transdateto}'"
1037         if $form->{transdateto};
1038
1039       if ($form->{description}) {
1040         $var = $form->like(lc $form->{description});
1041         $ordwhere .= " AND lower(oi.description) LIKE '$var'";
1042       }
1043
1044       $flds = qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1045                  oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
1046                  p.listprice, p.lastcost, p.rop, p.weight,
1047                  p.priceupdate, p.image, p.drawing, p.microfiche,
1048                  pg.partsgroup,
1049                  '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1050                  ct.name|;
1051
1052       if ($form->{ordered}) {
1053         $query .= qq|$union
1054                      SELECT $flds, 'oe' AS module, 'sales_order' AS type,
1055                     (SELECT buy FROM exchangerate ex
1056                      WHERE ex.curr = o.curr
1057                      AND ex.transdate = o.transdate) AS exchangerate
1058                      FROM orderitems oi
1059                      JOIN parts p ON (oi.parts_id = p.id)
1060                      JOIN oe o ON (oi.trans_id = o.id)
1061                      JOIN customer ct ON (o.customer_id = ct.id)
1062                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1063                      WHERE $ordwhere
1064                      AND o.customer_id > 0|;
1065         $union = "
1066                   UNION";
1067       }
1068
1069       if ($form->{onorder}) {
1070         $flds = qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1071                    oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
1072                    p.listprice, p.lastcost, p.rop, p.weight,
1073                    p.priceupdate, p.image, p.drawing, p.microfiche,
1074                    pg.partsgroup,
1075                    '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1076                    ct.name|;
1077
1078         $query .= qq|$union
1079                     SELECT $flds, 'oe' AS module, 'purchase_order' AS type,
1080                     (SELECT sell FROM exchangerate ex
1081                      WHERE ex.curr = o.curr
1082                      AND ex.transdate = o.transdate) AS exchangerate
1083                     FROM orderitems oi
1084                     JOIN parts p ON (oi.parts_id = p.id)
1085                     JOIN oe o ON (oi.trans_id = o.id)
1086                     JOIN vendor ct ON (o.vendor_id = ct.id)
1087                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1088                     WHERE $ordwhere
1089                     AND o.vendor_id > 0|;
1090       }
1091
1092     }
1093
1094     if ($form->{rfq} || $form->{quoted}) {
1095       my $quowhere = "$where
1096                      AND o.quotation = '1'";
1097       $quowhere .= " AND o.transdate >= '$form->{transdatefrom}'"
1098         if $form->{transdatefrom};
1099       $quowhere .= " AND o.transdate <= '$form->{transdateto}'"
1100         if $form->{transdateto};
1101
1102       if ($form->{description}) {
1103         $var = $form->like(lc $form->{description});
1104         $quowhere .= " AND lower(oi.description) LIKE '$var'";
1105       }
1106
1107       $flds = qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1108                  oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
1109                  p.listprice, p.lastcost, p.rop, p.weight,
1110                  p.priceupdate, p.image, p.drawing, p.microfiche,
1111                  pg.partsgroup,
1112                  '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1113                  ct.name|;
1114
1115       if ($form->{quoted}) {
1116         $query .= qq|$union
1117                      SELECT $flds, 'oe' AS module, 'sales_quotation' AS type,
1118                     (SELECT buy FROM exchangerate ex
1119                      WHERE ex.curr = o.curr
1120                      AND ex.transdate = o.transdate) AS exchangerate
1121                      FROM orderitems oi
1122                      JOIN parts p ON (oi.parts_id = p.id)
1123                      JOIN oe o ON (oi.trans_id = o.id)
1124                      JOIN customer ct ON (o.customer_id = ct.id)
1125                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1126                      WHERE $quowhere
1127                      AND o.customer_id > 0|;
1128         $union = "
1129                   UNION";
1130       }
1131
1132       if ($form->{rfq}) {
1133         $flds = qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1134                    oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
1135                    p.listprice, p.lastcost, p.rop, p.weight,
1136                    p.priceupdate, p.image, p.drawing, p.microfiche,
1137                    pg.partsgroup,
1138                    '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1139                    ct.name|;
1140
1141         $query .= qq|$union
1142                     SELECT $flds, 'oe' AS module, 'request_quotation' AS type,
1143                     (SELECT sell FROM exchangerate ex
1144                      WHERE ex.curr = o.curr
1145                      AND ex.transdate = o.transdate) AS exchangerate
1146                     FROM orderitems oi
1147                     JOIN parts p ON (oi.parts_id = p.id)
1148                     JOIN oe o ON (oi.trans_id = o.id)
1149                     JOIN vendor ct ON (o.vendor_id = ct.id)
1150                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1151                     WHERE $quowhere
1152                     AND o.vendor_id > 0|;
1153       }
1154
1155     }
1156     $query .= qq|
1157                  ORDER BY $sortorder|;
1158
1159   }
1160   my $sth = $dbh->prepare($query);
1161   $sth->execute || $form->dberror($query);
1162
1163   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1164     push @{ $form->{parts} }, $ref;
1165   }
1166
1167   $sth->finish;
1168
1169   # include individual items for assemblies
1170   if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
1171     foreach $item (@{ $form->{parts} }) {
1172       push @assemblies, $item;
1173       $query = qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
1174                   p.unit, p.bin,
1175                   p.sellprice, p.listprice, p.lastcost,
1176                   p.rop, p.weight, p.priceupdate,
1177                   p.image, p.drawing, p.microfiche
1178                   FROM parts p, assembly a
1179                   WHERE p.id = a.parts_id
1180                   AND a.id = $item->{id}|;
1181
1182       $sth = $dbh->prepare($query);
1183       $sth->execute || $form->dberror($query);
1184
1185       while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1186         $ref->{assemblyitem} = 1;
1187         push @assemblies, $ref;
1188       }
1189       $sth->finish;
1190
1191       push @assemblies, { id => $item->{id} };
1192
1193     }
1194
1195     # copy assemblies to $form->{parts}
1196     @{ $form->{parts} } = @assemblies;
1197   }
1198
1199   $dbh->disconnect;
1200   $main::lxdebug->leave_sub();
1201 }
1202
1203 sub create_links {
1204   $main::lxdebug->enter_sub();
1205
1206   my ($self, $module, $myconfig, $form) = @_;
1207
1208   # connect to database
1209   my $dbh = $form->dbconnect($myconfig);
1210
1211   if ($form->{id}) {
1212     $query = qq|SELECT c.accno, c.description, c.link, c.id,
1213                         p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
1214                         FROM chart c, parts p
1215                         WHERE c.link LIKE '%$module%'
1216                         AND p.id = $form->{id}
1217                         ORDER BY c.accno|;
1218   } else {
1219     $query = qq|SELECT c.accno, c.description, c.link, c.id,
1220                 d.inventory_accno_id, d.income_accno_id, d.expense_accno_id
1221                 FROM chart c, defaults d
1222                 WHERE c.link LIKE '%$module%'
1223                 ORDER BY c.accno|;
1224   }
1225
1226   my $sth = $dbh->prepare($query);
1227   $sth->execute || $form->dberror($query);
1228   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1229     foreach my $key (split /:/, $ref->{link}) {
1230       if ($key =~ /$module/) {
1231         if (   ($ref->{id} eq $ref->{inventory_accno_id})
1232             || ($ref->{id} eq $ref->{income_accno_id})
1233             || ($ref->{id} eq $ref->{expense_accno_id})) {
1234           push @{ $form->{"${module}_links"}{$key} },
1235             { accno       => $ref->{accno},
1236               description => $ref->{description},
1237               selected    => "selected" };
1238             } else {
1239           push @{ $form->{"${module}_links"}{$key} },
1240             { accno       => $ref->{accno},
1241               description => $ref->{description},
1242               selected    => "" };
1243         }
1244       }
1245     }
1246   }
1247   $sth->finish;
1248
1249   if ($form->{id}) {
1250     $query = qq|SELECT weightunit
1251                 FROM defaults|;
1252     $sth = $dbh->prepare($query);
1253     $sth->execute || $form->dberror($query);
1254
1255     ($form->{weightunit}) = $sth->fetchrow_array;
1256     $sth->finish;
1257
1258   } else {
1259     $query = qq|SELECT weightunit, current_date
1260                 FROM defaults|;
1261     $sth = $dbh->prepare($query);
1262     $sth->execute || $form->dberror($query);
1263
1264     ($form->{weightunit}, $form->{priceupdate}) = $sth->fetchrow_array;
1265     $sth->finish;
1266   }
1267
1268   $dbh->disconnect;
1269   $main::lxdebug->leave_sub();
1270 }
1271
1272 # get partnumber, description, unit, sellprice and soldtotal with choice through $sortorder for Top100
1273 sub get_parts {
1274   $main::lxdebug->enter_sub();
1275
1276   my ($self, $myconfig, $form, $sortorder) = @_;
1277   my $dbh   = $form->dbconnect($myconfig);
1278   my $order = " p.partnumber";
1279   my $where = "1 = 1";
1280
1281   if ($sortorder eq "all") {
1282     $where .= " AND p.partnumber LIKE '%$form->{partnumber}%'";
1283     $where .= " AND p.description LIKE '%$form->{description}%'";
1284   } else {
1285     if ($sortorder eq "partnumber") {
1286       $where .= " AND p.partnumber LIKE '%$form->{partnumber}%'";
1287       $order = qq|p.$sortorder|;
1288     }
1289     if ($sortorder eq "description") {
1290       $where .= " AND p.description LIKE '%$form->{description}%'";
1291     }
1292   }
1293
1294   my $query =
1295     qq|SELECT p.id, p.partnumber, p.description, p.unit, p.sellprice FROM parts p WHERE $where ORDER BY $order|;
1296   my $sth = $dbh->prepare($query);
1297   $sth->execute || $self->dberror($query);
1298   my $j = 0;
1299   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1300     if (($ref->{partnumber} eq "*") && ($ref->{description} eq "")) {
1301     } else {
1302       $j++;
1303       $form->{"id_$j"}          = $ref->{id};
1304       $form->{"partnumber_$j"}  = $ref->{partnumber};
1305       $form->{"description_$j"} = $ref->{description};
1306       $form->{"unit_$j"}        = $ref->{unit};
1307       $form->{"sellprice_$j"}   = $ref->{sellprice};
1308       $form->{"soldtotal_$j"}   = get_soldtotal($dbh, $ref->{id});
1309     }    #fi
1310   }    #while
1311   $form->{rows} = $j;
1312   $sth->finish;
1313   $dbh->disconnect;
1314
1315   $main::lxdebug->leave_sub();
1316
1317   return $self;
1318 }    #end get_parts()
1319
1320 # gets sum of sold part with part_id
1321 sub get_soldtotal {
1322   $main::lxdebug->enter_sub();
1323
1324   my ($dbh, $id) = @_;
1325
1326   my $query =
1327     qq|SELECT sum(i.qty) as totalsold FROM invoice i WHERE i.parts_id = $id|;
1328
1329   my $sth = $dbh->prepare($query);
1330   $sth->execute || $form->dberror($query);
1331
1332   my $sum = 0;
1333   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1334
1335     $sum = $ref->{totalsold};
1336   }    #while
1337   $sth->finish;
1338
1339   if ($sum eq undef) {
1340     $sum = 0;
1341   }    #fi
1342
1343   $main::lxdebug->leave_sub();
1344
1345   return $sum;
1346 }    #end get_soldtotal
1347
1348 sub retrieve_item {
1349   $main::lxdebug->enter_sub();
1350
1351   my ($self, $myconfig, $form) = @_;
1352   my $i     = $form->{rowcount};
1353   my $where = "NOT p.obsolete = '1'";
1354
1355   if ($form->{"partnumber_$i"}) {
1356     my $partnumber = $form->like(lc $form->{"partnumber_$i"});
1357     $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
1358   }
1359   if ($form->{"description_$i"}) {
1360     my $description = $form->like(lc $form->{"description_$i"});
1361     $where .= " AND lower(p.description) LIKE '$description'";
1362   }
1363
1364   if ($form->{"partsgroup_$i"}) {
1365     my $partsgroup = $form->like(lc $form->{"partsgroup_$i"});
1366     $where .= " AND lower(pg.partsgroup) LIKE '$partsgroup'";
1367   }
1368
1369   if ($form->{"description_$i"}) {
1370     $where .= " ORDER BY description";
1371   } else {
1372     $where .= " ORDER BY partnumber";
1373   }
1374
1375   # connect to database
1376   my $dbh = $form->dbconnect($myconfig);
1377
1378   my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
1379                         p.listprice,
1380                         c1.accno AS inventory_accno,
1381                         c2.accno AS income_accno,
1382                         c3.accno AS expense_accno,
1383                  p.unit, p.assembly, p.bin, p.onhand, p.notes AS partnotes,
1384                  pg.partsgroup
1385                  FROM parts p
1386                  LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
1387                  LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
1388                  LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
1389                  LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1390                  WHERE $where|;
1391   my $sth = $dbh->prepare($query);
1392   $sth->execute || $form->dberror($query);
1393
1394   #while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1395
1396   # get tax rates and description
1397   #$accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{inventory_accno};
1398   #$query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
1399   #           FROM chart c, tax t
1400   #           WHERE c.id=t.chart_id AND t.taxkey in (SELECT taxkey_id from chart where accno = '$accno_id')
1401   #           ORDER BY accno|;
1402   # $stw = $dbh->prepare($query);
1403   #$stw->execute || $form->dberror($query);
1404
1405   #$ref->{taxaccounts} = "";
1406   #while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1407
1408   #   $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1409   #  $form->{"$ptr->{accno}_description"} = $ptr->{description};
1410   #   $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1411   #   $form->{taxaccounts} .= "$ptr->{accno} ";
1412   #   $ref->{taxaccounts} .= "$ptr->{accno} ";
1413
1414   #}
1415
1416   #$stw->finish;
1417   #chop $ref->{taxaccounts};
1418
1419   push @{ $form->{item_list} }, $ref;
1420
1421   #}
1422   $sth->finish;
1423   $dbh->disconnect;
1424
1425   $main::lxdebug->leave_sub();
1426 }
1427
1428 1;