WebshopApi: falsche sql update Abhängigkeit
[kivitendo-erp.git] / SL / OE.pm
index f192c1d..a0a0abf 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -25,7 +25,8 @@
 # GNU General Public License for more details.
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1335, USA.
 #======================================================================
 #
 # Order entry module
@@ -50,6 +51,9 @@ use SL::DBUtils;
 use SL::HTML::Restrict;
 use SL::IC;
 use SL::TransNumber;
+use SL::Util qw(trim);
+use SL::DB;
+use Text::ParseWords;
 
 use strict;
 
@@ -134,11 +138,9 @@ sub transactions {
     qq|WHERE (o.quotation = ?) |;
   push(@values, $quotation);
 
-  my ($null, $split_department_id) = split /--/, $form->{department};
-  my $department_id = $form->{department_id} || $split_department_id;
-  if ($department_id) {
+  if ($form->{department_id}) {
     $query .= qq| AND o.department_id = ?|;
-    push(@values, $department_id);
+    push(@values, $form->{department_id});
   }
 
   if ($form->{"project_id"}) {
@@ -157,7 +159,7 @@ sub transactions {
         WHERE proi.projectnumber ILIKE ? AND oi.trans_id = o.id
       ))
 SQL
-    push @values, "%" . $form->{"projectnumber"} . "%", "%" . $form->{"projectnumber"} . "%" ;
+    push @values, like($form->{"projectnumber"}), like($form->{"projectnumber"});
   }
 
   if ($form->{"business_id"}) {
@@ -171,12 +173,12 @@ SQL
 
   } elsif ($form->{$vc}) {
     $query .= " AND ct.name ILIKE ?";
-    push(@values, '%' . $form->{$vc} . '%');
+    push(@values, like($form->{$vc}));
   }
 
   if ($form->{"cp_name"}) {
     $query .= " AND (cp.cp_name ILIKE ? OR cp.cp_givenname ILIKE ?)";
-    push(@values, ('%' . $form->{"cp_name"} . '%')x2);
+    push(@values, (like($form->{"cp_name"}))x2);
   }
 
   if (!$main::auth->assert('sales_all_edit', 1)) {
@@ -207,12 +209,12 @@ SQL
 
   if ($form->{$ordnumber}) {
     $query .= qq| AND o.$ordnumber ILIKE ?|;
-    push(@values, '%' . $form->{$ordnumber} . '%');
+    push(@values, like($form->{$ordnumber}));
   }
 
   if ($form->{cusordnumber}) {
     $query .= qq| AND o.cusordnumber ILIKE ?|;
-    push(@values, '%' . $form->{cusordnumber} . '%');
+    push(@values, like($form->{cusordnumber}));
   }
 
   if($form->{transdatefrom}) {
@@ -247,7 +249,7 @@ SQL
 
   if ($form->{shippingpoint}) {
     $query .= qq| AND o.shippingpoint ILIKE ?|;
-    push(@values, '%' . $form->{shippingpoint} . '%');
+    push(@values, like($form->{shippingpoint}));
   }
 
   if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
@@ -257,7 +259,7 @@ SQL
 
   if ($form->{transaction_description}) {
     $query .= qq| AND o.transaction_description ILIKE ?|;
-    push(@values, '%' . $form->{transaction_description} . '%');
+    push(@values, like($form->{transaction_description}));
   }
 
   if ($form->{periodic_invoices_active} ne $form->{periodic_invoices_inactive}) {
@@ -272,7 +274,7 @@ SQL
   if (($form->{order_probability_value} || '') ne '') {
     my $op  = $form->{order_probability_value} eq 'le' ? '<=' : '>=';
     $query .= qq| AND (o.order_probability ${op} ?)|;
-    push @values, $form->{order_probability_value};
+    push @values, trim($form->{order_probability_value});
   }
 
   if ($form->{expected_billing_date_from}) {
@@ -285,6 +287,45 @@ SQL
     push @values, conv_date($form->{expected_billing_date_to});
   }
 
+  if ($form->{parts_partnumber}) {
+    $query .= <<SQL;
+      AND EXISTS (
+        SELECT orderitems.trans_id
+        FROM orderitems
+        LEFT JOIN parts ON (orderitems.parts_id = parts.id)
+        WHERE (orderitems.trans_id = o.id)
+          AND (parts.partnumber ILIKE ?)
+        LIMIT 1
+      )
+SQL
+    push @values, like($form->{parts_partnumber});
+  }
+
+  if ($form->{parts_description}) {
+    $query .= <<SQL;
+      AND EXISTS (
+        SELECT orderitems.trans_id
+        FROM orderitems
+        WHERE (orderitems.trans_id = o.id)
+          AND (orderitems.description ILIKE ?)
+        LIMIT 1
+      )
+SQL
+    push @values, like($form->{parts_description});
+  }
+
+  if ($form->{all}) {
+    my @tokens = parse_line('\s+', 0, $form->{all});
+    # ordnumber quonumber customer.name vendor.name transaction_description
+    $query .= qq| AND (
+      o.ordnumber ILIKE ? OR
+      o.quonumber ILIKE ? OR
+      ct.name     ILIKE ? OR
+      o.transaction_description ILIKE ?
+    )| for @tokens;
+    push @values, (like($_))x4 for @tokens;
+  }
+
   my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
                                                             'trans_id_field' => 'ct.id',
                                                             'filter'         => $form,
@@ -378,12 +419,22 @@ sub transactions_for_todo_list {
 }
 
 sub save {
+  my ($self, $myconfig, $form) = @_;
+  $main::lxdebug->enter_sub();
+
+  my $rc = SL::DB->client->with_transaction(\&_save, $self, $myconfig, $form);
+
+  $::lxdebug->leave_sub;
+
+  return $rc;
+}
+
+sub _save {
   $main::lxdebug->enter_sub();
 
   my ($self, $myconfig, $form) = @_;
 
-  # connect to database, turn off autocommit
-  my $dbh = $form->get_standard_dbh;
+  my $dbh = SL::DB->client->dbh;
   my $restricter = SL::HTML::Restrict->create;
 
   my ($query, @values, $sth, $null);
@@ -407,6 +458,11 @@ sub save {
   $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
 
   if ($form->{id}) {
+    $query = qq|DELETE FROM custom_variables
+                WHERE (config_id IN (SELECT id        FROM custom_variable_configs WHERE (module = 'ShipTo')))
+                  AND (trans_id  IN (SELECT shipto_id FROM shipto                  WHERE (module = 'OE') AND (trans_id = ?)))|;
+    do_query($form, $dbh, $query, $form->{id});
+
     $query = qq|DELETE FROM shipto | .
              qq|WHERE trans_id = ? AND module = 'OE'|;
     do_query($form, $dbh, $query, $form->{id});
@@ -612,8 +668,12 @@ SQL
                                   dbh          => $dbh);
 
       # link previous items with orderitems
-      foreach (qw(orderitems invoice)) {
-        if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}) {
+      # assume we have a new workflow if we link from invoice or order to quotation
+      # unluckily orderitems are used for quotation and orders - therefore one more
+      # check to be sure NOT to link from order to quotation
+      foreach (qw(orderitems)) {
+        if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}
+              && $form->{type} !~ 'quotation') {
           RecordLinks->create_links('dbh'        => $dbh,
                                     'mode'       => 'ids',
                                     'from_table' => $_,
@@ -660,8 +720,6 @@ SQL
 
   my $quotation = $form->{type} =~ /_order$/ ? 'f' : 't';
 
-  ($null, $form->{department_id}) = split(/--/, $form->{department}) if $form->{department};
-
   # save OE record
   $query =
     qq|UPDATE oe SET
@@ -696,6 +754,8 @@ SQL
              conv_i($form->{id}));
   do_query($form, $dbh, $query, @values);
 
+  $form->new_lastmtime('oe');
+
   $form->{ordtotal} = $amount;
 
   $form->{name} = $form->{ $form->{vc} };
@@ -710,28 +770,21 @@ SQL
   $form->save_status($dbh);
 
   # Link this record to the records it was created from.
-  # check every record type we may link. i am not happy with converting the string to array back
-  # should be a array from the start (OE.pm -> retrieve).
-  #  and that i need the local array ref for close_quotation_rfqs. better ideas welcome
   $form->{convert_from_oe_ids} =~ s/^\s+//;
   $form->{convert_from_oe_ids} =~ s/\s+$//;
   my @convert_from_oe_ids      =  split m/\s+/, $form->{convert_from_oe_ids};
   delete $form->{convert_from_oe_ids};
-  @{ $form->{convert_from_oe_ids} }      =  @convert_from_oe_ids;
-  foreach (qw(ar oe)) {
-    if (!$form->{useasnew} && $form->{"convert_from_${_}_ids"}) {
-      RecordLinks->create_links('dbh'        => $dbh,
-                                'mode'       => 'ids',
-                                'from_table' => $_,
-                                'from_ids'   => $form->{"convert_from_${_}_ids"},
-                                'to_table'   => 'oe',
-                                'to_id'      => $form->{id},
-        );
-      delete $form->{"convert_from_${_}_ids"};
-    }
+  if (!$form->{useasnew} && scalar @convert_from_oe_ids) {
+    RecordLinks->create_links('dbh'        => $dbh,
+                              'mode'       => 'ids',
+                              'from_table' => 'oe',
+                              'from_ids'   => \@convert_from_oe_ids,
+                              'to_table'   => 'oe',
+                              'to_id'      => $form->{id},
+      );
     $self->_close_quotations_rfqs('dbh'     => $dbh,
                                   'from_id' => \@convert_from_oe_ids,
-                                  'to_id'   => $form->{id}) if $_ eq 'oe';
+                                  'to_id'   => $form->{id});
   }
 
   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
@@ -748,8 +801,6 @@ SQL
 
   Common::webdav_folder($form);
 
-  my $rc = $dbh->commit;
-
   $self->save_periodic_invoices_config(dbh         => $dbh,
                                        oe_id       => $form->{id},
                                        config_yaml => $form->{periodic_invoices_config})
@@ -757,7 +808,7 @@ SQL
 
   $main::lxdebug->leave_sub();
 
-  return $rc;
+  return 1;
 }
 
 sub save_periodic_invoices_config {
@@ -784,7 +835,7 @@ sub load_periodic_invoice_config {
 
     if ($config_obj) {
       my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity order_value_periodicity start_date_as_date end_date_as_date first_billing_date_as_date extend_automatically_by ar_chart_id
-                                                      print printer_id copies direct_debit) };
+                                                      print printer_id copies direct_debit send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
       $form->{periodic_invoices_config} = YAML::Dump($config);
     }
   }
@@ -801,37 +852,38 @@ sub _close_quotations_rfqs {
   my $myconfig = \%main::myconfig;
   my $form     = $main::form;
 
-  my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
+  my $dbh      = $params{dbh} || SL::DB->client->dbh;
 
-  my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
-  my $sth      = prepare_query($form, $dbh, $query);
+  SL::DB->client->with_transaction(sub {
 
-  do_statement($form, $sth, $query, conv_i($params{to_id}));
+    my $query    = qq|SELECT quotation FROM oe WHERE id = ?|;
+    my $sth      = prepare_query($form, $dbh, $query);
 
-  my ($quotation) = $sth->fetchrow_array();
+    do_statement($form, $sth, $query, conv_i($params{to_id}));
 
-  if ($quotation) {
-    $main::lxdebug->leave_sub();
-    return;
-  }
+    my ($quotation) = $sth->fetchrow_array();
 
-  my @close_ids;
+    if ($quotation) {
+      return 1;
+    }
 
-  foreach my $from_id (@{ $params{from_id} }) {
-    $from_id = conv_i($from_id);
-    do_statement($form, $sth, $query, $from_id);
-    ($quotation) = $sth->fetchrow_array();
-    push @close_ids, $from_id if ($quotation);
-  }
+    my @close_ids;
 
-  $sth->finish();
+    foreach my $from_id (@{ $params{from_id} }) {
+      $from_id = conv_i($from_id);
+      do_statement($form, $sth, $query, $from_id);
+      ($quotation) = $sth->fetchrow_array();
+      push @close_ids, $from_id if ($quotation);
+    }
 
-  if (scalar @close_ids) {
-    $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
-    do_query($form, $dbh, $query, @close_ids);
+    $sth->finish();
 
-    $dbh->commit() unless ($params{dbh});
-  }
+    if (scalar @close_ids) {
+      $query = qq|UPDATE oe SET closed = TRUE WHERE id IN (| . join(', ', ('?') x scalar @close_ids) . qq|)|;
+      do_query($form, $dbh, $query, @close_ids);
+    }
+    1;
+  }) or do { die SL::DB->client->error };
 
   $main::lxdebug->leave_sub();
 }
@@ -850,7 +902,7 @@ sub delete {
     unlink map { "$spool/$_" } @spoolfiles if $spool;
 
     1;
-  });
+  }) or do { die SL::DB->client->error };
 
   $main::lxdebug->leave_sub();
 
@@ -858,12 +910,20 @@ sub delete {
 }
 
 sub retrieve {
+  my ($self, $myconfig, $form) = @_;
   $main::lxdebug->enter_sub();
 
+  my $rc = SL::DB->client->with_transaction(\&_retrieve, $self, $myconfig, $form);
+
+  $::lxdebug->leave_sub;
+  return $rc;
+}
+
+sub _retrieve {
   my ($self, $myconfig, $form) = @_;
 
   # connect to database
-  my $dbh = $form->get_standard_dbh;
+  my $dbh = SL::DB->client->dbh;
 
   my ($query, $query_add, @values, @ids, $sth);
 
@@ -991,10 +1051,18 @@ sub retrieve {
       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
 
       $ref = $sth->fetchrow_hashref("NAME_lc");
-      delete($ref->{id});
-      map { $form->{$_} = $ref->{$_} } keys %$ref;
+      $form->{$_} = $ref->{$_} for grep { m{^shipto(?!_id$)} } keys %$ref;
       $sth->finish;
 
+      if ($ref->{shipto_id}) {
+        my $cvars = CVar->get_custom_variables(
+          dbh      => $dbh,
+          module   => 'ShipTo',
+          trans_id => $ref->{shipto_id},
+        );
+        $form->{"shiptocvar_$_->{name}"} = $_->{value} for @{ $cvars };
+      }
+
       # get printed, emailed and queued
       $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname FROM status s WHERE s.trans_id = ?|;
       $sth = prepare_execute_query($form, $dbh, $query, $form->{id});
@@ -1021,8 +1089,9 @@ sub retrieve {
            c2.accno AS income_accno,    c2.new_chart_id AS income_new_chart,    date($transdate) - c2.valid_from as income_valid,
            c3.accno AS expense_accno,   c3.new_chart_id AS expense_new_chart,   date($transdate) - c3.valid_from as expense_valid,
            oe.ordnumber AS ordnumber_oe, oe.transdate AS transdate_oe, oe.cusordnumber AS cusordnumber_oe,
-           p.partnumber, p.assembly, p.listprice, o.description, o.qty,
-           o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
+           p.partnumber, p.part_type, p.listprice, o.description, o.qty,
+           p.classification_id,
+           o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.part_type,
            o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
            o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
            o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
@@ -1054,10 +1123,10 @@ sub retrieve {
       map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
 
       # Handle accounts.
-      if (!$ref->{"part_inventory_accno_id"}) {
+      if (!$ref->{"part_type"} eq 'part') {
         map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid));
       }
-      delete($ref->{"part_inventory_accno_id"});
+      delete($ref->{"part_inventory_accno_id"});
 
       # in collective order, copy global ordnumber, transdate, cusordnumber into item scope
       #   unless already present there
@@ -1152,11 +1221,7 @@ sub retrieve {
 
   $self->load_periodic_invoice_config($form);
 
-  my $rc = $dbh->commit;
-
-  $main::lxdebug->leave_sub();
-
-  return $rc;
+  return 1;
 }
 
 sub retrieve_simple {
@@ -1189,7 +1254,7 @@ sub order_details {
   my ($self, $myconfig, $form) = @_;
 
   # connect to database
-  my $dbh = $form->get_standard_dbh;
+  my $dbh = SL::DB->client->dbh;
   my $query;
   my @values = ();
   my $sth;
@@ -1213,8 +1278,7 @@ sub order_details {
 
   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
-  $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS',
-                   'departments'   => 'ALL_DEPARTMENTS');
+  $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
   my %price_factors;
 
   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
@@ -1223,13 +1287,6 @@ sub order_details {
     $pfac->{formatted_factor}    = $form->format_amount($myconfig, $pfac->{factor});
   }
 
-  # lookup department
-  foreach my $dept (@{ $form->{ALL_DEPARTMENTS} }) {
-    next unless $dept->{id} eq $form->{department_id};
-    $form->{department} = $dept->{description};
-    last;
-  }
-
   # sort items by partsgroup
   for $i (1 .. $form->{rowcount}) {
     $partsgroup = "";
@@ -1262,6 +1319,7 @@ sub order_details {
   # so that they can be sorted in later
   my %prepared_template_arrays = IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
   my @prepared_arrays          = keys %prepared_template_arrays;
+  my @separate_totals          = qw(non_separate_subtotal);
 
   $form->{TEMPLATE_ARRAYS} = { };
 
@@ -1368,6 +1426,17 @@ sub order_details {
       push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} },     $form->{"discount_$i"};
 
+      if ( $prepared_template_arrays{separate}[$i - 1]  ) {
+        my $pabbr = $prepared_template_arrays{separate}[$i - 1];
+        if ( ! $form->{"separate_${pabbr}_subtotal"} ) {
+            push @separate_totals , "separate_${pabbr}_subtotal";
+            $form->{"separate_${pabbr}_subtotal"} = 0;
+        }
+        $form->{"separate_${pabbr}_subtotal"} += $linetotal;
+      } else {
+        $form->{non_separate_subtotal} += $linetotal;
+      }
+
       $form->{ordtotal}         += $linetotal;
       $form->{nodiscount_total} += $nodiscount_linetotal;
       $form->{discount_total}   += $discount;
@@ -1522,25 +1591,27 @@ sub order_details {
     $form->{subtotal_nofmt} = $form->{ordtotal};
   }
 
-  $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
+  my $grossamount = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
+  $form->{ordtotal} = $form->round_amount( $grossamount, 2, 1);
+  $form->{rounding} = $form->round_amount(
+    $form->{ordtotal} - $form->round_amount($grossamount, 2),
+    2
+  );
 
   # format amounts
+  $form->{rounding} = $form->format_amount($myconfig, $form->{rounding}, 2);
   $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
 
-  if ($form->{type} =~ /_quotation/) {
-    $form->set_payment_options($myconfig, $form->{quodate});
-  } else {
-    $form->set_payment_options($myconfig, $form->{orddate});
-  }
+  $form->set_payment_options($myconfig, $form->{$form->{type} =~ /_quotation/ ? 'quodate' : 'orddate'}, $form->{type});
 
   $form->{username} = $myconfig->{name};
 
-  $dbh->disconnect;
-
+  $form->{department}    = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
   $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
   $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
 
   $form->{order} = SL::DB::Manager::Order->find_by(id => $form->{id}) if $form->{id};
+  $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
 
   $main::lxdebug->leave_sub();
 }