Bei Nummernkreisen auch Sonderzeichen erlauben (Perls eigener Algorithmus hinter...
[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 use SL::DBUtils;
38
39 sub get_part {
40   $main::lxdebug->enter_sub();
41
42   my ($self, $myconfig, $form) = @_;
43
44   # connect to db
45   my $dbh = $form->dbconnect($myconfig);
46
47   my $query = qq|SELECT p.*,
48                  c1.accno AS inventory_accno,
49                  c2.accno AS income_accno,
50                  c3.accno AS expense_accno,
51                  pg.partsgroup
52                  FROM parts p
53                  LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
54                  LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
55                  LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
56                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
57                  WHERE p.id = ? |;
58   my @vars = ($form->{id});
59   my $sth = $dbh->prepare($query);
60   $sth->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
61   my $ref = $sth->fetchrow_hashref(NAME_lc);
62
63   # copy to $form variables
64   map { $form->{$_} = $ref->{$_} } (keys %{$ref});
65
66   $sth->finish;
67
68   my %oid = ('Pg'     => 'a.oid',
69              'Oracle' => 'a.rowid');
70
71   # part or service item
72   $form->{item} = ($form->{inventory_accno}) ? 'part' : 'service';
73   if ($form->{assembly}) {
74     $form->{item} = 'assembly';
75
76     # retrieve assembly items
77     $query = qq|SELECT p.id, p.partnumber, p.description,
78                 p.sellprice, p.weight, a.qty, a.bom, p.unit,
79                 pg.partsgroup
80                 FROM parts p
81                 JOIN assembly a ON (a.parts_id = p.id)
82                 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
83                 WHERE a.id = ?
84                 ORDER BY ?|;
85     @vars = ($form->{id}, $oid{$myconfig->{dbdriver}});
86     $sth = $dbh->prepare($query);
87     $sth->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
88     
89     $form->{assembly_rows} = 0;
90     while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
91       $form->{assembly_rows}++;
92       foreach my $key (keys %{$ref}) {
93         $form->{"${key}_$form->{assembly_rows}"} = $ref->{$key};
94       }
95     }
96     $sth->finish;
97
98   }
99
100   # setup accno hash for <option checked> {amount} is used in create_links
101   $form->{amount}{IC}         = $form->{inventory_accno};
102   $form->{amount}{IC_income}  = $form->{income_accno};
103   $form->{amount}{IC_sale}    = $form->{income_accno};
104   $form->{amount}{IC_expense} = $form->{expense_accno};
105   $form->{amount}{IC_cogs}    = $form->{expense_accno};
106
107   # get prices
108   $query =
109     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
110               WHERE parts_id = ? 
111               ORDER by pricegroup|;
112
113   @vars = ($form->{id});
114   $sth = $dbh->prepare($query);
115   $sth->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
116
117   @pricegroups          = ();
118   @pricegroups_not_used = ();
119
120   #for pricegroups
121   my $i = 1;
122   while (
123          ($form->{"klass_$i"}, $form->{"pricegroup_id_$i"},
124           $form->{"price_$i"}, $form->{"pricegroup_$i"})
125          = $sth->fetchrow_array
126     ) {
127     $form->{"price_$i"} = $form->round_amount($form->{"price_$i"}, 5);
128     $form->{"price_$i"} =
129       $form->format_amount($myconfig, $form->{"price_$i"}, 5);
130     push @pricegroups, $form->{"pricegroup_id_$i"};
131     $i++;
132   }
133
134   $sth->finish;
135
136   # get pricegroups
137   $query = qq|SELECT p.id, p.pricegroup FROM pricegroup p|;
138
139   $pkq = $dbh->prepare($query);
140   $pkq->execute || $form->dberror($query);
141   while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
142     push @{ $form->{PRICEGROUPS} }, $pkr;
143   }
144   $pkq->finish;
145
146   #find not used pricegroups
147   while ($tmp = pop @{ $form->{PRICEGROUPS} }) {
148     my $insert = 0;
149     foreach $item (@pricegroups) {
150       if ($item eq $tmp->{id}) {
151
152         #drop
153         $insert = 1;
154       }
155     }
156     if ($insert == 0) {
157       push @pricegroups_not_used, $tmp;
158     }
159   }
160
161   # if not used pricegroups are avaible
162   if (@pricegroups_not_used) {
163
164     foreach $name (@pricegroups_not_used) {
165       $form->{"klass_$i"} = "$name->{id}";
166       $form->{"price_$i"} = $form->round_amount($form->{sellprice}, 5);
167       $form->{"price_$i"} =
168         $form->format_amount($myconfig, $form->{"price_$i"}, 5);
169       $form->{"pricegroup_id_$i"} = "$name->{id}";
170       $form->{"pricegroup_$i"}    = "$name->{pricegroup}";
171       $i++;
172     }
173   }
174
175   #correct rows
176   $form->{price_rows} = $i - 1;
177
178   unless ($form->{item} eq 'service') {
179
180     # get makes
181     if ($form->{makemodel}) {
182       $query = qq|SELECT m.make, m.model FROM makemodel m
183                   WHERE m.parts_id = ?|;
184       @vars = ($form->{id});
185       $sth = $dbh->prepare($query);
186       $sth->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
187
188       my $i = 1;
189       while (($form->{"make_$i"}, $form->{"model_$i"}) = $sth->fetchrow_array)
190       {
191         $i++;
192       }
193       $sth->finish;
194       $form->{makemodel_rows} = $i - 1;
195
196     }
197   }
198
199   # get translations
200   $form->{language_values} = "";
201   $query = qq|SELECT language_id, translation FROM translation WHERE parts_id = ?|;
202   @vars = ($form->{id});
203   $trq = $dbh->prepare($query);
204   $trq->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
205   while ($tr = $trq->fetchrow_hashref(NAME_lc)) {
206     $form->{language_values} .= "---+++---".$tr->{language_id}."--++--".$tr->{translation};
207   }
208   $trq->finish;
209
210   # now get accno for taxes
211   $query = qq|SELECT c.accno
212               FROM chart c, partstax pt
213               WHERE pt.chart_id = c.id
214               AND pt.parts_id = $form->{id}|;
215
216   $sth = $dbh->prepare($query);
217   $sth->execute || $form->dberror($query);
218
219   while (($key) = $sth->fetchrow_array) {
220     $form->{amount}{$key} = $key;
221   }
222
223   $sth->finish;
224
225   # is it an orphan
226   $query = qq|SELECT i.parts_id
227               FROM invoice i
228               WHERE i.parts_id = $form->{id}
229             UNION
230               SELECT o.parts_id
231               FROM orderitems o
232               WHERE o.parts_id = $form->{id}
233             UNION
234               SELECT a.parts_id
235               FROM assembly a
236               WHERE a.parts_id = $form->{id}|;
237   $sth = $dbh->prepare($query);
238   $sth->execute || $form->dberror($query);
239
240   ($form->{orphaned}) = $sth->fetchrow_array;
241   $form->{orphaned} = !$form->{orphaned};
242   $sth->finish;
243
244   $form->{"unit_changeable"} = 1;
245   foreach my $table (qw(invoice assembly orderitems inventory license)) {
246     $query = "SELECT COUNT(*) FROM $table WHERE parts_id = ?";
247     my ($count) = $dbh->selectrow_array($query, undef, $form->{"id"});
248     $form->dberror($query . " (" . $form->{"id"} . ")") if ($dbh->err);
249
250     if ($count) {
251       $form->{"unit_changeable"} = 0;
252       last;
253     }
254   }
255
256   $dbh->disconnect;
257
258   $main::lxdebug->leave_sub();
259 }
260
261 sub get_pricegroups {
262   $main::lxdebug->enter_sub();
263
264   my ($self, $myconfig, $form) = @_;
265   my $dbh                  = $form->dbconnect($myconfig);
266   my $i                    = 1;
267   my @pricegroups_not_used = ();
268
269   # get pricegroups
270   my $query = qq|SELECT p.id, p.pricegroup FROM pricegroup p|;
271
272   my $pkq = $dbh->prepare($query);
273   $pkq->execute || $form->dberror($query);
274   while ($pkr = $pkq->fetchrow_hashref(NAME_lc)) {
275     push @{ $form->{PRICEGROUPS} }, $pkr;
276   }
277   $pkq->finish;
278
279   #find not used pricegroups
280   while ($tmp = pop @{ $form->{PRICEGROUPS} }) {
281     push @pricegroups_not_used, $tmp;
282   }
283
284   # if not used pricegroups are avaible
285   if (@pricegroups_not_used) {
286
287     foreach $name (@pricegroups_not_used) {
288       $form->{"klass_$i"} = "$name->{id}";
289       $form->{"price_$i"} = $form->round_amount($form->{sellprice}, 5);
290       $form->{"price_$i"} =
291         $form->format_amount($myconfig, $form->{"price_$i"}, 5);
292       $form->{"pricegroup_id_$i"} = "$name->{id}";
293       $form->{"pricegroup_$i"}    = "$name->{pricegroup}";
294       $i++;
295     }
296   }
297
298   #correct rows
299   $form->{price_rows} = $i - 1;
300
301   $dbh->disconnect;
302
303   $main::lxdebug->leave_sub();
304 }
305
306 sub retrieve_buchungsgruppen {
307   $main::lxdebug->enter_sub();
308
309   my ($self, $myconfig, $form) = @_;
310
311   my ($query, $sth);
312
313   my $dbh = $form->dbconnect($myconfig);
314
315   # get buchungsgruppen
316   $query = qq|SELECT id, description
317               FROM buchungsgruppen
318               ORDER BY sortkey|;
319   $sth = $dbh->prepare($query);
320   $sth->execute || $form->dberror($query);
321
322   $form->{BUCHUNGSGRUPPEN} = [];
323   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
324     push(@{ $form->{BUCHUNGSGRUPPEN} }, $ref);
325   }
326   $sth->finish;
327
328   $main::lxdebug->leave_sub();
329 }
330
331 sub save {
332   $main::lxdebug->enter_sub();
333
334   my ($self, $myconfig, $form) = @_;
335
336   # connect to database, turn off AutoCommit
337   my $dbh = $form->dbconnect_noauto($myconfig);
338
339   # save the part
340   # make up a unique handle and store in partnumber field
341   # then retrieve the record based on the unique handle to get the id
342   # replace the partnumber field with the actual variable
343   # add records for makemodel
344
345   # if there is a $form->{id} then replace the old entry
346   # delete all makemodel entries and add the new ones
347
348   # escape '
349   map { $form->{$_} =~ s/\'/\'\'/g } qw(partnumber description notes unit);
350
351   # undo amount formatting
352   map { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
353     qw(rop weight listprice sellprice gv lastcost stock);
354
355   # set date to NULL if nothing entered
356   $form->{priceupdate} =
357     ($form->{priceupdate}) ? qq|'$form->{priceupdate}'| : "NULL";
358
359   $form->{makemodel} = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
360
361   $form->{alternate} = 0;
362   $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
363   $form->{obsolete} *= 1;
364   $form->{shop}     *= 1;
365   $form->{onhand}   *= 1;
366   $form->{ve}       *= 1;
367   $form->{ge}       *= 1;
368   $form->{buchungsgruppen_id}       *= 1;
369   $form->{not_discountable}       *= 1;
370   $form->{payment_id}       *= 1;
371
372   my ($query, $sth);
373
374   if ($form->{id}) {
375
376     # get old price
377     $query = qq|SELECT p.sellprice, p.weight
378                 FROM parts p
379                 WHERE p.id = $form->{id}|;
380     $sth = $dbh->prepare($query);
381     $sth->execute || $form->dberror($query);
382     my ($sellprice, $weight) = $sth->fetchrow_array;
383     $sth->finish;
384
385     # if item is part of an assembly adjust all assemblies
386     $query = qq|SELECT a.id, a.qty
387                 FROM assembly a
388                 WHERE a.parts_id = $form->{id}|;
389     $sth = $dbh->prepare($query);
390     $sth->execute || $form->dberror($query);
391     while (my ($id, $qty) = $sth->fetchrow_array) {
392       &update_assembly($dbh, $form, $id, $qty, $sellprice * 1, $weight * 1);
393     }
394     $sth->finish;
395
396     if ($form->{item} ne 'service') {
397
398       # delete makemodel records
399       $query = qq|DELETE FROM makemodel
400                   WHERE parts_id = $form->{id}|;
401       $dbh->do($query) || $form->dberror($query);
402     }
403
404     if ($form->{item} eq 'assembly') {
405       if ($form->{onhand} != 0) {
406         &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
407       }
408
409       # delete assembly records
410       $query = qq|DELETE FROM assembly
411                   WHERE id = $form->{id}|;
412       $dbh->do($query) || $form->dberror($query);
413
414       $form->{onhand} += $form->{stock};
415     }
416
417     # delete tax records
418     $query = qq|DELETE FROM partstax
419                 WHERE parts_id = $form->{id}|;
420     $dbh->do($query) || $form->dberror($query);
421
422     # delete translations
423     $query = qq|DELETE FROM translation
424                 WHERE parts_id = $form->{id}|;
425     $dbh->do($query) || $form->dberror($query);
426
427   } else {
428     my $uid = rand() . time;
429     $uid .= $form->{login};
430
431     $query = qq|SELECT p.id FROM parts p
432                 WHERE p.partnumber = '$form->{partnumber}'|;
433     $sth = $dbh->prepare($query);
434     $sth->execute || $form->dberror($query);
435     ($form->{id}) = $sth->fetchrow_array;
436     $sth->finish;
437
438     if ($form->{id} ne "") {
439       $main::lxdebug->leave_sub();
440       return 3;
441     }
442     $query = qq|INSERT INTO parts (partnumber, description)
443                 VALUES ('$uid', 'dummy')|;
444     $dbh->do($query) || $form->dberror($query);
445
446     $query = qq|SELECT p.id FROM parts p
447                 WHERE p.partnumber = '$uid'|;
448     $sth = $dbh->prepare($query);
449     $sth->execute || $form->dberror($query);
450
451     ($form->{id}) = $sth->fetchrow_array;
452     $sth->finish;
453
454     $form->{orphaned} = 1;
455     $form->{onhand} = $form->{stock} if $form->{item} eq 'assembly';
456     if ($form->{partnumber} eq "" && $form->{"item"} eq "service") {
457       $form->{partnumber} = $form->update_defaults($myconfig, "servicenumber");
458     }
459     if ($form->{partnumber} eq "" && $form->{"item"} ne "service") {
460       $form->{partnumber} = $form->update_defaults($myconfig, "articlenumber");
461     }
462
463   }
464   my $partsgroup_id = 0;
465
466   if ($form->{partsgroup}) {
467     ($partsgroup, $partsgroup_id) = split /--/, $form->{partsgroup};
468   }
469
470   my ($subq_inventory, $subq_expense, $subq_income);
471   if ($form->{"item"} eq "part") {
472     $subq_inventory =
473       qq|(SELECT bg.inventory_accno_id | .
474       qq| FROM buchungsgruppen bg | .
475       qq| WHERE bg.id = | . $dbh->quote($form->{"buchungsgruppen_id"}) . qq|)|;
476   } else {
477     $subq_inventory = "NULL";
478   }
479
480   if ($form->{"item"} ne "assembly") {
481     $subq_expense =
482       qq|(SELECT bg.expense_accno_id_0 | .
483       qq| FROM buchungsgruppen bg | .
484       qq| WHERE bg.id = | . $dbh->quote($form->{"buchungsgruppen_id"}) . qq|)|;
485   } else {
486     $subq_expense = "NULL";
487   }
488
489   $subq_income =
490     qq|(SELECT bg.income_accno_id_0 | .
491     qq| FROM buchungsgruppen bg | .
492     qq| WHERE bg.id = | . $dbh->quote($form->{"buchungsgruppen_id"}) . qq|)|;
493
494   $query = qq|UPDATE parts SET
495               partnumber = '$form->{partnumber}',
496               description = '$form->{description}',
497               makemodel = '$form->{makemodel}',
498               alternate = '$form->{alternate}',
499               assembly = '$form->{assembly}',
500               listprice = $form->{listprice},
501               sellprice = $form->{sellprice},
502               lastcost = $form->{lastcost},
503               weight = $form->{weight},
504               priceupdate = $form->{priceupdate},
505               unit = '$form->{unit}',
506               notes = '$form->{notes}',
507               formel = '$form->{formel}',
508               rop = $form->{rop},
509               bin = '$form->{bin}',
510               buchungsgruppen_id = '$form->{buchungsgruppen_id}',
511               payment_id = '$form->{payment_id}',
512               inventory_accno_id = $subq_inventory,
513               income_accno_id = $subq_income,
514               expense_accno_id = $subq_expense,
515               obsolete = '$form->{obsolete}',
516               image = '$form->{image}',
517               drawing = '$form->{drawing}',
518               shop = '$form->{shop}',
519               ve = '$form->{ve}',
520               gv = '$form->{gv}',
521               ean = '$form->{ean}',
522               not_discountable = '$form->{not_discountable}',
523               microfiche = '$form->{microfiche}',
524               partsgroup_id = $partsgroup_id
525               WHERE id = $form->{id}|;
526   $dbh->do($query) || $form->dberror($query);
527
528   # delete translation records
529   $query = qq|DELETE FROM translation
530               WHERE parts_id = $form->{id}|;
531   $dbh->do($query) || $form->dberror($query);
532
533   if ($form->{language_values} ne "") {
534     split /---\+\+\+---/,$form->{language_values};
535     foreach $item (@_) {
536       my ($language_id, $translation, $longdescription) = split /--\+\+--/, $item;
537       if ($translation ne "") {
538         $query = qq|INSERT into translation (parts_id, language_id, translation, longdescription) VALUES
539                     ($form->{id}, $language_id, | . $dbh->quote($translation) . qq|, | . $dbh->quote($longdescription) . qq| )|;
540         $dbh->do($query) || $form->dberror($query);
541       }
542     }
543   }
544   # delete price records
545   $query = qq|DELETE FROM prices
546               WHERE parts_id = $form->{id}|;
547   $dbh->do($query) || $form->dberror($query);
548   # insert price records only if different to sellprice
549   for my $i (1 .. $form->{price_rows}) {
550     if ($form->{"price_$i"} eq "0") {
551       $form->{"price_$i"} = $form->{sellprice};
552     }
553     if (
554         (   $form->{"price_$i"}
555          || $form->{"klass_$i"}
556          || $form->{"pricegroup_id_$i"})
557         and $form->{"price_$i"} != $form->{sellprice}
558       ) {
559       $klass = $form->parse_amount($myconfig, $form->{"klass_$i"});
560       $price = $form->parse_amount($myconfig, $form->{"price_$i"});
561       $pricegroup_id =
562         $form->parse_amount($myconfig, $form->{"pricegroup_id_$i"});
563       $query = qq|INSERT INTO prices (parts_id, pricegroup_id, price)
564                   VALUES($form->{id},$pricegroup_id,$price)|;
565       $dbh->do($query) || $form->dberror($query);
566     }
567   }
568
569   # insert makemodel records
570   unless ($form->{item} eq 'service') {
571     for my $i (1 .. $form->{makemodel_rows}) {
572       if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
573         map { $form->{"${_}_$i"} =~ s/\'/\'\'/g } qw(make model);
574
575         $query = qq|INSERT INTO makemodel (parts_id, make, model)
576                     VALUES ($form->{id},
577                     '$form->{"make_$i"}', '$form->{"model_$i"}')|;
578         $dbh->do($query) || $form->dberror($query);
579       }
580     }
581   }
582
583   # insert taxes
584   foreach $item (split / /, $form->{taxaccounts}) {
585     if ($form->{"IC_tax_$item"}) {
586       $query = qq|INSERT INTO partstax (parts_id, chart_id)
587                   VALUES ($form->{id},
588                           (SELECT c.id
589                            FROM chart c
590                            WHERE c.accno = '$item'))|;
591       $dbh->do($query) || $form->dberror($query);
592     }
593   }
594
595   # add assembly records
596   if ($form->{item} eq 'assembly') {
597
598     for my $i (1 .. $form->{assembly_rows}) {
599       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
600
601       if ($form->{"qty_$i"} != 0) {
602         $form->{"bom_$i"} *= 1;
603         $query = qq|INSERT INTO assembly (id, parts_id, qty, bom)
604                     VALUES ($form->{id}, $form->{"id_$i"},
605                     $form->{"qty_$i"}, '$form->{"bom_$i"}')|;
606         $dbh->do($query) || $form->dberror($query);
607       }
608     }
609
610     # adjust onhand for the parts
611     if ($form->{onhand} != 0) {
612       &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
613     }
614
615     @a = localtime;
616     $a[5] += 1900;
617     $a[4]++;
618     my $shippingdate = "$a[5]-$a[4]-$a[3]";
619
620     $form->get_employee($dbh);
621
622     # add inventory record
623     $query = qq|INSERT INTO inventory (warehouse_id, parts_id, qty,
624                 shippingdate, employee_id) VALUES (
625                 0, $form->{id}, $form->{stock}, '$shippingdate',
626                 $form->{employee_id})|;
627     $dbh->do($query) || $form->dberror($query);
628
629   }
630
631   #set expense_accno=inventory_accno if they are different => bilanz
632   $vendor_accno =
633     ($form->{expense_accno} != $form->{inventory_accno})
634     ? $form->{inventory_accno}
635     : $form->{expense_accno};
636
637   # get tax rates and description
638   $accno_id =
639     ($form->{vc} eq "customer") ? $form->{income_accno} : $vendor_accno;
640   $query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
641               FROM chart c, tax t
642               WHERE c.id=t.chart_id AND t.taxkey in (SELECT taxkey_id from chart where accno = '$accno_id')
643               ORDER BY c.accno|;
644   $stw = $dbh->prepare($query);
645
646   $stw->execute || $form->dberror($query);
647
648   $form->{taxaccount} = "";
649   while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
650
651     #    if ($customertax{$ref->{accno}}) {
652     $form->{taxaccount} .= "$ptr->{accno} ";
653     if (!($form->{taxaccount2} =~ /$ptr->{accno}/)) {
654       $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
655       $form->{"$ptr->{accno}_description"} = $ptr->{description};
656       $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
657       $form->{taxaccount2} .= " $ptr->{accno} ";
658     }
659
660   }
661
662   # commit
663   my $rc = $dbh->commit;
664   $dbh->disconnect;
665
666   $main::lxdebug->leave_sub();
667
668   return $rc;
669 }
670
671 sub update_assembly {
672   $main::lxdebug->enter_sub();
673
674   my ($dbh, $form, $id, $qty, $sellprice, $weight) = @_;
675
676   my $query = qq|SELECT a.id, a.qty
677                  FROM assembly a
678                  WHERE a.parts_id = $id|;
679   my $sth = $dbh->prepare($query);
680   $sth->execute || $form->dberror($query);
681
682   while (my ($pid, $aqty) = $sth->fetchrow_array) {
683     &update_assembly($dbh, $form, $pid, $aqty * $qty, $sellprice, $weight);
684   }
685   $sth->finish;
686
687   $query = qq|UPDATE parts
688               SET sellprice = sellprice +
689                   $qty * ($form->{sellprice} - $sellprice),
690                   weight = weight +
691                   $qty * ($form->{weight} - $weight)
692               WHERE id = $id|;
693   $dbh->do($query) || $form->dberror($query);
694
695   $main::lxdebug->leave_sub();
696 }
697
698 sub retrieve_assemblies {
699   $main::lxdebug->enter_sub();
700
701   my ($self, $myconfig, $form) = @_;
702
703   # connect to database
704   my $dbh = $form->dbconnect($myconfig);
705
706   my $where = '1 = 1';
707
708   if ($form->{partnumber}) {
709     my $partnumber = $form->like(lc $form->{partnumber});
710     $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
711   }
712
713   if ($form->{description}) {
714     my $description = $form->like(lc $form->{description});
715     $where .= " AND lower(p.description) LIKE '$description'";
716   }
717   $where .= " AND NOT p.obsolete = '1'";
718
719   # retrieve assembly items
720   my $query = qq|SELECT p.id, p.partnumber, p.description,
721                  p.bin, p.onhand, p.rop,
722                    (SELECT sum(p2.inventory_accno_id)
723                     FROM parts p2, assembly a
724                     WHERE p2.id = a.parts_id
725                     AND a.id = p.id) AS inventory
726                  FROM parts p
727                  WHERE $where
728                  AND assembly = '1'|;
729
730   my $sth = $dbh->prepare($query);
731   $sth->execute || $form->dberror($query);
732
733   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
734     push @{ $form->{assembly_items} }, $ref if $ref->{inventory};
735   }
736   $sth->finish;
737
738   $dbh->disconnect;
739
740   $main::lxdebug->leave_sub();
741 }
742
743 sub restock_assemblies {
744   $main::lxdebug->enter_sub();
745
746   my ($self, $myconfig, $form) = @_;
747
748   # connect to database
749   my $dbh = $form->dbconnect_noauto($myconfig);
750
751   for my $i (1 .. $form->{rowcount}) {
752
753     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
754
755     if ($form->{"qty_$i"} != 0) {
756       &adjust_inventory($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
757     }
758
759   }
760
761   my $rc = $dbh->commit;
762   $dbh->disconnect;
763
764   $main::lxdebug->leave_sub();
765
766   return $rc;
767 }
768
769 sub adjust_inventory {
770   $main::lxdebug->enter_sub();
771
772   my ($dbh, $form, $id, $qty) = @_;
773
774   my $query = qq|SELECT p.id, p.inventory_accno_id, p.assembly, a.qty
775                  FROM parts p, assembly a
776                  WHERE a.parts_id = p.id
777                  AND a.id = $id|;
778   my $sth = $dbh->prepare($query);
779   $sth->execute || $form->dberror($query);
780
781   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
782
783     my $allocate = $qty * $ref->{qty};
784
785     # is it a service item, then loop
786     $ref->{inventory_accno_id} *= 1;
787     next if (($ref->{inventory_accno_id} == 0) && !$ref->{assembly});
788
789     # adjust parts onhand
790     $form->update_balance($dbh, "parts", "onhand",
791                           qq|id = $ref->{id}|,
792                           $allocate * -1);
793   }
794
795   $sth->finish;
796
797   # update assembly
798   my $rc = $form->update_balance($dbh, "parts", "onhand", qq|id = ?|, $qty, $id);
799
800   $main::lxdebug->leave_sub();
801
802   return $rc;
803 }
804
805 sub delete {
806   $main::lxdebug->enter_sub();
807
808   my ($self, $myconfig, $form) = @_;
809
810   # connect to database, turn off AutoCommit
811   my $dbh = $form->dbconnect_noauto($myconfig);
812
813   # first delete prices of pricegroup 
814   my $query = qq|DELETE FROM prices
815            WHERE parts_id = $form->{id}|;
816   $dbh->do($query) || $form->dberror($query);
817
818   my $query = qq|DELETE FROM parts
819                  WHERE id = $form->{id}|;
820   $dbh->do($query) || $form->dberror($query);
821
822   $query = qq|DELETE FROM partstax
823               WHERE parts_id = $form->{id}|;
824   $dbh->do($query) || $form->dberror($query);
825
826   # check if it is a part, assembly or service
827   if ($form->{item} ne 'service') {
828     $query = qq|DELETE FROM makemodel
829                 WHERE parts_id = $form->{id}|;
830     $dbh->do($query) || $form->dberror($query);
831   }
832
833   if ($form->{item} eq 'assembly') {
834
835     # delete inventory
836     $query = qq|DELETE FROM inventory
837                 WHERE parts_id = $form->{id}|;
838     $dbh->do($query) || $form->dberror($query);
839
840     $query = qq|DELETE FROM assembly
841                 WHERE id = $form->{id}|;
842     $dbh->do($query) || $form->dberror($query);
843   }
844
845   if ($form->{item} eq 'alternate') {
846     $query = qq|DELETE FROM alternate
847                 WHERE id = $form->{id}|;
848     $dbh->do($query) || $form->dberror($query);
849   }
850
851   # commit
852   my $rc = $dbh->commit;
853   $dbh->disconnect;
854
855   $main::lxdebug->leave_sub();
856
857   return $rc;
858 }
859
860 sub assembly_item {
861   $main::lxdebug->enter_sub();
862
863   my ($self, $myconfig, $form) = @_;
864
865   my $i = $form->{assembly_rows};
866   my $var;
867   my $where = "1 = 1";
868
869   if ($form->{"partnumber_$i"}) {
870     $var = $form->like(lc $form->{"partnumber_$i"});
871     $where .= " AND lower(p.partnumber) LIKE '$var'";
872   }
873   if ($form->{"description_$i"}) {
874     $var = $form->like(lc $form->{"description_$i"});
875     $where .= " AND lower(p.description) LIKE '$var'";
876   }
877   if ($form->{"partsgroup_$i"}) {
878     $var = $form->like(lc $form->{"partsgroup_$i"});
879     $where .= " AND lower(pg.partsgroup) LIKE '$var'";
880   }
881
882   if ($form->{id}) {
883     $where .= " AND NOT p.id = $form->{id}";
884   }
885
886   if ($partnumber) {
887     $where .= " ORDER BY p.partnumber";
888   } else {
889     $where .= " ORDER BY p.description";
890   }
891
892   # connect to database
893   my $dbh = $form->dbconnect($myconfig);
894
895   my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
896                  p.weight, p.onhand, p.unit,
897                  pg.partsgroup
898                  FROM parts p
899                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
900                  WHERE $where|;
901   my $sth = $dbh->prepare($query);
902   $sth->execute || $form->dberror($query);
903
904   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
905     push @{ $form->{item_list} }, $ref;
906   }
907
908   $sth->finish;
909   $dbh->disconnect;
910
911   $main::lxdebug->leave_sub();
912 }
913
914 sub all_parts {
915   $main::lxdebug->enter_sub();
916
917   my ($self, $myconfig, $form) = @_;
918
919   my $where = '1 = 1';
920   my $var;
921
922   my $group;
923   my $limit;
924
925   foreach my $item (qw(partnumber drawing microfiche)) {
926     if ($form->{$item}) {
927       $var = $form->like(lc $form->{$item});
928       $where .= " AND lower(p.$item) LIKE '$var'";
929     }
930   }
931
932   # special case for description
933   if ($form->{description}) {
934     unless (   $form->{bought}
935             || $form->{sold}
936             || $form->{onorder}
937             || $form->{ordered}
938             || $form->{rfq}
939             || $form->{quoted}) {
940       $var = $form->like(lc $form->{description});
941       $where .= " AND lower(p.description) LIKE '$var'";
942     }
943   }
944
945   # special case for serialnumber
946   if ($form->{l_serialnumber}) {
947     if ($form->{serialnumber}) {
948       $var = $form->like(lc $form->{serialnumber});
949       $where .= " AND lower(serialnumber) LIKE '$var'";
950     }
951   }
952
953   if ($form->{ean}) {
954     $var = $form->like(lc $form->{ean});
955     $where .= " AND lower(ean) LIKE '$var'";
956   }
957   
958   if ($form->{searchitems} eq 'part') {
959     $where .= " AND p.inventory_accno_id > 0";
960   }
961   if ($form->{searchitems} eq 'assembly') {
962     $form->{bought} = "";
963     $where .= " AND p.assembly = '1'";
964   }
965   if ($form->{searchitems} eq 'service') {
966     $where .= " AND p.inventory_accno_id IS NULL AND NOT p.assembly = '1'";
967
968     # irrelevant for services
969     $form->{make} = $form->{model} = "";
970   }
971
972   # items which were never bought, sold or on an order
973   if ($form->{itemstatus} eq 'orphaned') {
974     $form->{onhand}  = $form->{short}   = 0;
975     $form->{bought}  = $form->{sold}    = 0;
976     $form->{onorder} = $form->{ordered} = 0;
977     $form->{rfq}     = $form->{quoted}  = 0;
978
979     $form->{transdatefrom} = $form->{transdateto} = "";
980
981     $where .= " AND p.onhand = 0
982                 AND p.id NOT IN (SELECT p.id FROM parts p, invoice i
983                                  WHERE p.id = i.parts_id)
984                 AND p.id NOT IN (SELECT p.id FROM parts p, assembly a
985                                  WHERE p.id = a.parts_id)
986                 AND p.id NOT IN (SELECT p.id FROM parts p, orderitems o
987                                  WHERE p.id = o.parts_id)";
988   }
989
990   if ($form->{itemstatus} eq 'active') {
991     $where .= " AND p.obsolete = '0'";
992   }
993   if ($form->{itemstatus} eq 'obsolete') {
994     $where .= " AND p.obsolete = '1'";
995     $form->{onhand} = $form->{short} = 0;
996   }
997   if ($form->{itemstatus} eq 'onhand') {
998     $where .= " AND p.onhand > 0";
999   }
1000   if ($form->{itemstatus} eq 'short') {
1001     $where .= " AND p.onhand < p.rop";
1002   }
1003   if ($form->{make}) {
1004     $var = $form->like(lc $form->{make});
1005     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
1006                            FROM makemodel m WHERE lower(m.make) LIKE '$var')";
1007   }
1008   if ($form->{model}) {
1009     $var = $form->like(lc $form->{model});
1010     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
1011                            FROM makemodel m WHERE lower(m.model) LIKE '$var')";
1012   }
1013   if ($form->{partsgroup}) {
1014     $var = $form->like(lc $form->{partsgroup});
1015     $where .= " AND lower(pg.partsgroup) LIKE '$var'";
1016   }
1017   if ($form->{l_soldtotal}) {
1018     $where .= " AND p.id=i.parts_id AND  i.qty >= 0";
1019     $group =
1020       " GROUP BY  p.id,p.partnumber,p.description,p.onhand,p.unit,p.bin, p.sellprice,p.listprice,p.lastcost,p.priceupdate,pg.partsgroup";
1021   }
1022   if ($form->{top100}) {
1023     $limit = " LIMIT 100";
1024   }
1025
1026   # tables revers?
1027   if ($form->{revers} == 1) {
1028     $form->{desc} = " DESC";
1029   } else {
1030     $form->{desc} = "";
1031   }
1032
1033   # connect to database
1034   my $dbh = $form->dbconnect($myconfig);
1035
1036   my $sortorder = $form->{sort};
1037   $sortorder .= $form->{desc};
1038   $sortorder = $form->{sort} if $form->{sort};
1039
1040   my $query = "";
1041
1042   if ($form->{l_soldtotal}) {
1043     $form->{soldtotal} = 'soldtotal';
1044     $query =
1045       qq|SELECT p.id,p.partnumber,p.description,p.onhand,p.unit,p.bin,p.sellprice,p.listprice,
1046                 p.lastcost,p.priceupdate,pg.partsgroup,sum(i.qty) as soldtotal FROM parts
1047                 p LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id), invoice i
1048                 WHERE $where
1049                 $group
1050                 ORDER BY $sortorder
1051                 $limit|;
1052   } else {
1053     $query = qq|SELECT p.id, p.partnumber, p.description, p.onhand, p.unit,
1054                  p.bin, p.sellprice, p.listprice, p.lastcost, p.rop, p.weight,
1055                  p.priceupdate, p.image, p.drawing, p.microfiche,
1056                  pg.partsgroup
1057                  FROM parts p
1058                  LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1059                  WHERE $where
1060                  $group
1061                  ORDER BY $sortorder|;
1062   }
1063
1064   # rebuild query for bought and sold items
1065   if (   $form->{bought}
1066       || $form->{sold}
1067       || $form->{onorder}
1068       || $form->{ordered}
1069       || $form->{rfq}
1070       || $form->{quoted}) {
1071
1072     my @a = qw(partnumber description bin priceupdate name);
1073
1074     push @a, qw(invnumber serialnumber) if ($form->{bought} || $form->{sold});
1075     push @a, "ordnumber" if ($form->{onorder} || $form->{ordered});
1076     push @a, "quonumber" if ($form->{rfq}     || $form->{quoted});
1077
1078     my $union = "";
1079     $query = "";
1080
1081     if ($form->{bought} || $form->{sold}) {
1082
1083       my $invwhere = "$where";
1084       $invwhere .= " AND i.assemblyitem = '0'";
1085       $invwhere .= " AND a.transdate >= '$form->{transdatefrom}'"
1086         if $form->{transdatefrom};
1087       $invwhere .= " AND a.transdate <= '$form->{transdateto}'"
1088         if $form->{transdateto};
1089
1090       if ($form->{description}) {
1091         $var = $form->like(lc $form->{description});
1092         $invwhere .= " AND lower(i.description) LIKE '$var'";
1093       }
1094
1095       my $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
1096                     i.qty AS onhand, i.unit, p.bin, i.sellprice,
1097                     p.listprice, p.lastcost, p.rop, p.weight,
1098                     p.priceupdate, p.image, p.drawing, p.microfiche,
1099                     pg.partsgroup,
1100                     a.invnumber, a.ordnumber, a.quonumber, i.trans_id,
1101                     ct.name, i.deliverydate|;
1102
1103       if ($form->{bought}) {
1104         $query = qq|
1105                     SELECT $flds, 'ir' AS module, '' AS type,
1106                     1 AS exchangerate
1107                     FROM invoice i
1108                     JOIN parts p ON (p.id = i.parts_id)
1109                     JOIN ap a ON (a.id = i.trans_id)
1110                     JOIN vendor ct ON (a.vendor_id = ct.id)
1111                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1112                     WHERE $invwhere|;
1113         $union = "
1114                   UNION";
1115       }
1116
1117       if ($form->{sold}) {
1118         $query .= qq|$union
1119                      SELECT $flds, 'is' AS module, '' AS type,
1120                      1 As exchangerate
1121                      FROM invoice i
1122                      JOIN parts p ON (p.id = i.parts_id)
1123                      JOIN ar a ON (a.id = i.trans_id)
1124                      JOIN customer ct ON (a.customer_id = ct.id)
1125                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1126                      WHERE $invwhere|;
1127         $union = "
1128                   UNION";
1129       }
1130     }
1131
1132     if ($form->{onorder} || $form->{ordered}) {
1133       my $ordwhere = "$where
1134                      AND o.quotation = '0'";
1135       $ordwhere .= " AND o.transdate >= '$form->{transdatefrom}'"
1136         if $form->{transdatefrom};
1137       $ordwhere .= " AND o.transdate <= '$form->{transdateto}'"
1138         if $form->{transdateto};
1139
1140       if ($form->{description}) {
1141         $var = $form->like(lc $form->{description});
1142         $ordwhere .= " AND lower(oi.description) LIKE '$var'";
1143       }
1144
1145       $flds =
1146         qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1147                  oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
1148                  p.listprice, p.lastcost, p.rop, p.weight,
1149                  p.priceupdate, p.image, p.drawing, p.microfiche,
1150                  pg.partsgroup,
1151                  '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1152                  ct.name, NULL AS deliverydate|;
1153
1154       if ($form->{ordered}) {
1155         $query .= qq|$union
1156                      SELECT $flds, 'oe' AS module, 'sales_order' AS type,
1157                     (SELECT buy FROM exchangerate ex
1158                      WHERE ex.curr = o.curr
1159                      AND ex.transdate = o.transdate) AS exchangerate
1160                      FROM orderitems oi
1161                      JOIN parts p ON (oi.parts_id = p.id)
1162                      JOIN oe o ON (oi.trans_id = o.id)
1163                      JOIN customer ct ON (o.customer_id = ct.id)
1164                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1165                      WHERE $ordwhere
1166                      AND o.customer_id > 0|;
1167         $union = "
1168                   UNION";
1169       }
1170
1171       if ($form->{onorder}) {
1172         $flds =
1173           qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1174                    oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
1175                    p.listprice, p.lastcost, p.rop, p.weight,
1176                    p.priceupdate, p.image, p.drawing, p.microfiche,
1177                    pg.partsgroup,
1178                    '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1179                    ct.name, NULL AS deliverydate|;
1180
1181         $query .= qq|$union
1182                     SELECT $flds, 'oe' AS module, 'purchase_order' AS type,
1183                     (SELECT sell FROM exchangerate ex
1184                      WHERE ex.curr = o.curr
1185                      AND ex.transdate = o.transdate) AS exchangerate
1186                     FROM orderitems oi
1187                     JOIN parts p ON (oi.parts_id = p.id)
1188                     JOIN oe o ON (oi.trans_id = o.id)
1189                     JOIN vendor ct ON (o.vendor_id = ct.id)
1190                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1191                     WHERE $ordwhere
1192                     AND o.vendor_id > 0|;
1193       }
1194
1195     }
1196
1197     if ($form->{rfq} || $form->{quoted}) {
1198       my $quowhere = "$where
1199                      AND o.quotation = '1'";
1200       $quowhere .= " AND o.transdate >= '$form->{transdatefrom}'"
1201         if $form->{transdatefrom};
1202       $quowhere .= " AND o.transdate <= '$form->{transdateto}'"
1203         if $form->{transdateto};
1204
1205       if ($form->{description}) {
1206         $var = $form->like(lc $form->{description});
1207         $quowhere .= " AND lower(oi.description) LIKE '$var'";
1208       }
1209
1210       $flds =
1211         qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1212                  oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
1213                  p.listprice, p.lastcost, p.rop, p.weight,
1214                  p.priceupdate, p.image, p.drawing, p.microfiche,
1215                  pg.partsgroup,
1216                  '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1217                  ct.name, NULL AS deliverydate|;
1218
1219       if ($form->{quoted}) {
1220         $query .= qq|$union
1221                      SELECT $flds, 'oe' AS module, 'sales_quotation' AS type,
1222                     (SELECT buy FROM exchangerate ex
1223                      WHERE ex.curr = o.curr
1224                      AND ex.transdate = o.transdate) AS exchangerate
1225                      FROM orderitems oi
1226                      JOIN parts p ON (oi.parts_id = p.id)
1227                      JOIN oe o ON (oi.trans_id = o.id)
1228                      JOIN customer ct ON (o.customer_id = ct.id)
1229                      LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1230                      WHERE $quowhere
1231                      AND o.customer_id > 0|;
1232         $union = "
1233                   UNION";
1234       }
1235
1236       if ($form->{rfq}) {
1237         $flds =
1238           qq|p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
1239                    oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
1240                    p.listprice, p.lastcost, p.rop, p.weight,
1241                    p.priceupdate, p.image, p.drawing, p.microfiche,
1242                    pg.partsgroup,
1243                    '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
1244                    ct.name, NULL AS deliverydate|;
1245
1246         $query .= qq|$union
1247                     SELECT $flds, 'oe' AS module, 'request_quotation' AS type,
1248                     (SELECT sell FROM exchangerate ex
1249                      WHERE ex.curr = o.curr
1250                      AND ex.transdate = o.transdate) AS exchangerate
1251                     FROM orderitems oi
1252                     JOIN parts p ON (oi.parts_id = p.id)
1253                     JOIN oe o ON (oi.trans_id = o.id)
1254                     JOIN vendor ct ON (o.vendor_id = ct.id)
1255                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1256                     WHERE $quowhere
1257                     AND o.vendor_id > 0|;
1258       }
1259
1260     }
1261     $query .= qq|
1262                  ORDER BY $sortorder|;
1263
1264   }
1265   my $sth = $dbh->prepare($query);
1266   $sth->execute || $form->dberror($query);
1267
1268   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1269     push @{ $form->{parts} }, $ref;
1270   }
1271
1272   $sth->finish;
1273
1274   # include individual items for assemblies
1275   if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
1276     foreach $item (@{ $form->{parts} }) {
1277       push @assemblies, $item;
1278       $query = qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
1279                   p.unit, p.bin,
1280                   p.sellprice, p.listprice, p.lastcost,
1281                   p.rop, p.weight, p.priceupdate,
1282                   p.image, p.drawing, p.microfiche
1283                   FROM parts p, assembly a
1284                   WHERE p.id = a.parts_id
1285                   AND a.id = $item->{id}|;
1286
1287       $sth = $dbh->prepare($query);
1288       $sth->execute || $form->dberror($query);
1289
1290       while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1291         $ref->{assemblyitem} = 1;
1292         push @assemblies, $ref;
1293       }
1294       $sth->finish;
1295
1296       push @assemblies, { id => $item->{id} };
1297
1298     }
1299
1300     # copy assemblies to $form->{parts}
1301     @{ $form->{parts} } = @assemblies;
1302   }
1303
1304   $dbh->disconnect;
1305   $main::lxdebug->leave_sub();
1306 }
1307
1308 sub update_prices {
1309   $main::lxdebug->enter_sub();
1310
1311   my ($self, $myconfig, $form) = @_;
1312
1313   my $where = '1 = 1';
1314   my $var;
1315
1316   my $group;
1317   my $limit;
1318
1319   foreach my $item (qw(partnumber drawing microfiche make model)) {
1320     if ($form->{$item}) {
1321       $var = $form->like(lc $form->{$item});
1322
1323       # make will build later Bugfix 145
1324       if ($item ne 'make') {
1325         $where .= " AND lower(p.$item) LIKE '$var'";
1326       }
1327     }
1328   }
1329
1330   # special case for description
1331   if ($form->{description}) {
1332     unless (   $form->{bought}
1333             || $form->{sold}
1334             || $form->{onorder}
1335             || $form->{ordered}
1336             || $form->{rfq}
1337             || $form->{quoted}) {
1338       $var = $form->like(lc $form->{description});
1339       $where .= " AND lower(p.description) LIKE '$var'";
1340     }
1341   }
1342
1343   # special case for serialnumber
1344   if ($form->{l_serialnumber}) {
1345     if ($form->{serialnumber}) {
1346       $var = $form->like(lc $form->{serialnumber});
1347       $where .= " AND lower(serialnumber) LIKE '$var'";
1348     }
1349   }
1350
1351
1352   # items which were never bought, sold or on an order
1353   if ($form->{itemstatus} eq 'orphaned') {
1354     $form->{onhand}  = $form->{short}   = 0;
1355     $form->{bought}  = $form->{sold}    = 0;
1356     $form->{onorder} = $form->{ordered} = 0;
1357     $form->{rfq}     = $form->{quoted}  = 0;
1358
1359     $form->{transdatefrom} = $form->{transdateto} = "";
1360
1361     $where .= " AND p.onhand = 0
1362                 AND p.id NOT IN (SELECT p.id FROM parts p, invoice i
1363                                  WHERE p.id = i.parts_id)
1364                 AND p.id NOT IN (SELECT p.id FROM parts p, assembly a
1365                                  WHERE p.id = a.parts_id)
1366                 AND p.id NOT IN (SELECT p.id FROM parts p, orderitems o
1367                                  WHERE p.id = o.parts_id)";
1368   }
1369
1370   if ($form->{itemstatus} eq 'active') {
1371     $where .= " AND p.obsolete = '0'";
1372   }
1373   if ($form->{itemstatus} eq 'obsolete') {
1374     $where .= " AND p.obsolete = '1'";
1375     $form->{onhand} = $form->{short} = 0;
1376   }
1377   if ($form->{itemstatus} eq 'onhand') {
1378     $where .= " AND p.onhand > 0";
1379   }
1380   if ($form->{itemstatus} eq 'short') {
1381     $where .= " AND p.onhand < p.rop";
1382   }
1383   if ($form->{make}) {
1384     $var = $form->like(lc $form->{make});
1385     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
1386                            FROM makemodel m WHERE lower(m.make) LIKE '$var')";
1387   }
1388   if ($form->{model}) {
1389     $var = $form->like(lc $form->{model});
1390     $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
1391                            FROM makemodel m WHERE lower(m.model) LIKE '$var')";
1392   }
1393   if ($form->{partsgroup}) {
1394     $var = $form->like(lc $form->{partsgroup});
1395     $where .= " AND lower(pg.partsgroup) LIKE '$var'";
1396   }
1397
1398
1399   # connect to database
1400   my $dbh = $form->dbconnect_noauto($myconfig);
1401
1402   if ($form->{"sellprice"} ne "") {
1403     my $update = "";
1404     my $faktor = $form->parse_amount($myconfig,$form->{"sellprice"});
1405     if ($form->{"sellprice_type"} eq "percent") {
1406       my $faktor = $form->parse_amount($myconfig,$form->{"sellprice"})/100 +1;
1407       $update = "sellprice* $faktor";
1408     } else {
1409       $update = "sellprice+$faktor";
1410     }
1411   
1412     $query = qq|UPDATE parts set sellprice=$update WHERE id IN (SELECT p.id
1413                   FROM parts p
1414                   LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1415                   WHERE $where)|;
1416     $dbh->do($query);
1417   }
1418
1419   if ($form->{"listprice"} ne "") {
1420     my $update = "";
1421     my $faktor = $form->parse_amount($myconfig,$form->{"listprice"});
1422     if ($form->{"listprice_type"} eq "percent") {
1423       my $faktor = $form->parse_amount($myconfig,$form->{"sellprice"})/100 +1;
1424       $update = "listprice* $faktor";
1425     } else {
1426       $update = "listprice+$faktor";
1427     }
1428   
1429     $query = qq|UPDATE parts set listprice=$update WHERE id IN (SELECT p.id
1430                   FROM parts p
1431                   LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1432                   WHERE $where)|;
1433   
1434     $dbh->do($query);
1435   }
1436
1437
1438
1439
1440   for my $i (1 .. $form->{price_rows}) {
1441
1442     my $query = "";
1443     
1444   
1445     if ($form->{"price_$i"} ne "") {
1446       my $update = "";
1447       my $faktor = $form->parse_amount($myconfig,$form->{"price_$i"});
1448       if ($form->{"pricegroup_type_$i"} eq "percent") {
1449         my $faktor = $form->parse_amount($myconfig,$form->{"sellprice"})/100 +1;
1450         $update = "price* $faktor";
1451       } else {
1452         $update = "price+$faktor";
1453       }
1454     
1455       $query = qq|UPDATE prices set price=$update WHERE parts_id IN (SELECT p.id
1456                     FROM parts p
1457                     LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
1458                     WHERE $where) AND pricegroup_id=$form->{"pricegroup_id_$i"}|;
1459     
1460       $dbh->do($query);
1461     }
1462   }
1463
1464
1465
1466   my $rc= $dbh->commit;
1467   $dbh->disconnect;
1468   $main::lxdebug->leave_sub();
1469
1470   return $rc;
1471 }
1472
1473 sub create_links {
1474   $main::lxdebug->enter_sub();
1475
1476   my ($self, $module, $myconfig, $form) = @_;
1477
1478   # connect to database
1479   my $dbh = $form->dbconnect($myconfig);
1480
1481   if ($form->{id}) {
1482     $query = qq|SELECT c.accno, c.description, c.link, c.id,
1483                         p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
1484                         FROM chart c, parts p
1485                         WHERE c.link LIKE '%$module%'
1486                         AND p.id = $form->{id}
1487                         ORDER BY c.accno|;
1488   } else {
1489     $query = qq|SELECT c.accno, c.description, c.link, c.id,
1490                 d.inventory_accno_id, d.income_accno_id, d.expense_accno_id
1491                 FROM chart c, defaults d
1492                 WHERE c.link LIKE '%$module%'
1493                 ORDER BY c.accno|;
1494   }
1495
1496   my $sth = $dbh->prepare($query);
1497   $sth->execute || $form->dberror($query);
1498   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1499     foreach my $key (split /:/, $ref->{link}) {
1500       if ($key =~ /$module/) {
1501         if (   ($ref->{id} eq $ref->{inventory_accno_id})
1502             || ($ref->{id} eq $ref->{income_accno_id})
1503             || ($ref->{id} eq $ref->{expense_accno_id})) {
1504           push @{ $form->{"${module}_links"}{$key} },
1505             { accno       => $ref->{accno},
1506               description => $ref->{description},
1507               selected    => "selected" };
1508           $form->{"${key}_default"} = "$ref->{accno}--$ref->{description}";
1509             } else {
1510           push @{ $form->{"${module}_links"}{$key} },
1511             { accno       => $ref->{accno},
1512               description => $ref->{description},
1513               selected    => "" };
1514         }
1515       }
1516     }
1517   }
1518   $sth->finish;
1519
1520   # get buchungsgruppen
1521   $query = qq|SELECT id, description
1522               FROM buchungsgruppen|;
1523   $sth = $dbh->prepare($query);
1524   $sth->execute || $form->dberror($query);
1525
1526   $form->{BUCHUNGSGRUPPEN} = [];
1527   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1528     push @{ $form->{BUCHUNGSGRUPPEN} }, $ref;
1529   }
1530   $sth->finish;
1531
1532   # get payment terms
1533   $query = qq|SELECT id, description
1534               FROM payment_terms
1535               ORDER BY sortkey|;
1536   $sth = $dbh->prepare($query);
1537   $sth->execute || $form->dberror($query);
1538
1539   while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
1540     push @{ $self->{payment_terms} }, $ref;
1541   }
1542   $sth->finish;
1543
1544   if (!$form->{id}) {
1545     $query = qq|SELECT current_date FROM defaults|;
1546     $sth = $dbh->prepare($query);
1547     $sth->execute || $form->dberror($query);
1548
1549     ($form->{priceupdate}) = $sth->fetchrow_array;
1550     $sth->finish;
1551   }
1552
1553   $dbh->disconnect;
1554   $main::lxdebug->leave_sub();
1555 }
1556
1557 # get partnumber, description, unit, sellprice and soldtotal with choice through $sortorder for Top100
1558 sub get_parts {
1559   $main::lxdebug->enter_sub();
1560
1561   my ($self, $myconfig, $form, $sortorder) = @_;
1562   my $dbh   = $form->dbconnect($myconfig);
1563   my $order = " p.partnumber";
1564   my $where = "1 = 1";
1565
1566   if ($sortorder eq "all") {
1567     $where .= " AND p.partnumber LIKE '%$form->{partnumber}%'";
1568     $where .= " AND p.description LIKE '%$form->{description}%'";
1569   } else {
1570     if ($sortorder eq "partnumber") {
1571       $where .= " AND p.partnumber LIKE '%$form->{partnumber}%'";
1572       $order = qq|p.$sortorder|;
1573     }
1574     if ($sortorder eq "description") {
1575       $where .= " AND p.description LIKE '%$form->{description}%'";
1576     }
1577   }
1578
1579   my $query =
1580     qq|SELECT p.id, p.partnumber, p.description, p.unit, p.sellprice FROM parts p WHERE $where ORDER BY $order|;
1581   my $sth = $dbh->prepare($query);
1582   $sth->execute || $self->dberror($query);
1583   my $j = 0;
1584   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1585     if (($ref->{partnumber} eq "*") && ($ref->{description} eq "")) {
1586     } else {
1587       $j++;
1588       $form->{"id_$j"}          = $ref->{id};
1589       $form->{"partnumber_$j"}  = $ref->{partnumber};
1590       $form->{"description_$j"} = $ref->{description};
1591       $form->{"unit_$j"}        = $ref->{unit};
1592       $form->{"sellprice_$j"}   = $ref->{sellprice};
1593       $form->{"soldtotal_$j"}   = get_soldtotal($dbh, $ref->{id});
1594     }    #fi
1595   }    #while
1596   $form->{rows} = $j;
1597   $sth->finish;
1598   $dbh->disconnect;
1599
1600   $main::lxdebug->leave_sub();
1601
1602   return $self;
1603 }    #end get_parts()
1604
1605 # gets sum of sold part with part_id
1606 sub get_soldtotal {
1607   $main::lxdebug->enter_sub();
1608
1609   my ($dbh, $id) = @_;
1610
1611   my $query =
1612     qq|SELECT sum(i.qty) as totalsold FROM invoice i WHERE i.parts_id = $id|;
1613
1614   my $sth = $dbh->prepare($query);
1615   $sth->execute || $form->dberror($query);
1616
1617   my $sum = 0;
1618   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1619
1620     $sum = $ref->{totalsold};
1621   }    #while
1622   $sth->finish;
1623
1624   if ($sum eq undef) {
1625     $sum = 0;
1626   }    #fi
1627
1628   $main::lxdebug->leave_sub();
1629
1630   return $sum;
1631 }    #end get_soldtotal
1632
1633 sub retrieve_item {
1634   $main::lxdebug->enter_sub();
1635
1636   my ($self, $myconfig, $form) = @_;
1637   my $i     = $form->{rowcount};
1638   my $where = "NOT p.obsolete = '1'";
1639
1640   if ($form->{"partnumber_$i"}) {
1641     my $partnumber = $form->like(lc $form->{"partnumber_$i"});
1642     $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
1643   }
1644   if ($form->{"description_$i"}) {
1645     my $description = $form->like(lc $form->{"description_$i"});
1646     $where .= " AND lower(p.description) LIKE '$description'";
1647   }
1648
1649   if ($form->{"partsgroup_$i"}) {
1650     my $partsgroup = $form->like(lc $form->{"partsgroup_$i"});
1651     $where .= " AND lower(pg.partsgroup) LIKE '$partsgroup'";
1652   }
1653
1654   if ($form->{"description_$i"}) {
1655     $where .= " ORDER BY description";
1656   } else {
1657     $where .= " ORDER BY partnumber";
1658   }
1659
1660   # connect to database
1661   my $dbh = $form->dbconnect($myconfig);
1662
1663   my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
1664                         p.listprice,
1665                         c1.accno AS inventory_accno,
1666                         c2.accno AS income_accno,
1667                         c3.accno AS expense_accno,
1668                  p.unit, p.assembly, p.bin, p.onhand, p.notes AS partnotes,
1669                  pg.partsgroup
1670                  FROM parts p
1671                  LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
1672                  LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
1673                  LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
1674                  LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
1675                  WHERE $where|;
1676   my $sth = $dbh->prepare($query);
1677   $sth->execute || $form->dberror($query);
1678
1679   #while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1680
1681   # get tax rates and description
1682   #$accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{inventory_accno};
1683   #$query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
1684   #           FROM chart c, tax t
1685   #           WHERE c.id=t.chart_id AND t.taxkey in (SELECT taxkey_id from chart where accno = '$accno_id')
1686   #           ORDER BY accno|;
1687   # $stw = $dbh->prepare($query);
1688   #$stw->execute || $form->dberror($query);
1689
1690   #$ref->{taxaccounts} = "";
1691   #while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
1692
1693   #   $form->{"$ptr->{accno}_rate"} = $ptr->{rate};
1694   #  $form->{"$ptr->{accno}_description"} = $ptr->{description};
1695   #   $form->{"$ptr->{accno}_taxnumber"} = $ptr->{taxnumber};
1696   #   $form->{taxaccounts} .= "$ptr->{accno} ";
1697   #   $ref->{taxaccounts} .= "$ptr->{accno} ";
1698
1699   #}
1700
1701   #$stw->finish;
1702   #chop $ref->{taxaccounts};
1703
1704   push @{ $form->{item_list} }, $ref;
1705
1706   #}
1707   $sth->finish;
1708   $dbh->disconnect;
1709
1710   $main::lxdebug->leave_sub();
1711 }
1712
1713 sub retrieve_languages {
1714   $main::lxdebug->enter_sub();
1715
1716   my ($self, $myconfig, $form) = @_;
1717
1718   # connect to database
1719   my $dbh = $form->dbconnect($myconfig);
1720
1721   if ($form->{id}) {
1722     $where .= "tr.parts_id=$form->{id}";
1723   }
1724
1725
1726   if ($form->{language_values} ne "") {
1727   $query = qq|SELECT l.id, l.description, tr.translation, tr.longdescription
1728                  FROM language l LEFT OUTER JOIN translation tr ON (tr.language_id=l.id AND $where)|;
1729   } else {
1730   $query = qq|SELECT l.id, l.description
1731                  FROM language l|;
1732   }
1733   my $sth = $dbh->prepare($query);
1734   $sth->execute || $form->dberror($query);
1735
1736   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
1737     push(@{$languages}, $ref);
1738   }
1739   $sth->finish;
1740
1741   $dbh->disconnect;
1742
1743   $main::lxdebug->leave_sub();
1744   return $languages;
1745
1746 }
1747
1748 sub follow_account_chain {
1749   $main::lxdebug->enter_sub(2);
1750
1751   my ($self, $form, $dbh, $transdate, $accno_id, $accno) = @_;
1752
1753   my @visited_accno_ids = ($accno_id);
1754
1755   my ($query, $sth);
1756
1757   $query =
1758     "SELECT c.new_chart_id, date($transdate) >= c.valid_from AS is_valid, " .
1759     "  cnew.accno " .
1760     "FROM chart c " .
1761     "LEFT JOIN chart cnew ON c.new_chart_id = cnew.id " .
1762     "WHERE (c.id = ?) AND NOT c.new_chart_id ISNULL AND (c.new_chart_id > 0)";
1763   $sth = $dbh->prepare($query);
1764
1765   while (1) {
1766     $sth->execute($accno_id) || $form->dberror($query . " ($accno_id)");
1767     $ref = $sth->fetchrow_hashref();
1768     last unless ($ref && $ref->{"is_valid"} &&
1769                  !grep({ $_ == $ref->{"new_chart_id"} } @visited_accno_ids));
1770     $accno_id = $ref->{"new_chart_id"};
1771     $accno = $ref->{"accno"};
1772     push(@visited_accno_ids, $accno_id);
1773   }
1774
1775   $main::lxdebug->leave_sub(2);
1776
1777   return ($accno_id, $accno);
1778 }
1779
1780 sub retrieve_accounts {
1781   $main::lxdebug->enter_sub(2);
1782
1783   my ($self, $myconfig, $form, $parts_id, $index, $copy_accnos) = @_;
1784
1785   my ($query, $sth, $dbh);
1786
1787   $form->{"taxzone_id"} *= 1;
1788
1789   $dbh = $form->dbconnect($myconfig);
1790
1791   my $transdate = "";
1792   if ($form->{type} eq "invoice") {
1793     if (($form->{vc} eq "vendor") || !$form->{deliverydate}) {
1794       $transdate = $form->{invdate};
1795     } else {
1796       $transdate = $form->{deliverydate};
1797     }
1798   } elsif ($form->{type} eq "credit_note") {
1799     $transdate = $form->{invdate};
1800   } else {
1801     $transdate = $form->{transdate};
1802   }
1803
1804   if ($transdate eq "") {
1805     $transdate = "current_date";
1806   } else {
1807     $transdate = $dbh->quote($transdate);
1808   }
1809
1810   $query =
1811     "SELECT " .
1812     "  p.inventory_accno_id AS is_part, " .
1813     "  bg.inventory_accno_id, " .
1814     "  bg.income_accno_id_$form->{taxzone_id} AS income_accno_id, " .
1815     "  bg.expense_accno_id_$form->{taxzone_id} AS expense_accno_id, " .
1816     "  c1.accno AS inventory_accno, " .
1817     "  c2.accno AS income_accno, " .
1818     "  c3.accno AS expense_accno " .
1819     "FROM parts p " .
1820     "LEFT JOIN buchungsgruppen bg ON p.buchungsgruppen_id = bg.id " .
1821     "LEFT JOIN chart c1 ON bg.inventory_accno_id = c1.id " .
1822     "LEFT JOIN chart c2 ON bg.income_accno_id_$form->{taxzone_id} = c2.id " .
1823     "LEFT JOIN chart c3 ON bg.expense_accno_id_$form->{taxzone_id} = c3.id " .
1824     "WHERE p.id = ?";
1825   $sth = $dbh->prepare($query);
1826   $sth->execute($parts_id) || $form->dberror($query . " ($parts_id)");
1827   my $ref = $sth->fetchrow_hashref();
1828   $sth->finish();
1829
1830 #   $main::lxdebug->message(0, "q $query");
1831
1832   if (!$ref) {
1833     $dbh->disconnect();
1834     return $main::lxdebug->leave_sub(2);
1835   }
1836
1837   $ref->{"inventory_accno_id"} = undef unless ($ref->{"is_part"});
1838
1839   my %accounts;
1840   foreach my $type (qw(inventory income expense)) {
1841     next unless ($ref->{"${type}_accno_id"});
1842     ($accounts{"${type}_accno_id"}, $accounts{"${type}_accno"}) =
1843       $self->follow_account_chain($form, $dbh, $transdate,
1844                                   $ref->{"${type}_accno_id"},
1845                                   $ref->{"${type}_accno"});
1846   }
1847
1848   map({ $form->{"${_}_accno_$index"} = $accounts{"${_}_accno"} }
1849       qw(inventory income expense));
1850
1851   my $inc_exp = $form->{"vc"} eq "customer" ? "income" : "expense";
1852   my $accno_id = $accounts{"${inc_exp}_accno_id"};
1853
1854   $query =
1855     "SELECT c.accno, t.taxdescription AS description, t.rate, t.taxnumber " .
1856     "FROM tax t " .
1857     "LEFT JOIN chart c ON c.id = t.chart_id " .
1858     "WHERE t.id IN " .
1859     "  (SELECT tk.tax_id " .
1860     "   FROM taxkeys tk " .
1861     "   WHERE tk.chart_id = ? AND startdate <= " . quote_db_date($transdate) .
1862     "   ORDER BY startdate DESC LIMIT 1) ";
1863   @vars = ($accno_id);
1864   $sth = $dbh->prepare($query);
1865   $sth->execute(@vars) || $form->dberror("$query (" . join(', ', @vars) . ")");
1866   $ref = $sth->fetchrow_hashref();
1867   $sth->finish();
1868   $dbh->disconnect();
1869
1870   unless ($ref) {
1871     $main::lxdebug->leave_sub(2);
1872     return;
1873   }
1874
1875   $form->{"taxaccounts_$index"} = $ref->{"accno"};
1876   if ($form->{"taxaccounts"} !~ /$ref->{accno}/) {
1877     $form->{"taxaccounts"} .= "$ref->{accno} ";
1878   }
1879   map({ $form->{"$ref->{accno}_${_}"} = $ref->{$_}; }
1880       qw(rate description taxnumber));
1881
1882 #   $main::lxdebug->message(0, "formvars: rate " . $form->{"$ref->{accno}_rate"} .
1883 #                           " description " . $form->{"$ref->{accno}_description"} .
1884 #                           " taxnumber " . $form->{"$ref->{accno}_taxnumber"} .
1885 #                           " || taxaccounts_$index " . $form->{"taxaccounts_$index"} .
1886 #                           " || taxaccounts " . $form->{"taxaccounts"});
1887
1888   $main::lxdebug->leave_sub(2);
1889 }
1890
1891 1;