1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger Accounting
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  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.
 
  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 #======================================================================
 
  31 # Inventory Control backend
 
  33 #======================================================================
 
  40   $main::lxdebug->enter_sub();
 
  42   my ($self, $myconfig, $form) = @_;
 
  45   my $dbh = $form->dbconnect($myconfig);
 
  51          c1.accno AS inventory_accno,
 
  52          c2.accno AS income_accno,
 
  53          c3.accno AS expense_accno,
 
  56        LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
 
  57        LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
 
  58        LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
 
  59        LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
  61   my $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
 
  63   # copy to $form variables
 
  64   map { $form->{$_} = $ref->{$_} } (keys %{$ref});
 
  66   my %oid = ('Pg'     => 'a.oid',
 
  67              'Oracle' => 'a.rowid');
 
  69   # part or service item
 
  70   $form->{item} = ($form->{inventory_accno}) ? 'part' : 'service';
 
  71   if ($form->{assembly}) {
 
  72     $form->{item} = 'assembly';
 
  74     # retrieve assembly items
 
  76       qq|SELECT p.id, p.partnumber, p.description,
 
  77            p.sellprice, p.weight, a.qty, a.bom, p.unit,
 
  80          JOIN assembly a ON (a.parts_id = p.id)
 
  81          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
  83          ORDER BY $oid{$myconfig->{dbdriver}}|;
 
  84     $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
  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};
 
  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};
 
 104   my @pricegroups          = ();
 
 105   my @pricegroups_not_used = ();
 
 109     qq|SELECT p.parts_id, p.pricegroup_id, p.price,
 
 110          (SELECT pg.pricegroup
 
 112           WHERE pg.id = p.pricegroup_id) AS pricegroup
 
 115        ORDER BY pricegroup|;
 
 116   $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
 120   while (($form->{"klass_$i"}, $form->{"pricegroup_id_$i"},
 
 121           $form->{"price_$i"}, $form->{"pricegroup_$i"})
 
 122          = $sth->fetchrow_array()) {
 
 123     $form->{"price_$i"} = $form->round_amount($form->{"price_$i"}, 5);
 
 124     $form->{"price_$i"} = $form->format_amount($myconfig, $form->{"price_$i"}, -2);
 
 125     push @pricegroups, $form->{"pricegroup_id_$i"};
 
 132   $query = qq|SELECT id, pricegroup FROM pricegroup|;
 
 133   $form->{PRICEGROUPS} = selectall_hashref_query($form, $dbh, $query);
 
 135   #find not used pricegroups
 
 136   while ($tmp = pop(@{ $form->{PRICEGROUPS} })) {
 
 138     foreach my $item (@pricegroups) {
 
 139       if ($item eq $tmp->{id}) {
 
 144     push(@pricegroups_not_used, $tmp) unless ($in_use);
 
 147   # if not used pricegroups are avaible
 
 148   if (@pricegroups_not_used) {
 
 150     foreach $name (@pricegroups_not_used) {
 
 151       $form->{"klass_$i"} = "$name->{id}";
 
 152       $form->{"price_$i"} = $form->round_amount($form->{sellprice}, 5);
 
 153       $form->{"price_$i"} = $form->format_amount($myconfig, $form->{"price_$i"}, -2);
 
 154       $form->{"pricegroup_id_$i"} = "$name->{id}";
 
 155       $form->{"pricegroup_$i"}    = "$name->{pricegroup}";
 
 161   $form->{price_rows} = $i - 1;
 
 163   unless ($form->{item} eq 'service') {
 
 166     if ($form->{makemodel}) {
 
 167       $query = qq|SELECT m.make, m.model FROM makemodel m | .
 
 168                qq|WHERE m.parts_id = ?|;
 
 169       @values = ($form->{id});
 
 170       $sth = $dbh->prepare($query);
 
 171       $sth->execute(@values) || $form->dberror("$query (" . join(', ', @values) . ")");
 
 174       while (($form->{"make_$i"}, $form->{"model_$i"}) = $sth->fetchrow_array)
 
 179       $form->{makemodel_rows} = $i - 1;
 
 185   $form->{language_values} = "";
 
 186   $query = qq|SELECT language_id, translation FROM translation WHERE parts_id = ?|;
 
 187   my $trq = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
 188   while ($tr = $trq->fetchrow_hashref(NAME_lc)) {
 
 189     $form->{language_values} .= "---+++---".$tr->{language_id}."--++--".$tr->{translation};
 
 193   # now get accno for taxes
 
 196        FROM chart c, partstax pt
 
 197        WHERE (pt.chart_id = c.id) AND (pt.parts_id = ?)|;
 
 198   $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
 199   while (($key) = $sth->fetchrow_array) {
 
 200     $form->{amount}{$key} = $key;
 
 209        WHERE (i.parts_id = ?)
 
 215        WHERE (o.parts_id = ?)
 
 221        WHERE (a.parts_id = ?)|;
 
 222   @values = (conv_i($form->{id}), conv_i($form->{id}), conv_i($form->{id}));
 
 223   ($form->{orphaned}) = selectrow_query($form, $dbh, $query, @values);
 
 224   $form->{orphaned} = !$form->{orphaned};
 
 226   $form->{"unit_changeable"} = 1;
 
 227   foreach my $table (qw(invoice assembly orderitems inventory license)) {
 
 228     $query = qq|SELECT COUNT(*) FROM $table WHERE parts_id = ?|;
 
 229     my ($count) = selectrow_query($form, $dbh, $query, conv_i($form->{"id"}));
 
 232       $form->{"unit_changeable"} = 0;
 
 239   $main::lxdebug->leave_sub();
 
 242 sub get_pricegroups {
 
 243   $main::lxdebug->enter_sub();
 
 245   my ($self, $myconfig, $form) = @_;
 
 247   my $dbh = $form->dbconnect($myconfig);
 
 250   my $query = qq|SELECT id, pricegroup FROM pricegroup|;
 
 251   my $pricegroups = selectall_hashref_query($form, $dbh, $query);
 
 254   foreach $pg (@{ $pricegroups }) {
 
 255     $form->{"klass_$i"} = "$pg->{id}";
 
 256     $form->{"price_$i"} = $form->format_amount($myconfig, $form->{"price_$i"}, -2);
 
 257     $form->{"pricegroup_id_$i"} = "$pg->{id}";
 
 258     $form->{"pricegroup_$i"}    = "$pg->{pricegroup}";
 
 263   $form->{price_rows} = $i - 1;
 
 267   $main::lxdebug->leave_sub();
 
 270 sub retrieve_buchungsgruppen {
 
 271   $main::lxdebug->enter_sub();
 
 273   my ($self, $myconfig, $form) = @_;
 
 277   my $dbh = $form->dbconnect($myconfig);
 
 279   # get buchungsgruppen
 
 280   $query = qq|SELECT id, description FROM buchungsgruppen ORDER BY sortkey|;
 
 281   $form->{BUCHUNGSGRUPPEN} = selectall_hashref_query($form, $dbh, $query);
 
 283   $main::lxdebug->leave_sub();
 
 287   $main::lxdebug->enter_sub();
 
 289   my ($self, $myconfig, $form) = @_;
 
 291   # connect to database, turn off AutoCommit
 
 292   my $dbh = $form->dbconnect_noauto($myconfig);
 
 295   # make up a unique handle and store in partnumber field
 
 296   # then retrieve the record based on the unique handle to get the id
 
 297   # replace the partnumber field with the actual variable
 
 298   # add records for makemodel
 
 300   # if there is a $form->{id} then replace the old entry
 
 301   # delete all makemodel entries and add the new ones
 
 303   # undo amount formatting
 
 304   map { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
 
 305     qw(rop weight listprice sellprice gv lastcost stock);
 
 307   my $makemodel = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
 
 309   $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
 
 316     $query = qq|SELECT sellprice, weight FROM parts WHERE id = ?|;
 
 317     my ($sellprice, $weight) = selectrow_query($form, $dbh, $query, conv_i($form->{id}));
 
 319     # if item is part of an assembly adjust all assemblies
 
 320     $query = qq|SELECT id, qty FROM assembly WHERE parts_id = ?|;
 
 321     $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
 322     while (my ($id, $qty) = $sth->fetchrow_array) {
 
 323       &update_assembly($dbh, $form, $id, $qty, $sellprice * 1, $weight * 1);
 
 327     if ($form->{item} ne 'service') {
 
 328       # delete makemodel records
 
 329       do_query($form, $dbh, qq|DELETE FROM makemodel WHERE parts_id = ?|, conv_i($form->{id}));
 
 332     if ($form->{item} eq 'assembly') {
 
 333       if ($form->{onhand} != 0) {
 
 334         &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
 
 337       # delete assembly records
 
 338       do_query($form, $dbh, qq|DELETE FROM assembly WHERE id = ?|, conv_i($form->{id}));
 
 340       $form->{onhand} += $form->{stock};
 
 344     do_query($form, $dbh, qq|DELETE FROM partstax WHERE parts_id = ?|, conv_i($form->{id}));
 
 346     # delete translations
 
 347     do_query($form, $dbh, qq|DELETE FROM translation WHERE parts_id = ?|, conv_i($form->{id}));
 
 350     my ($count) = selectrow_query($form, $dbh, qq|SELECT COUNT(*) FROM parts WHERE partnumber = ?|, $form->{partnumber});
 
 352       $main::lxdebug->leave_sub();
 
 356     ($form->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('id')|);
 
 357     do_query($form, $dbh, qq|INSERT INTO parts (id, partnumber) VALUES (?, '')|, $form->{id});
 
 359     $form->{orphaned} = 1;
 
 360     $form->{onhand} = $form->{stock} if $form->{item} eq 'assembly';
 
 361     if ($form->{partnumber} eq "" && $form->{"item"} eq "service") {
 
 362       $form->{partnumber} = $form->update_defaults($myconfig, "servicenumber");
 
 364     if ($form->{partnumber} eq "" && $form->{"item"} ne "service") {
 
 365       $form->{partnumber} = $form->update_defaults($myconfig, "articlenumber");
 
 369   my $partsgroup_id = 0;
 
 371   if ($form->{partsgroup}) {
 
 372     ($partsgroup, $partsgroup_id) = split(/--/, $form->{partsgroup});
 
 375   my ($subq_inventory, $subq_expense, $subq_income);
 
 376   if ($form->{"item"} eq "part") {
 
 378       qq|(SELECT bg.inventory_accno_id
 
 379           FROM buchungsgruppen bg
 
 380           WHERE bg.id = | . conv_i($form->{"buchungsgruppen_id"}, 'NULL') . qq|)|;
 
 382     $subq_inventory = "NULL";
 
 385   if ($form->{"item"} ne "assembly") {
 
 387       qq|(SELECT bg.expense_accno_id_0
 
 388           FROM buchungsgruppen bg
 
 389           WHERE bg.id = | . conv_i($form->{"buchungsgruppen_id"}, 'NULL') . qq|)|;
 
 391     $subq_expense = "NULL";
 
 411          buchungsgruppen_id = ?,
 
 413          inventory_accno_id = $subq_inventory,
 
 414          income_accno_id = (SELECT bg.income_accno_id_0 FROM buchungsgruppen bg WHERE bg.id = ?),
 
 415          expense_accno_id = $subq_expense,
 
 423          not_discountable = ?,
 
 427   @values = ($form->{partnumber},
 
 428              $form->{description},
 
 429              $makemodel ? 't' : 'f',
 
 430              $form->{assembly} ? 't' : 'f',
 
 435              conv_date($form->{priceupdate}),
 
 441              conv_i($form->{buchungsgruppen_id}),
 
 442              conv_i($form->{payment_id}),
 
 443              conv_i($form->{buchungsgruppen_id}),
 
 444              $form->{obsolete} ? 't' : 'f',
 
 447              $form->{shop} ? 't' : 'f',
 
 451              $form->{not_discountable} ? 't' : 'f',
 
 453              conv_i($partsgroup_id),
 
 456   do_query($form, $dbh, $query, @values);
 
 458   # delete translation records
 
 459   do_query($form, $dbh, qq|DELETE FROM translation WHERE parts_id = ?|, conv_i($form->{id}));
 
 461   if ($form->{language_values} ne "") {
 
 462     foreach $item (split(/---\+\+\+---/, $form->{language_values})) {
 
 463       my ($language_id, $translation, $longdescription) = split(/--\+\+--/, $item);
 
 464       if ($translation ne "") {
 
 465         $query = qq|INSERT into translation (parts_id, language_id, translation, longdescription)
 
 466                     VALUES ( ?, ?, ?, ? )|;
 
 467         @values = (conv_i($form->{id}), conv_i($language_id), $translation, $longdescription);
 
 468         do_query($form, $dbh, $query, @values);
 
 473   # delete price records
 
 474   do_query($form, $dbh, qq|DELETE FROM prices WHERE parts_id = ?|, conv_i($form->{id}));
 
 476   # insert price records only if different to sellprice
 
 477   for my $i (1 .. $form->{price_rows}) {
 
 478     if ($form->{"price_$i"} eq "0") {
 
 479       $form->{"price_$i"} = $form->{sellprice};
 
 482         (   $form->{"price_$i"}
 
 483          || $form->{"klass_$i"}
 
 484          || $form->{"pricegroup_id_$i"})
 
 485         and $form->{"price_$i"} != $form->{sellprice}
 
 487       #$klass = $form->parse_amount($myconfig, $form->{"klass_$i"});
 
 488       $price = $form->parse_amount($myconfig, $form->{"price_$i"});
 
 490         $form->parse_amount($myconfig, $form->{"pricegroup_id_$i"});
 
 491       $query = qq|INSERT INTO prices (parts_id, pricegroup_id, price) | .
 
 493       @values = (conv_i($form->{id}), conv_i($pricegroup_id), $price);
 
 494       do_query($form, $dbh, $query, @values);
 
 498   # insert makemodel records
 
 499   unless ($form->{item} eq 'service') {
 
 500     for my $i (1 .. $form->{makemodel_rows}) {
 
 501       if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
 
 502         map { $form->{"${_}_$i"} =~ s/\'/\'\'/g } qw(make model);
 
 504         $query = qq|INSERT INTO makemodel (parts_id, make, model) | .
 
 505                              qq|VALUES (?, ?, ?)|;
 
 506                     @values = (conv_i($form->{id}), $form->{"make_$i"}, $form->{"model_$i"});
 
 507         do_query($form, $dbh, $query, @values);
 
 513   foreach $item (split(/ /, $form->{taxaccounts})) {
 
 514     if ($form->{"IC_tax_$item"}) {
 
 516         qq|INSERT INTO partstax (parts_id, chart_id)
 
 517            VALUES (?, (SELECT id FROM chart WHERE accno = ?))|;
 
 518                         @values = (conv_i($form->{id}), $item);
 
 519       do_query($form, $dbh, $query, @values);
 
 523   # add assembly records
 
 524   if ($form->{item} eq 'assembly') {
 
 526     for my $i (1 .. $form->{assembly_rows}) {
 
 527       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
 529       if ($form->{"qty_$i"} != 0) {
 
 530         $form->{"bom_$i"} *= 1;
 
 531         $query = qq|INSERT INTO assembly (id, parts_id, qty, bom) | .
 
 532                              qq|VALUES (?, ?, ?, ?)|;
 
 533                     @values = (conv_i($form->{id}), conv_i($form->{"id_$i"}), conv_i($form->{"qty_$i"}), $form->{"bom_$i"} ? 't' : 'f');
 
 534         do_query($form, $dbh, $query, @values);
 
 538     # adjust onhand for the parts
 
 539     if ($form->{onhand} != 0) {
 
 540       &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
 
 546     my $shippingdate = "$a[5]-$a[4]-$a[3]";
 
 548     $form->get_employee($dbh);
 
 550     # add inventory record
 
 552       qq|INSERT INTO inventory (warehouse_id, parts_id, qty, shippingdate, employee_id)
 
 553          VALUES (0, ?, ?, '$shippingdate', ?)|;
 
 554     @values = (conv_i($form->{id}), $form->{stock}, conv_i($form->{employee_id}));
 
 555     do_query($form, $dbh, $query, @values);
 
 559   #set expense_accno=inventory_accno if they are different => bilanz
 
 561     ($form->{expense_accno} != $form->{inventory_accno})
 
 562     ? $form->{inventory_accno}
 
 563     : $form->{expense_accno};
 
 565   # get tax rates and description
 
 567     ($form->{vc} eq "customer") ? $form->{income_accno} : $vendor_accno;
 
 569     qq|SELECT c.accno, c.description, t.rate, t.taxnumber
 
 571        WHERE (c.id = t.chart_id) AND (t.taxkey IN (SELECT taxkey_id FROM chart where accno = ?))
 
 573   $stw = prepare_execute_query($form, $dbh, $query, $accno_id);
 
 575   $form->{taxaccount} = "";
 
 576   while ($ptr = $stw->fetchrow_hashref(NAME_lc)) {
 
 577     $form->{taxaccount} .= "$ptr->{accno} ";
 
 578     if (!($form->{taxaccount2} =~ /$ptr->{accno}/)) {
 
 579       $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
 
 580       $form->{"$ptr->{accno}_description"} = $ptr->{description};
 
 581       $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
 
 582       $form->{taxaccount2} .= " $ptr->{accno} ";
 
 587   my $rc = $dbh->commit;
 
 590   $main::lxdebug->leave_sub();
 
 595 sub update_assembly {
 
 596   $main::lxdebug->enter_sub();
 
 598   my ($dbh, $form, $id, $qty, $sellprice, $weight) = @_;
 
 600   my $query = qq|SELECT id, qty FROM assembly WHERE parts_id = ?|;
 
 601   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
 
 603   while (my ($pid, $aqty) = $sth->fetchrow_array) {
 
 604     &update_assembly($dbh, $form, $pid, $aqty * $qty, $sellprice, $weight);
 
 609     qq|UPDATE parts SET sellprice = sellprice + ?, weight = weight + ?
 
 611   @values = ($qty * ($form->{sellprice} - $sellprice),
 
 612              $qty * ($form->{weight} - $weight), conv_i($id));
 
 613   do_query($form, $dbh, $query, @values);
 
 615   $main::lxdebug->leave_sub();
 
 618 sub retrieve_assemblies {
 
 619   $main::lxdebug->enter_sub();
 
 621   my ($self, $myconfig, $form) = @_;
 
 623   # connect to database
 
 624   my $dbh = $form->dbconnect($myconfig);
 
 626   my $where = qq|NOT p.obsolete|;
 
 629   if ($form->{partnumber}) {
 
 630     $where .= qq| AND (p.partnumber ILIKE ?)|;
 
 631     push(@values, '%' . $form->{partnumber} . '%');
 
 634   if ($form->{description}) {
 
 635     $where .= qq| AND (p.description ILIKE ?)|;
 
 636     push(@values, '%' . $form->{description} . '%');
 
 639   # retrieve assembly items
 
 641     qq|SELECT p.id, p.partnumber, p.description,
 
 642          p.bin, p.onhand, p.rop,
 
 643          (SELECT sum(p2.inventory_accno_id)
 
 644           FROM parts p2, assembly a
 
 645           WHERE (p2.id = a.parts_id) AND (a.id = p.id)) AS inventory
 
 647        WHERE NOT p.obsolete AND p.assembly $where|;
 
 649   $form->{assembly_items} = selectall_hashref_query($form, $dbh, $query, @values);
 
 653   $main::lxdebug->leave_sub();
 
 656 sub restock_assemblies {
 
 657   $main::lxdebug->enter_sub();
 
 659   my ($self, $myconfig, $form) = @_;
 
 661   # connect to database
 
 662   my $dbh = $form->dbconnect_noauto($myconfig);
 
 664   for my $i (1 .. $form->{rowcount}) {
 
 666     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
 
 668     if ($form->{"qty_$i"} != 0) {
 
 669       &adjust_inventory($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
 
 674   my $rc = $dbh->commit;
 
 677   $main::lxdebug->leave_sub();
 
 682 sub adjust_inventory {
 
 683   $main::lxdebug->enter_sub();
 
 685   my ($dbh, $form, $id, $qty) = @_;
 
 688     qq|SELECT p.id, p.inventory_accno_id, p.assembly, a.qty
 
 689        FROM parts p, assembly a
 
 690        WHERE (a.parts_id = p.id) AND (a.id = ?)|;
 
 691   my $sth = prepare_execute_query($form, $dbh, $query, conv_i($id));
 
 693   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
 
 695     my $allocate = $qty * $ref->{qty};
 
 697     # is it a service item, then loop
 
 698     $ref->{inventory_accno_id} *= 1;
 
 699     next if (($ref->{inventory_accno_id} == 0) && !$ref->{assembly});
 
 701     # adjust parts onhand
 
 702     $form->update_balance($dbh, "parts", "onhand",
 
 710   my $rc = $form->update_balance($dbh, "parts", "onhand", qq|id = ?|, $qty, $id);
 
 712   $main::lxdebug->leave_sub();
 
 718   $main::lxdebug->enter_sub();
 
 720   my ($self, $myconfig, $form) = @_;
 
 721   my @values = (conv_i($form->{id}));
 
 722   # connect to database, turn off AutoCommit
 
 723   my $dbh = $form->dbconnect_noauto($myconfig);
 
 725   my %columns = ( "assembly" => "id", "alternate" => "id", "parts" => "id" );
 
 727   for my $table (qw(prices partstax makemodel inventory assembly parts)) {
 
 728     my $column = defined($columns{$table}) ? $columns{$table} : "parts_id";
 
 729     do_query($form, $dbh, qq|DELETE FROM $table WHERE $column = ?|, @values);
 
 733   my $rc = $dbh->commit;
 
 736   $main::lxdebug->leave_sub();
 
 742   $main::lxdebug->enter_sub();
 
 744   my ($self, $myconfig, $form) = @_;
 
 746   my $i = $form->{assembly_rows};
 
 748   my $where = qq|1 = 1|;
 
 751   my %columns = ("partnumber" => "p", "description" => "p", "partsgroup" => "pg");
 
 753   while (my ($column, $table) = each(%columns)) {
 
 754     next unless ($form->{"${column}_$i"});
 
 755     $where .= qq| AND ${table}.${column} ILIKE ?|;
 
 756     push(@values, '%' . $form->{"${column}_$i"} . '%');
 
 760     $where .= qq| AND NOT (p.id = ?)|;
 
 761     push(@values, conv_i($form->{id}));
 
 765     $where .= qq| ORDER BY p.partnumber|;
 
 767     $where .= qq| ORDER BY p.description|;
 
 770   # connect to database
 
 771   my $dbh = $form->dbconnect($myconfig);
 
 774     qq|SELECT p.id, p.partnumber, p.description, p.sellprice, p.weight, p.onhand, p.unit, pg.partsgroup
 
 776        LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
 778   $form->{item_list} = selectall_hashref_query($form, $dbh, $query, @values);
 
 782   $main::lxdebug->leave_sub();
 
 787 # Warning, deep magic ahead.
 
 788 # This function gets all parts from the database according to the filters specified
 
 791 #   partnumber ean description partsgroup serialnumber make model drawing microfiche
 
 792 #   transdatefrom transdateto sort
 
 795 #   itemstatus  = active | onhand | short | obsolete | orphaned
 
 796 #   searchitems = part | assembly | service
 
 799 #   l_partnumber l_description l_listprice l_sellprice l_lastcost l_priceupdate l_weight l_unit l_bin l_rop l_image l_drawing l_microfiche l_partsgroup 
 
 802 #   bought sold onorder ordered rfq quoted onhand short
 
 803 #   l_serialnumber l_linetotal l_subtotal l_soldtotal l_deliverydate
 
 807   $main::lxdebug->enter_sub();
 
 809   my ($self, $myconfig, $form) = @_;
 
 811   my $where = qq|1 = 1|;
 
 812   my (@values, $var, $flds, $group, $limit);
 
 814   foreach my $item (qw(partnumber drawing microfiche ean pg.partsgroup)) {
 
 816     $column =~ s/.*\.//; # get rid of table prefixes
 
 817     if ($form->{$column}) {
 
 818       $where .= qq| AND ($item ILIKE ?)|;
 
 819       push(@values, "%$form->{$column}%");
 
 823   # special case for description
 
 824   if ($form->{description}
 
 825       && !(   $form->{bought}  || $form->{sold} || $form->{onorder}
 
 826            || $form->{ordered} || $form->{rfq} || $form->{quoted})) {
 
 827     $where .= qq| AND (p.description ILIKE ?)|;
 
 828     push(@values, "%$form->{description}%");
 
 831   # special case for serialnumber
 
 832   if ($form->{l_serialnumber} && $form->{serialnumber}) {
 
 833     $where .= qq| AND (serialnumber ILIKE ?)|;
 
 834     push(@values, "%$form->{serialnumber}%");
 
 837   if ($form->{searchitems} eq 'part') {
 
 838     $where .= qq| AND (p.inventory_accno_id > 0) |;
 
 841   if ($form->{searchitems} eq 'assembly') {
 
 842     $form->{bought} = "";
 
 843     $where .= qq| AND p.assembly|;
 
 846   if ($form->{searchitems} eq 'service') {
 
 847     $where .= qq| AND (p.inventory_accno_id IS NULL) AND NOT (p.assembly = '1')|;
 
 848     # irrelevant for services
 
 849     map { $form->{$_} = '' } qw(make model);
 
 852   # items which were never bought, sold or on an order
 
 853   if ($form->{itemstatus} eq 'orphaned') {
 
 854     map { $form->{$_} = 0  } qw(onhand short bought sold onorder ordered rfq quoted);
 
 855     map { $form->{$_} = '' } qw(transdatefrom transdateto);
 
 858       qq| AND (p.onhand = 0)
 
 861               SELECT DISTINCT parts_id FROM invoice
 
 863               SELECT DISTINCT parts_id FROM assembly
 
 865               SELECT DISTINCT parts_id FROM orderitems
 
 869   my %status2condition = (
 
 870     active   => " AND (p.obsolete = '0')",
 
 871     obsolete => " AND (p.obsolete = '1')",
 
 872     onhand   => " AND (p.onhand > 0)",
 
 873     short    => " AND (p.onhand < p.rop)",
 
 875   $where .= $status2condition{$form->{itemstatus}};
 
 877   $form->{onhand} = $form->{short} = 0 if ($form->{itemstatus} eq 'obsolete');
 
 880   foreach my $column (qw(make model)) {
 
 881     push @subcolumns, $column if $form->{$column};
 
 884     $where .= qq| AND p.id IN (SELECT DISTINCT parts_id FROM makemodel WHERE | . (join " AND ", map { "($_ ILIKE ?)"; } @subcolumns) . ")";
 
 885     push @values, map { '%' . $form->{$_} . '%' } @subcolumns;
 
 888   if ($form->{l_soldtotal}) {
 
 889     $where .= qq| AND (p.id = i.parts_id) AND (i.qty >= 0)|;
 
 890     $group = qq| GROUP BY p.id, p.partnumber, p.description, p.onhand, p.unit, p.bin, p.sellprice, p.listprice, p.lastcost, p.priceupdate, pg.partsgroup|;
 
 893   $limit = qq| LIMIT 100| if ($form->{top100});
 
 895   # connect to database
 
 896   my $dbh = $form->dbconnect($myconfig);
 
 898   my @sort_cols = qw(id partnumber description partsgroup bin priceupdate onhand
 
 899                      invnumber ordnumber quonumber name drawing microfiche
 
 900                      serialnumber soldtotal deliverydate);
 
 902   my $sortorder = "partnumber";
 
 903   $sortorder = $form->{sort} if ($form->{sort} && grep({ $_ eq $form->{sort} } @sort_cols));
 
 904   $sortorder .= " DESC" if ($form->{revers});
 
 908   if ($form->{l_soldtotal}) {
 
 909     $form->{soldtotal} = 'soldtotal';
 
 911       qq|SELECT p.id, p.partnumber, p.description, p.onhand, p.unit, 
 
 912            p.bin, p.sellprice, p.listprice, p.lastcost, 
 
 913            p.priceupdate, pg.partsgroup,sum(i.qty) AS soldtotal 
 
 915          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id), invoice i
 
 918          ORDER BY $sortorder $limit|;
 
 921       qq|SELECT p.id, p.partnumber, p.description, p.onhand, p.unit,
 
 922            p.bin, p.sellprice, p.listprice, p.lastcost, p.rop, p.weight,
 
 923            p.priceupdate, p.image, p.drawing, p.microfiche,
 
 926          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
 929          ORDER BY $sortorder $limit|;
 
 932   my @all_values = @values;
 
 934   # rebuild query for bought and sold items
 
 940       || $form->{quoted}) {
 
 945     if ($form->{bought} || $form->{sold}) {
 
 947       my @invvalues = @values;
 
 948       my $invwhere = "$where";
 
 949       $invwhere .= qq| AND i.assemblyitem = '0'|;
 
 951       if ($form->{transdatefrom}) {
 
 952         $invwhere .= qq| AND a.transdate >= ?|;
 
 953         push(@invvalues, $form->{transdatefrom});
 
 956       if ($form->{transdateto}) {
 
 957         $invwhere .= qq| AND a.transdate <= ?|;
 
 958         push(@invvalues, $form->{transdateto});
 
 961       if ($form->{description}) {
 
 962         $invwhere .= qq| AND i.description ILIKE ?|;
 
 963         push(@invvalues, '%' . $form->{description} . '%');
 
 967         qq|p.id, p.partnumber, i.description, i.serialnumber,
 
 968            i.qty AS onhand, i.unit, p.bin, i.sellprice,
 
 969            p.listprice, p.lastcost, p.rop, p.weight,
 
 970            p.priceupdate, p.image, p.drawing, p.microfiche,
 
 972            a.invnumber, a.ordnumber, a.quonumber, i.trans_id,
 
 973            ct.name, i.deliverydate|;
 
 975       if ($form->{bought}) {
 
 977           qq|SELECT $flds, 'ir' AS module, '' AS type, 1 AS exchangerate
 
 979              JOIN parts p ON (p.id = i.parts_id)
 
 980              JOIN ap a ON (a.id = i.trans_id)
 
 981              JOIN vendor ct ON (a.vendor_id = ct.id)
 
 982              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
 985         $union = qq| UNION |;
 
 987         push(@all_values, @invvalues);
 
 994              SELECT $flds, 'is' AS module, '' AS type, 1 As exchangerate
 
 996              JOIN parts p ON (p.id = i.parts_id)
 
 997              JOIN ar a ON (a.id = i.trans_id)
 
 998              JOIN customer ct ON (a.customer_id = ct.id)
 
 999              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1001         $union = qq| UNION |;
 
1003         push(@all_values, @invvalues);
 
1007     if ($form->{onorder} || $form->{ordered}) {
 
1008       my @ordvalues = @values;
 
1009       my $ordwhere = $where . qq| AND o.quotation = '0'|;
 
1011       if ($form->{transdatefrom}) {
 
1012         $ordwhere .= qq| AND o.transdate >= ?|;
 
1013         push(@ordvalues, $form->{transdatefrom});
 
1016       if ($form->{transdateto}) {
 
1017         $ordwhere .= qq| AND o.transdate <= ?|;
 
1018         push(@ordvalues, $form->{transdateto});
 
1021       if ($form->{description}) {
 
1022         $ordwhere .= qq| AND oi.description ILIKE ?|;
 
1023         push(@ordvalues, '%' . $form->{description} . '%');
 
1026       if ($form->{ordered}) {
 
1030              SELECT p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
 
1031                oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
 
1032                p.listprice, p.lastcost, p.rop, p.weight,
 
1033                p.priceupdate, p.image, p.drawing, p.microfiche,
 
1035                '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
 
1036                ct.name, NULL AS deliverydate,
 
1037                'oe' AS module, 'sales_order' AS type,
 
1038                (SELECT buy FROM exchangerate ex
 
1039                 WHERE ex.curr = o.curr AND ex.transdate = o.transdate) AS exchangerate
 
1041              JOIN parts p ON (oi.parts_id = p.id)
 
1042              JOIN oe o ON (oi.trans_id = o.id)
 
1043              JOIN customer ct ON (o.customer_id = ct.id)
 
1044              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1045              WHERE $ordwhere AND (o.customer_id > 0)|;
 
1046         $union = qq| UNION |;
 
1048         push(@all_values, @ordvalues);
 
1051       if ($form->{onorder}) {
 
1055              SELECT p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
 
1056                oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
 
1057                p.listprice, p.lastcost, p.rop, p.weight,
 
1058                p.priceupdate, p.image, p.drawing, p.microfiche,
 
1060                '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
 
1061                ct.name, NULL AS deliverydate,
 
1062                'oe' AS module, 'purchase_order' AS type,
 
1063                (SELECT sell FROM exchangerate ex
 
1064                WHERE ex.curr = o.curr AND (ex.transdate = o.transdate)) AS exchangerate
 
1066              JOIN parts p ON (oi.parts_id = p.id)
 
1067              JOIN oe o ON (oi.trans_id = o.id)
 
1068              JOIN vendor ct ON (o.vendor_id = ct.id)
 
1069              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1070              WHERE $ordwhere AND (o.vendor_id > 0)|;
 
1071         $union = qq| UNION |;
 
1073         push(@all_values, @ordvalues);
 
1078     if ($form->{rfq} || $form->{quoted}) {
 
1079       my $quowhere = $where . qq| AND o.quotation = '1'|;
 
1080       my @quovalues = @values;
 
1082       if ($form->{transdatefrom}) {
 
1083         $quowhere .= qq| AND o.transdate >= ?|;
 
1084         push(@quovalues, $form->{transdatefrom});
 
1087       if ($form->{transdateto}) {
 
1088         $quowhere .= qq| AND o.transdate <= ?|;
 
1089         push(@quovalues, $form->{transdateto});
 
1092       if ($form->{description}) {
 
1093         $quowhere .= qq| AND oi.description ILIKE ?|;
 
1094         push(@quovalues, '%' . $form->{description} . '%');
 
1097       if ($form->{quoted}) {
 
1102                p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
 
1103                oi.qty AS onhand, oi.unit, p.bin, oi.sellprice,
 
1104                p.listprice, p.lastcost, p.rop, p.weight,
 
1105                p.priceupdate, p.image, p.drawing, p.microfiche,
 
1107                '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
 
1108                ct.name, NULL AS deliverydate, 'oe' AS module, 'sales_quotation' AS type,
 
1109                (SELECT buy FROM exchangerate ex
 
1110                 WHERE (ex.curr = o.curr) AND (ex.transdate = o.transdate)) AS exchangerate
 
1112              JOIN parts p ON (oi.parts_id = p.id)
 
1113              JOIN oe o ON (oi.trans_id = o.id)
 
1114              JOIN customer ct ON (o.customer_id = ct.id)
 
1115              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1117              AND o.customer_id > 0|;
 
1118         $union = qq| UNION |;
 
1120         push(@all_values, @quovalues);
 
1127              SELECT p.id, p.partnumber, oi.description, oi.serialnumber AS serialnumber,
 
1128                oi.qty * -1 AS onhand, oi.unit, p.bin, oi.sellprice,
 
1129                p.listprice, p.lastcost, p.rop, p.weight,
 
1130                p.priceupdate, p.image, p.drawing, p.microfiche,
 
1132                '' AS invnumber, o.ordnumber, o.quonumber, oi.trans_id,
 
1133                ct.name, NULL AS deliverydate,
 
1134                'oe' AS module, 'request_quotation' AS type,
 
1135                (SELECT sell FROM exchangerate ex
 
1136                WHERE (ex.curr = o.curr) AND (ex.transdate = o.transdate)) AS exchangerate
 
1138              JOIN parts p ON (oi.parts_id = p.id)
 
1139              JOIN oe o ON (oi.trans_id = o.id)
 
1140              JOIN vendor ct ON (o.vendor_id = ct.id)
 
1141              LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1143              AND o.vendor_id > 0|;
 
1145         push(@all_values, @quovalues);
 
1149     $query .= qq| ORDER BY  | . $sortorder;
 
1153   $form->{parts} = selectall_hashref_query($form, $dbh, $query, @all_values);
 
1156   # include individual items for assemblies
 
1157   if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
 
1159       qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
 
1161            p.sellprice, p.listprice, p.lastcost,
 
1162            p.rop, p.weight, p.priceupdate,
 
1163            p.image, p.drawing, p.microfiche
 
1164          FROM parts p, assembly a
 
1165          WHERE (p.id = a.parts_id) AND (a.id = ?)|;
 
1166     $sth = prepare_query($form, $dbh, $query);
 
1168     foreach $item (@{ $form->{parts} }) {
 
1169       push(@assemblies, $item);
 
1170       do_statement($form, $sth, $query, conv_i($item->{id}));
 
1172       while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
 
1173         $ref->{assemblyitem} = 1;
 
1174         push(@assemblies, $ref);
 
1179     # copy assemblies to $form->{parts}
 
1180     $form->{parts} = \@assemblies;
 
1184   $main::lxdebug->leave_sub();
 
1188   $main::lxdebug->enter_sub();
 
1190   my ($self, $myconfig, $form) = @_;
 
1192   my $where = '1 = 1';
 
1200   if ($item ne 'make') {
 
1201     foreach my $item (qw(partnumber drawing microfiche make model pg.partsgroup)) {
 
1203       $column =~ s/.*\.//;
 
1204       next unless ($form->{$column});
 
1205       $where .= qq| AND $item ILIKE ?|;
 
1206       push(@where_values, '%' . $form->{$column} . '%');
 
1210   # special case for description
 
1211   if ($form->{description}
 
1212       && !(   $form->{bought}  || $form->{sold} || $form->{onorder}
 
1213            || $form->{ordered} || $form->{rfq} || $form->{quoted})) {
 
1214     $where .= qq| AND (p.description ILIKE ?)|;
 
1215     push(@where_values, '%' . $form->{description} . '%');
 
1218   # special case for serialnumber
 
1219   if ($form->{l_serialnumber} && $form->{serialnumber}) {
 
1220     $where .= qq| AND serialnumber ILIKE ?|;
 
1221     push(@where_values, '%' . $form->{serialnumber} . '%');
 
1225   # items which were never bought, sold or on an order
 
1226   if ($form->{itemstatus} eq 'orphaned') {
 
1227     $form->{onhand}  = $form->{short}   = 0;
 
1228     $form->{bought}  = $form->{sold}    = 0;
 
1229     $form->{onorder} = $form->{ordered} = 0;
 
1230     $form->{rfq}     = $form->{quoted}  = 0;
 
1232     $form->{transdatefrom} = $form->{transdateto} = "";
 
1235       qq| AND (p.onhand = 0)
 
1238               SELECT DISTINCT parts_id FROM invoice
 
1240               SELECT DISTINCT parts_id FROM assembly
 
1242               SELECT DISTINCT parts_id FROM orderitems
 
1246   if ($form->{itemstatus} eq 'active') {
 
1247     $where .= qq| AND p.obsolete = '0'|;
 
1250   if ($form->{itemstatus} eq 'obsolete') {
 
1251     $where .= qq| AND p.obsolete = '1'|;
 
1252     $form->{onhand} = $form->{short} = 0;
 
1255   if ($form->{itemstatus} eq 'onhand') {
 
1256     $where .= qq| AND p.onhand > 0|;
 
1259   if ($form->{itemstatus} eq 'short') {
 
1260     $where .= qq| AND p.onhand < p.rop|;
 
1263   foreach my $column (qw(make model)) {
 
1264     next unless ($form->{$colum});
 
1265     $where .= qq| AND p.id IN (SELECT DISTINCT parts_id FROM makemodel WHERE $column ILIKE ?|;
 
1266     push(@where_values, '%' . $form->{$column} . '%');
 
1269   # connect to database
 
1270   my $dbh = $form->dbconnect_noauto($myconfig);
 
1272   for my $column (qw(sellprice listprice)) {
 
1273     next if ($form->{$column} eq "");
 
1275     my $value = $form->parse_amount($myconfig, $form->{$column});
 
1278     if ($form->{"${column}_type"} eq "percent") {
 
1279       $value = ($value / 100) + 1;
 
1284       qq|UPDATE parts SET $column = $column $operator ?
 
1288             LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1290     do_query($from, $dbh, $query, $value, @where_values);
 
1294     qq|UPDATE prices SET price = price + ?
 
1298           LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1299           WHERE $where) AND (pricegroup_id = ?)|;
 
1300   my $sth_add = prepare_query($form, $dbh, $q_add);
 
1303     qq|UPDATE prices SET price = price * ?
 
1307           LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
 
1308           WHERE $where) AND (pricegroup_id = ?)|;
 
1309   my $sth_multiply = prepare_query($form, $dbh, $q_multiply);
 
1311   for my $i (1 .. $form->{price_rows}) {
 
1312     next if ($form->{"price_$i"} eq "");
 
1314     my $value = $form->parse_amount($myconfig, $form->{"price_$i"});
 
1316     if ($form->{"pricegroup_type_$i"} eq "percent") {
 
1317       do_statement($form, $sth_multiply, $q_multiply, ($value / 100) + 1, @where_values, conv_i($form->{"pricegroup_id_$i"}));
 
1319       do_statement($form, $sth_add, $q_add, $value, @where_values, conv_i($form->{"pricegroup_id_$i"}));
 
1324   $sth_multiply->finish();
 
1326   my $rc= $dbh->commit;
 
1329   $main::lxdebug->leave_sub();
 
1335   $main::lxdebug->enter_sub();
 
1337   my ($self, $module, $myconfig, $form) = @_;
 
1339   # connect to database
 
1340   my $dbh = $form->dbconnect($myconfig);
 
1342   my @values = ('%' . $module . '%');
 
1346       qq|SELECT c.accno, c.description, c.link, c.id,
 
1347            p.inventory_accno_id, p.income_accno_id, p.expense_accno_id
 
1348          FROM chart c, parts p
 
1349          WHERE (c.link LIKE ?) AND (p.id = ?)
 
1351     push(@values, conv_i($form->{id}));
 
1355       qq|SELECT c.accno, c.description, c.link, c.id,
 
1356            d.inventory_accno_id, d.income_accno_id, d.expense_accno_id
 
1357          FROM chart c, defaults d
 
1362   my $sth = prepare_execute_query($form, $dbh, $query, @values);
 
1363   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
 
1364     foreach my $key (split(/:/, $ref->{link})) {
 
1365       if ($key =~ /$module/) {
 
1366         if (   ($ref->{id} eq $ref->{inventory_accno_id})
 
1367             || ($ref->{id} eq $ref->{income_accno_id})
 
1368             || ($ref->{id} eq $ref->{expense_accno_id})) {
 
1369           push @{ $form->{"${module}_links"}{$key} },
 
1370             { accno       => $ref->{accno},
 
1371               description => $ref->{description},
 
1372               selected    => "selected" };
 
1373           $form->{"${key}_default"} = "$ref->{accno}--$ref->{description}";
 
1375           push @{ $form->{"${module}_links"}{$key} },
 
1376             { accno       => $ref->{accno},
 
1377               description => $ref->{description},
 
1385   # get buchungsgruppen
 
1386   $form->{BUCHUNGSGRUPPEN} = selectall_hashref_query($form, $dbh, qq|SELECT id, description FROM buchungsgruppen|);
 
1389   $form->{payment_terms} = selectall_hashref_query($form, $dbh, qq|SELECT id, description FROM payment_terms ORDER BY sortkey|);
 
1392     ($form->{priceupdate}) = selectrow_query($form, $dbh, qq|SELECT current_date|);
 
1396   $main::lxdebug->leave_sub();
 
1399 # get partnumber, description, unit, sellprice and soldtotal with choice through $sortorder for Top100
 
1401   $main::lxdebug->enter_sub();
 
1403   my ($self, $myconfig, $form, $sortorder) = @_;
 
1404   my $dbh   = $form->dbconnect($myconfig);
 
1405   my $order = qq| p.partnumber|;
 
1406   my $where = qq|1 = 1|;
 
1409   if ($sortorder eq "all") {
 
1410     $where .= qq| AND (partnumber ILIKE ?) AND (description ILIKE ?)|;
 
1411     push(@values, '%' . $form->{partnumber} . '%', '%' . $form->{description} . '%');
 
1413   } elsif ($sortorder eq "partnumber") {
 
1414     $where .= qq| AND (partnumber ILIKE ?)|;
 
1415     push(@values, '%' . $form->{partnumber} . '%');
 
1417   } elsif ($sortorder eq "description") {
 
1418     $where .= qq| AND (description ILIKE ?)|;
 
1419     push(@values, '%' . $form->{description} . '%');
 
1420     $order = "description";
 
1425     qq|SELECT id, partnumber, description, unit, sellprice
 
1427        WHERE $where ORDER BY $order|;
 
1429   my $sth = prepare_execute_query($form, $dbh, $query, @values);
 
1432   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
 
1433     if (($ref->{partnumber} eq "*") && ($ref->{description} eq "")) {
 
1438     $form->{"id_$j"}          = $ref->{id};
 
1439     $form->{"partnumber_$j"}  = $ref->{partnumber};
 
1440     $form->{"description_$j"} = $ref->{description};
 
1441     $form->{"unit_$j"}        = $ref->{unit};
 
1442     $form->{"sellprice_$j"}   = $ref->{sellprice};
 
1443     $form->{"soldtotal_$j"}   = get_soldtotal($dbh, $ref->{id});
 
1449   $main::lxdebug->leave_sub();
 
1454 # gets sum of sold part with part_id
 
1456   $main::lxdebug->enter_sub();
 
1458   my ($dbh, $id) = @_;
 
1460   my $query = qq|SELECT sum(qty) FROM invoice WHERE parts_id = ?|;
 
1461   my ($sum) = selectrow_query($form, $dbh, $query, conv_i($id));
 
1464   $main::lxdebug->leave_sub();
 
1467 }    #end get_soldtotal
 
1469 sub retrieve_languages {
 
1470   $main::lxdebug->enter_sub();
 
1472   my ($self, $myconfig, $form) = @_;
 
1474   # connect to database
 
1475   my $dbh = $form->dbconnect($myconfig);
 
1480   if ($form->{language_values} ne "") {
 
1482       qq|SELECT l.id, l.description, tr.translation, tr.longdescription
 
1484          LEFT OUTER JOIN translation tr ON (tr.language_id = l.id) AND (tr.parts_id = ?)|;
 
1485     @values = (conv_i($form->{id}));
 
1488     $query = qq|SELECT id, description FROM language|;
 
1491   my $languages = selectall_hashref_query($form, $dbh, $query, @values);
 
1495   $main::lxdebug->leave_sub();
 
1500 sub follow_account_chain {
 
1501   $main::lxdebug->enter_sub(2);
 
1503   my ($self, $form, $dbh, $transdate, $accno_id, $accno) = @_;
 
1505   my @visited_accno_ids = ($accno_id);
 
1510     qq|SELECT c.new_chart_id, date($transdate) >= c.valid_from AS is_valid, | .
 
1513     qq|LEFT JOIN chart cnew ON c.new_chart_id = cnew.id | .
 
1514     qq|WHERE (c.id = ?) AND NOT c.new_chart_id ISNULL AND (c.new_chart_id > 0)|;
 
1515   $sth = prepare_query($form, $dbh, $query);
 
1518     do_statement($form, $sth, $query, $accno_id);
 
1519     $ref = $sth->fetchrow_hashref();
 
1520     last unless ($ref && $ref->{"is_valid"} &&
 
1521                  !grep({ $_ == $ref->{"new_chart_id"} } @visited_accno_ids));
 
1522     $accno_id = $ref->{"new_chart_id"};
 
1523     $accno = $ref->{"accno"};
 
1524     push(@visited_accno_ids, $accno_id);
 
1527   $main::lxdebug->leave_sub(2);
 
1529   return ($accno_id, $accno);
 
1532 sub retrieve_accounts {
 
1533   $main::lxdebug->enter_sub(2);
 
1535   my ($self, $myconfig, $form, $parts_id, $index) = @_;
 
1537   my ($query, $sth, $dbh);
 
1539   $form->{"taxzone_id"} *= 1;
 
1541   $dbh = $form->get_standard_dbh($myconfig);
 
1544   if ($form->{type} eq "invoice") {
 
1545     if (($form->{vc} eq "vendor") || !$form->{deliverydate}) {
 
1546       $transdate = $form->{invdate};
 
1548       $transdate = $form->{deliverydate};
 
1550   } elsif (($form->{type} eq "credit_note") || ($form->{script} eq 'ir.pl')) {
 
1551     $transdate = $form->{invdate};
 
1553     $transdate = $form->{transdate};
 
1556   if ($transdate eq "") {
 
1557     $transdate = "current_date";
 
1559     $transdate = $dbh->quote($transdate);
 
1564     qq|  p.inventory_accno_id AS is_part, | .
 
1565     qq|  bg.inventory_accno_id, | .
 
1566     qq|  bg.income_accno_id_$form->{taxzone_id} AS income_accno_id, | .
 
1567     qq|  bg.expense_accno_id_$form->{taxzone_id} AS expense_accno_id, | .
 
1568     qq|  c1.accno AS inventory_accno, | .
 
1569     qq|  c2.accno AS income_accno, | .
 
1570     qq|  c3.accno AS expense_accno | .
 
1572     qq|LEFT JOIN buchungsgruppen bg ON p.buchungsgruppen_id = bg.id | .
 
1573     qq|LEFT JOIN chart c1 ON bg.inventory_accno_id = c1.id | .
 
1574     qq|LEFT JOIN chart c2 ON bg.income_accno_id_$form->{taxzone_id} = c2.id | .
 
1575     qq|LEFT JOIN chart c3 ON bg.expense_accno_id_$form->{taxzone_id} = c3.id | .
 
1577   my $ref = selectfirst_hashref_query($form, $dbh, $query, $parts_id);
 
1579   return $main::lxdebug->leave_sub(2) if (!$ref);
 
1581   $ref->{"inventory_accno_id"} = undef unless ($ref->{"is_part"});
 
1584   foreach my $type (qw(inventory income expense)) {
 
1585     next unless ($ref->{"${type}_accno_id"});
 
1586     ($accounts{"${type}_accno_id"}, $accounts{"${type}_accno"}) =
 
1587       $self->follow_account_chain($form, $dbh, $transdate,
 
1588                                   $ref->{"${type}_accno_id"},
 
1589                                   $ref->{"${type}_accno"});
 
1592   map({ $form->{"${_}_accno_$index"} = $accounts{"${_}_accno"} }
 
1593       qw(inventory income expense));
 
1595   my $inc_exp = $form->{"vc"} eq "customer" ? "income" : "expense";
 
1596   my $accno_id = $accounts{"${inc_exp}_accno_id"};
 
1599     qq|SELECT c.accno, t.taxdescription AS description, t.rate, t.taxnumber | .
 
1601     qq|LEFT JOIN chart c ON c.id = t.chart_id | .
 
1602     qq|WHERE t.id IN | .
 
1603     qq|  (SELECT tk.tax_id | .
 
1604     qq|   FROM taxkeys tk | .
 
1605     qq|   WHERE tk.chart_id = ? AND startdate <= | . quote_db_date($transdate) .
 
1606     qq|   ORDER BY startdate DESC LIMIT 1) |;
 
1607   $ref = selectfirst_hashref_query($form, $dbh, $query, $accno_id);
 
1610     $main::lxdebug->leave_sub(2);
 
1614   $form->{"taxaccounts_$index"} = $ref->{"accno"};
 
1615   if ($form->{"taxaccounts"} !~ /$ref->{accno}/) {
 
1616     $form->{"taxaccounts"} .= "$ref->{accno} ";
 
1618   map({ $form->{"$ref->{accno}_${_}"} = $ref->{$_}; }
 
1619       qw(rate description taxnumber));
 
1621 #   $main::lxdebug->message(0, "formvars: rate " . $form->{"$ref->{accno}_rate"} .
 
1622 #                           " description " . $form->{"$ref->{accno}_description"} .
 
1623 #                           " taxnumber " . $form->{"$ref->{accno}_taxnumber"} .
 
1624 #                           " || taxaccounts_$index " . $form->{"taxaccounts_$index"} .
 
1625 #                           " || taxaccounts " . $form->{"taxaccounts"});
 
1627   $main::lxdebug->leave_sub(2);