Merge branch 'master' of vc.linet-services.de:public/lx-office-erp
[kivitendo-erp.git] / SL / IC.pm
index ac3d6f2..0f41870 100644 (file)
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -40,6 +40,7 @@ use YAML;
 
 use SL::CVar;
 use SL::DBUtils;
+use SL::TransNumber;
 
 use strict;
 
@@ -163,28 +164,25 @@ sub get_part {
   #correct rows
   $form->{price_rows} = $i - 1;
 
-  unless ($form->{item} eq 'service') {
+  # get makes
+  if ($form->{makemodel}) {
+  #hli
+    $query = qq|SELECT m.make, m.model,m.lastcost,m.lastcost,m.lastupdate,m.sortorder FROM makemodel m | .
+             qq|WHERE m.parts_id = ? order by m.sortorder asc|;
+    my @values = ($form->{id});
+    $sth = $dbh->prepare($query);
+    $sth->execute(@values) || $form->dberror("$query (" . join(', ', @values) . ")");
 
-    # get makes
-    if ($form->{makemodel}) {
-    #hli
-      $query = qq|SELECT m.make, m.model,m.lastcost,m.lastcost,m.lastupdate,m.sortorder FROM makemodel m | .
-               qq|WHERE m.parts_id = ? order by m.sortorder asc|;
-      my @values = ($form->{id});
-      $sth = $dbh->prepare($query);
-      $sth->execute(@values) || $form->dberror("$query (" . join(', ', @values) . ")");
-
-      my $i = 1;
-
-      while (($form->{"make_$i"}, $form->{"model_$i"}, $form->{"old_lastcost_$i"},
-                $form->{"lastcost_$i"}, $form->{"lastupdate_$i"}, $form->{"sortorder_$i"}) = $sth->fetchrow_array)
-      {
-        $i++;
-      }
-      $sth->finish;
-      $form->{makemodel_rows} = $i - 1;
+    my $i = 1;
 
+    while (($form->{"make_$i"}, $form->{"model_$i"}, $form->{"old_lastcost_$i"},
+              $form->{"lastcost_$i"}, $form->{"lastupdate_$i"}, $form->{"sortorder_$i"}) = $sth->fetchrow_array)
+    {
+      $i++;
     }
+    $sth->finish;
+    $form->{makemodel_rows} = $i - 1;
+
   }
 
   # get translations
@@ -287,7 +285,7 @@ sub save {
   my ($self, $myconfig, $form) = @_;
   my @values;
   # connect to database, turn off AutoCommit
-  my $dbh = $form->dbconnect_noauto($myconfig);
+  my $dbh = $form->get_standard_dbh;
 
   # save the part
   # make up a unique handle and store in partnumber field
@@ -311,6 +309,11 @@ sub save {
   my $priceupdate = ', priceupdate = current_date';
 
   if ($form->{id}) {
+    my $trans_number = SL::TransNumber->new(type => $form->{item}, dbh => $dbh, number => $form->{partnumber}, id => $form->{id});
+    if (!$trans_number->is_unique) {
+      $::lxdebug->leave_sub;
+      return 3;
+    }
 
     # get old price
     $query = qq|SELECT sellprice, weight FROM parts WHERE id = ?|;
@@ -324,10 +327,8 @@ sub save {
     }
     $sth->finish;
 
-    if ($form->{item} ne 'service') {
-      # delete makemodel records
-      do_query($form, $dbh, qq|DELETE FROM makemodel WHERE parts_id = ?|, conv_i($form->{id}));
-    }
+    # delete makemodel records
+    do_query($form, $dbh, qq|DELETE FROM makemodel WHERE parts_id = ?|, conv_i($form->{id}));
 
     if ($form->{item} eq 'assembly') {
       # delete assembly records
@@ -346,23 +347,19 @@ sub save {
     $priceupdate        = '' if (all { $previous_values->{$_} == $form->{$_} } qw(sellprice lastcost listprice));
 
   } else {
-    my ($count) = selectrow_query($form, $dbh, qq|SELECT COUNT(*) FROM parts WHERE partnumber = ?|, $form->{partnumber});
-    if ($count) {
-      $main::lxdebug->leave_sub();
+    my $trans_number = SL::TransNumber->new(type => $form->{item}, dbh => $dbh, number => $form->{partnumber}, save => 1);
+
+    if ($form->{partnumber} && !$trans_number->is_unique) {
+      $::lxdebug->leave_sub;
       return 3;
     }
 
+    $form->{partnumber} ||= $trans_number->create_unique;
+
     ($form->{id}) = selectrow_query($form, $dbh, qq|SELECT nextval('id')|);
-    do_query($form, $dbh, qq|INSERT INTO parts (id, partnumber, unit) VALUES (?, '', '')|, $form->{id});
+    do_query($form, $dbh, qq|INSERT INTO parts (id, partnumber, unit) VALUES (?, ?, ?)|, $form->{id}, $form->{partnumber}, $form->{unit});
 
     $form->{orphaned} = 1;
-    if ($form->{partnumber} eq "" && $form->{"item"} eq "service") {
-      $form->{partnumber} = $form->update_defaults($myconfig, "servicenumber");
-    }
-    if ($form->{partnumber} eq "" && $form->{"item"} ne "service") {
-      $form->{partnumber} = $form->update_defaults($myconfig, "articlenumber");
-    }
-
   }
   my $partsgroup_id = 0;
 
@@ -495,14 +492,13 @@ sub save {
   }
 
   # insert makemodel records
-  unless ($form->{item} eq 'service') {
     my $lastupdate = '';
     my $value = 0;
     for my $i (1 .. $form->{makemodel_rows}) {
       if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
         #hli
         $value = $form->parse_amount($myconfig, $form->{"lastcost_$i"});
-        if ($value == $form->{"old_lastcost_$i"})
+        if ($value == $form->parse_amount($myconfig, $form->{"old_lastcost_$i"}))
         {
             if ($form->{"lastupdate_$i"} eq "") {
                 $lastupdate = 'now()';
@@ -519,7 +515,6 @@ sub save {
         do_query($form, $dbh, $query, @values);
       }
     }
-  }
 
   # insert taxes
   foreach my $item (split(/ /, $form->{taxaccounts})) {
@@ -583,14 +578,14 @@ sub save {
     }
   }
 
-  CVar->save_custom_variables('dbh'       => $dbh,
-                              'module'    => 'IC',
-                              'trans_id'  => $form->{id},
-                              'variables' => $form);
+  CVar->save_custom_variables(dbh           => $dbh,
+                              module        => 'IC',
+                              trans_id      => $form->{id},
+                              variables     => $form,
+                              save_validity => 1);
 
   # commit
   my $rc = $dbh->commit;
-  $dbh->disconnect;
 
   $main::lxdebug->leave_sub();
 
@@ -668,7 +663,7 @@ sub delete {
 
   my %columns = ( "assembly" => "id", "parts" => "id" );
 
-  for my $table (qw(prices partstax makemodel inventory assembly license translation parts)) {
+  for my $table (qw(prices partstax makemodel inventory assembly translation parts)) {
     my $column = defined($columns{$table}) ? $columns{$table} : "parts_id";
     do_query($form, $dbh, qq|DELETE FROM $table WHERE $column = ?|, @values);
   }
@@ -705,6 +700,12 @@ sub assembly_item {
     push(@values, conv_i($form->{id}));
   }
 
+  # Search for part ID overrides all other criteria.
+  if ($form->{"id_${i}"}) {
+    $where  = qq|p.id = ?|;
+    @values = ($form->{"id_${i}"});
+  }
+
   if ($form->{partnumber}) {
     $where .= qq| ORDER BY p.partnumber|;
   } else {
@@ -786,12 +787,13 @@ sub all_parts {
   $form->{soldtotal} = undef if $form->{l_soldtotal}; # security fix. top100 insists on putting strings in there...
 
   my @simple_filters       = qw(partnumber ean description partsgroup microfiche drawing onhand);
+  my @project_filters      = qw(projectnumber projectdescription);
   my @makemodel_filters    = qw(make model);
   my @invoice_oi_filters   = qw(serialnumber soldtotal);
   my @apoe_filters         = qw(transdate);
   my @like_filters         = (@simple_filters, @invoice_oi_filters);
-  my @all_columns          = (@simple_filters, @makemodel_filters, @apoe_filters, qw(serialnumber));
-  my @simple_l_switches    = (@all_columns, qw(listprice sellprice lastcost priceupdate weight unit bin rop image));
+  my @all_columns          = (@simple_filters, @makemodel_filters, @apoe_filters, @project_filters, qw(serialnumber));
+  my @simple_l_switches    = (@all_columns, qw(notes listprice sellprice lastcost priceupdate weight unit bin rop image));
   my @oe_flags             = qw(bought sold onorder ordered rfq quoted);
   my @qsooqr_flags         = qw(invnumber ordnumber quonumber trans_id name module qty);
   my @deliverydate_flags   = qw(deliverydate);
@@ -810,14 +812,14 @@ sub all_parts {
     pfac       => 'LEFT JOIN price_factors pfac ON (pfac.id     = p.price_factor_id)',
     invoice_oi =>
       q|LEFT JOIN (
-         SELECT parts_id, description, serialnumber, trans_id, unit, sellprice, qty,          assemblyitem,         deliverydate, 'invoice'    AS ioi, id FROM invoice UNION
-         SELECT parts_id, description, serialnumber, trans_id, unit, sellprice, qty, FALSE AS assemblyitem, NULL AS deliverydate, 'orderitems' AS ioi, id FROM orderitems
+         SELECT parts_id, description, serialnumber, trans_id, unit, sellprice, qty,          assemblyitem,         deliverydate, 'invoice'    AS ioi, project_id, id FROM invoice UNION
+         SELECT parts_id, description, serialnumber, trans_id, unit, sellprice, qty, FALSE AS assemblyitem, NULL AS deliverydate, 'orderitems' AS ioi, project_id, id FROM orderitems
        ) AS ioi ON ioi.parts_id = p.id|,
     apoe       =>
       q|LEFT JOIN (
-         SELECT id, transdate, 'ir' AS module, ordnumber, quonumber,         invnumber, FALSE AS quotation, NULL AS customer_id,         vendor_id,    NULL AS deliverydate, 'invoice'    AS ioi FROM ap UNION
-         SELECT id, transdate, 'is' AS module, ordnumber, quonumber,         invnumber, FALSE AS quotation,         customer_id, NULL AS vendor_id,            deliverydate, 'invoice'    AS ioi FROM ar UNION
-         SELECT id, transdate, 'oe' AS module, ordnumber, quonumber, NULL AS invnumber,          quotation,         customer_id,         vendor_id, reqdate AS deliverydate, 'orderitems' AS ioi FROM oe
+         SELECT id, transdate, 'ir' AS module, ordnumber, quonumber,         invnumber, FALSE AS quotation, NULL AS customer_id,         vendor_id,    NULL AS deliverydate, globalproject_id, 'invoice'    AS ioi FROM ap UNION
+         SELECT id, transdate, 'is' AS module, ordnumber, quonumber,         invnumber, FALSE AS quotation,         customer_id, NULL AS vendor_id,            deliverydate, globalproject_id, 'invoice'    AS ioi FROM ar UNION
+         SELECT id, transdate, 'oe' AS module, ordnumber, quonumber, NULL AS invnumber,          quotation,         customer_id,         vendor_id, reqdate AS deliverydate, globalproject_id, 'orderitems' AS ioi FROM oe
        ) AS apoe ON ((ioi.trans_id = apoe.id) AND (ioi.ioi = apoe.ioi))|,
     cv         =>
       q|LEFT JOIN (
@@ -825,8 +827,9 @@ sub all_parts {
            SELECT id, name, 'vendor'   AS cv FROM vendor
          ) AS cv ON cv.id = apoe.customer_id OR cv.id = apoe.vendor_id|,
     mv         => 'LEFT JOIN vendor AS mv ON mv.id = mm.make',
+    project    => 'LEFT JOIN project AS pj ON pj.id = COALESCE(ioi.project_id, apoe.globalproject_id)',
   );
-  my @join_order = qw(partsgroup makemodel mv invoice_oi apoe cv pfac);
+  my @join_order = qw(partsgroup makemodel mv invoice_oi apoe cv pfac project);
 
   my %table_prefix = (
      deliverydate => 'apoe.', serialnumber => 'ioi.',
@@ -836,8 +839,8 @@ sub all_parts {
      quonumber    => 'apoe.', model        => 'mm.',
      invnumber    => 'apoe.', partsgroup   => 'pg.',
      lastcost     => 'p.',  , soldtotal    => ' ',
-     factor       => 'pfac.',
-     'SUM(ioi.qty)' => ' ',
+     factor       => 'pfac.', projectnumber => 'pj.',
+     'SUM(ioi.qty)' => ' ',   projectdescription => 'pj.',
      description  => 'p.',
      qty          => 'ioi.',
      serialnumber => 'ioi.',
@@ -851,7 +854,7 @@ sub all_parts {
   # of the scecified table will gently override (coalesce actually) the original value
   # use it to conditionally coalesce values from subtables
   my @column_override = (
-    #  column name,   prefix,  joins_needed
+    #  column name,   prefix,  joins_needed,  nick name (in case column is named like another)
     [ 'description',  'ioi.',  'invoice_oi'  ],
     [ 'deliverydate', 'ioi.',  'invoice_oi'  ],
     [ 'transdate',    'apoe.', 'apoe'        ],
@@ -865,6 +868,11 @@ sub all_parts {
     'SUM(ioi.qty)' => 'soldtotal',
     'ioi.id'       => 'ioi_id',
     'ioi.ioi'      => 'ioi',
+    'projectdescription' => 'projectdescription',
+  );
+
+  my %real_column = (
+    projectdescription => 'description',
   );
 
   if (($form->{searchitems} eq 'assembly') && $form->{l_lastcost}) {
@@ -874,19 +882,20 @@ sub all_parts {
   my $make_token_builder = sub {
     my $joins_needed = shift;
     sub {
-      my ($col, $alias) = @_;
+      my ($nick, $alias) = @_;
+      my ($col) = $real_column{$nick} || $nick;
       my @coalesce_tokens =
         map  { ($_->[1] || 'p.') . $_->[0] }
         grep { !$_->[2] || $joins_needed->{$_->[2]} }
-        grep {  $_->[0] eq $col }
-        @column_override, [ $col, $table_prefix{$col} ];
+        grep { ($_->[3] || $_->[0]) eq $nick }
+        @column_override, [ $col, $table_prefix{$nick}, undef , $nick ];
 
       my $coalesce = scalar @coalesce_tokens > 1;
       return ($coalesce
         ? sprintf 'COALESCE(%s)', join ', ', @coalesce_tokens
         : shift                              @coalesce_tokens)
-        . ($alias && ($coalesce || $renamed_columns{$col})
-        ?  " AS " . ($renamed_columns{$col} || $col)
+        . ($alias && ($coalesce || $renamed_columns{$nick})
+        ?  " AS " . ($renamed_columns{$nick} || $nick)
         : '');
     }
   };
@@ -904,6 +913,12 @@ sub all_parts {
     }
   }
 
+  if ($form->{"partsgroup_id"}) {
+    $form->{"l_partsgroup"} = '1'; # show the column
+    push @where_tokens, "pg.id = ?";
+    push @bind_vars, $form->{"partsgroup_id"};
+  }
+
   foreach (@like_filters) {
     next unless $form->{$_};
     $form->{"l_$_"} = '1'; # show the column
@@ -989,17 +1004,12 @@ sub all_parts {
 
   $joins_needed{partsgroup}  = 1;
   $joins_needed{pfac}        = 1;
+  $joins_needed{project}     = 1 if grep { $form->{$_} || $form->{"l_$_"} } @project_filters;
   $joins_needed{makemodel}   = 1 if grep { $form->{$_} || $form->{"l_$_"} } @makemodel_filters;
   $joins_needed{mv}          = 1 if $joins_needed{makemodel};
   $joins_needed{cv}          = 1 if $bsooqr;
   $joins_needed{apoe}        = 1 if $joins_needed{cv}   || grep { $form->{$_} || $form->{"l_$_"} } @apoe_filters;
-  $joins_needed{invoice_oi}  = 1 if $joins_needed{apoe} || grep { $form->{$_} || $form->{"l_$_"} } @invoice_oi_filters;
-
-  # in bsoorq, use qtys instead of onhand
-  if ($joins_needed{invoice_oi}) {
-    $renamed_columns{onhand} = 'onhand_before_bsooqr';
-    $renamed_columns{qty}    = 'onhand';
-  }
+  $joins_needed{invoice_oi}  = 1 if $joins_needed{project} || $joins_needed{apoe} || grep { $form->{$_} || $form->{"l_$_"} } @invoice_oi_filters;
 
   # special case for description search.
   # up in the simple filter section the description filter got interpreted as something like: WHERE description ILIKE '%$form->{description}%'
@@ -1034,7 +1044,7 @@ sub all_parts {
   my $select_clause = join ', ',    map { $token_builder->($_, 1) } @select_tokens;
   my $join_clause   = join ' ',     @joins{ grep $joins_needed{$_}, @join_order };
   my $where_clause  = join ' AND ', map { "($_)" } @where_tokens;
-  my $group_clause  = ' GROUP BY ' . join ', ',    map { $token_builder->($_) } @group_tokens if scalar @group_tokens;
+  my $group_clause  = @group_tokens ? ' GROUP BY ' . join ', ',    map { $token_builder->($_) } @group_tokens : '';
 
   my %oe_flag_to_cvar = (
     bought   => 'invoice',
@@ -1071,13 +1081,20 @@ sub all_parts {
 
   map { $_->{onhand} *= 1 } @{ $form->{parts} };
 
+  # fix qty sign in ap. those are saved negative
+  if ($bsooqr && $form->{bought}) {
+    for my $row (@{ $form->{parts} }) {
+      $row->{qty} *= -1 if $row->{module} eq 'ir';
+    }
+  }
+
   # post processing for assembly parts lists (bom)
   # for each part get the assembly parts and add them into the partlist.
   my @assemblies;
   if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
     $query =
       qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
-           p.unit, p.bin,
+           p.unit, p.bin, p.notes,
            p.sellprice, p.listprice, p.lastcost,
            p.rop, p.weight, p.priceupdate,
            p.image, p.drawing, p.microfiche,
@@ -1104,6 +1121,26 @@ sub all_parts {
     $form->{parts} = \@assemblies;
   }
 
+  if ($form->{l_pricegroups} ) {
+    my $query = <<SQL;
+       SELECT parts_id, price, pricegroup_id
+       FROM prices
+       WHERE parts_id = ?
+SQL
+
+    my $sth = prepare_query($form, $dbh, $query);
+
+    foreach my $part (@{ $form->{parts} }) {
+      do_statement($form, $sth, $query, conv_i($part->{id}));
+
+      while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
+        $part->{"pricegroup_$ref->{pricegroup_id}"} = $ref->{price};
+      }
+      $sth->finish;
+    }
+  };
+
+
   $main::lxdebug->leave_sub();
 
   return wantarray ? @{ $form->{parts} } : $form->{parts};
@@ -1662,7 +1699,7 @@ sub prepare_parts_for_printing {
   }
 
   my $placeholders = join ', ', ('?') x scalar(@part_ids);
-  my $query        = qq|SELECT mm.parts_id, mm.model, v.name AS make
+  my $query        = qq|SELECT mm.parts_id, mm.model, mm.lastcost, v.name AS make
                         FROM makemodel mm
                         LEFT JOIN vendor v ON (mm.make = v.id)
                         WHERE mm.parts_id IN ($placeholders)|;