Sammelaufträge - fehlerhaftes Verhalten
[kivitendo-erp.git] / SL / OE.pm
index 5e2551b..f776d51 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -109,7 +109,9 @@ sub transactions {
     qq|  o.closed, o.delivered, o.quonumber, o.cusordnumber, o.shippingpoint, o.shipvia, | .
     qq|  o.transaction_description, | .
     qq|  o.marge_total, o.marge_percent, | .
+    qq|  o.itime::DATE AS insertdate, | .
     qq|  ex.$rate AS exchangerate, | .
+    qq|  pt.description AS payment_terms, | .
     qq|  pr.projectnumber AS globalprojectnumber, | .
     qq|  e.name AS employee, s.name AS salesman, | .
     qq|  ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id,  | .
@@ -124,6 +126,7 @@ sub transactions {
     qq|LEFT JOIN exchangerate ex ON (ex.currency_id = o.currency_id | .
     qq|  AND ex.transdate = o.transdate) | .
     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
+    qq|LEFT JOIN payment_terms pt ON (pt.id = o.payment_id)| .
     qq|LEFT JOIN tax_zones tz ON (o.taxzone_id = tz.id) | .
     qq|$periodic_invoices_joins | .
     qq|WHERE (o.quotation = ?) |;
@@ -176,7 +179,7 @@ SQL
 
   if (!$main::auth->assert('sales_all_edit', 1)) {
     $query .= " AND o.employee_id = (select id from employee where login= ?)";
-    push @values, $form->{login};
+    push @values, $::myconfig{login};
   }
   if ($form->{employee_id}) {
     $query .= " AND o.employee_id = ?";
@@ -230,6 +233,16 @@ SQL
     push(@values, conv_date($form->{reqdateto}));
   }
 
+  if($form->{insertdatefrom}) {
+    $query .= qq| AND o.itime::DATE >= ?|;
+    push(@values, conv_date($form->{insertdatefrom}));
+  }
+
+  if($form->{insertdateto}) {
+    $query .= qq| AND o.itime::DATE <= ?|;
+    push(@values, conv_date($form->{insertdateto}));
+  }
+
   if ($form->{shippingpoint}) {
     $query .= qq| AND o.shippingpoint ILIKE ?|;
     push(@values, '%' . $form->{shippingpoint} . '%');
@@ -270,8 +283,17 @@ SQL
     push @values, conv_date($form->{expected_billing_date_to});
   }
 
+  my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
+                                                            'trans_id_field' => 'ct.id',
+                                                            'filter'         => $form,
+                                                           );
+  if ($cvar_where) {
+    $query .= qq| AND ($cvar_where)|;
+    push @values, @cvar_values;
+  }
+
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
-  my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name")));
+  my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
   my %allowed_sort_columns = (
     "transdate"               => "o.transdate",
     "reqdate"                 => "o.reqdate",
@@ -285,10 +307,12 @@ SQL
     "shipvia"                 => "o.shipvia",
     "transaction_description" => "o.transaction_description",
     "shippingpoint"           => "o.shippingpoint",
+    "insertdate"              => "o.itime",
     "taxzone"                 => "tz.description",
+    "payment_terms"           => "pt.description",
   );
   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
-    $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
+    $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}"  . ", o.itime ${sortdir}";
   }
   $query .= qq| ORDER by | . $sortorder;
 
@@ -325,7 +349,7 @@ sub transactions_for_todo_list {
   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
   my $query    = qq|SELECT id FROM employee WHERE login = ?|;
-  my ($e_id)   = selectrow_query($form, $dbh, $query, $form->{login});
+  my ($e_id)   = selectrow_query($form, $dbh, $query, $::myconfig{login});
 
   $query       =
     qq|SELECT oe.id, oe.transdate, oe.reqdate, oe.quonumber, oe.transaction_description, oe.amount,
@@ -509,32 +533,22 @@ sub save {
       $pricegroup_id *= 1;
       $pricegroup_id  = undef if !$pricegroup_id;
 
+      CVar->get_non_editable_ic_cvars(form               => $form,
+                                      dbh                => $dbh,
+                                      row                => $i,
+                                      sub_module         => 'orderitems',
+                                      may_converted_from => ['orderitems', 'invoice']);
+
       my $position = $i;
 
       # save detail record in orderitems table
-      my $cvars;
       if (! $form->{"orderitems_id_$i"}) {
         $query = qq|SELECT nextval('orderitemsid')|;
         ($form->{"orderitems_id_$i"}) = selectrow_query($form, $dbh, $query);
 
         $query = qq|INSERT INTO orderitems (id, position) VALUES (?, ?)|;
         do_query($form, $dbh, $query, $form->{"orderitems_id_$i"}, conv_i($position));
-
-        # get values for CVars from master data for new items
-        $cvars = CVar->get_custom_variables(dbh      => $dbh,
-                                            module   => 'IC',
-                                            trans_id => $form->{"id_$i"},
-                                           );
-      } else {
-        # get values for CVars from custom_variables for existing items
-        $cvars = CVar->get_custom_variables(dbh        => $dbh,
-                                            module     => 'IC',
-                                            sub_module => 'orderitems',
-                                            trans_id   => $form->{"orderitems_id_$i"},
-                                           );
       }
-      # map only non-editable CVars to form (editable ones are already there)
-      map { $form->{"ic_cvar_$_->{name}_$i"} = $_->{value} unless $_->{flag_editable} } @{ $cvars };
 
       my $orderitems_id = $form->{"orderitems_id_$i"};
       push @processed_orderitems, $orderitems_id;
@@ -543,7 +557,7 @@ sub save {
          UPDATE orderitems SET
           trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
           sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
-          pricegroup_id = ?, ordnumber = ?, transdate = ?, cusordnumber = ?, subtotal = ?,
+          pricegroup_id = ?, subtotal = ?,
           marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
           active_price_source = ?, active_discount_source = ?,
           price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
@@ -555,9 +569,8 @@ SQL
            $form->{"qty_$i"}, $baseqty,
            $fxsellprice, $form->{"discount_$i"},
            $form->{"unit_$i"}, conv_date($reqdate), conv_i($form->{"project_id_$i"}),
-           $form->{"serialnumber_$i"}, $form->{"ship_$i"}, $pricegroup_id,
-           $form->{"ordnumber_$i"}, conv_date($form->{"transdate_$i"}),
-           $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
+           $form->{"serialnumber_$i"}, $form->{"ship_$i"},
+           $pricegroup_id, $form->{"subtotal_$i"} ? 't' : 'f',
            $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
            $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}),
            $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
@@ -578,9 +591,10 @@ SQL
                                   name_prefix  => 'ic_',
                                   name_postfix => "_$i",
                                   dbh          => $dbh);
+
       # link previous items with orderitems
       foreach (qw(orderitems invoice)) {
-        if ($form->{"converted_from_${_}_id_$i"}) {
+        if (!$form->{saveasnew} && !$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}) {
           RecordLinks->create_links('dbh'        => $dbh,
                                     'mode'       => 'ids',
                                     'from_table' => $_,
@@ -588,11 +602,12 @@ SQL
                                     'to_table'   => 'orderitems',
                                     'to_id'      => $orderitems_id,
           );
-          delete $form->{"converted_from_${_}_id_$i"};
         }
+        delete $form->{"converted_from_${_}_id_$i"};
       }
     }
   }
+
   # search for orphaned ids
   $query  = sprintf 'SELECT id FROM orderitems WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_orderitems;
   @values = (conv_i($form->{id}), map { conv_i($_) } @processed_orderitems);
@@ -642,7 +657,7 @@ SQL
              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
              $amount, $netamount, conv_date($reqdate),
              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
-             $form->{shipvia}, $form->{notes}, $form->{intnotes},
+             $form->{shipvia}, $restricter->process($form->{notes}), $form->{intnotes},
              $form->{currency}, $form->{closed} ? 't' : 'f',
              $form->{delivered} ? "t" : "f", $form->{proforma} ? 't' : 'f',
              $quotation, conv_i($form->{department_id}),
@@ -673,23 +688,28 @@ 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};
-
-  if (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},
-      );
-
+  @{ $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"};
+    }
     $self->_close_quotations_rfqs('dbh'     => $dbh,
                                   'from_id' => \@convert_from_oe_ids,
-                                  'to_id'   => $form->{id});
+                                  'to_id'   => $form->{id}) if $_ eq 'oe';
   }
 
   if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
@@ -741,7 +761,7 @@ sub load_periodic_invoice_config {
     my $config_obj = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $form->{id});
 
     if ($config_obj) {
-      my $config = { map { $_ => $config_obj->$_ } qw(active terminated periodicity start_date_as_date end_date_as_date first_billing_date_as_date extend_automatically_by ar_chart_id
+      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) };
       $form->{periodic_invoices_config} = YAML::Dump($config);
     }
@@ -839,20 +859,31 @@ sub retrieve {
   if ($form->{"rowcount"} and $#ids == 0) {
     $form->{"id"} = $ids[0];
     undef @ids;
+    delete $form->{convert_from_oe_ids};
   }
 
   # and remember for the rest of the function
   my $is_collective_order = scalar @ids;
 
+  # If collective order was created from exactly 1 order, we assume the same
+  # behaviour as a "save as new" from within an order is actually desired, i.e.
+  # the original order isn't part of a workflow where we want to remember
+  # record_links, but simply a quick way of generating a new order from an old
+  # one without having to enter everything again.
+  # Setting useasnew will prevent the creation of record_links for the items
+  # when saving the new order.
+  # This form variable is probably not necessary, could just set saveasnew instead
+  $form->{useasnew} = 1 if $is_collective_order == 1;
+
   if (!$form->{id}) {
-    my $wday         = (localtime(time))[6];
-    my $next_workday = $wday == 5 ? 3 : $wday == 6 ? 2 : 1;
+    my $extra_days   = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval : 1;
+    my $next_workday = DateTime->today_local->add(days => $extra_days);
+    my $day_of_week  = $next_workday->day_of_week;
 
-    # if we have a client configured interval for sales quotation, we add this
-    $next_workday   += $::instance_conf->get_reqdate_interval if ($::instance_conf->get_reqdate_interval &&
-                                                                    $form->{type} eq 'sales_quotation' );
+    $next_workday->add(days => (8 - $day_of_week)) if $day_of_week >= 6;
 
-    $query_add       = qq|, current_date AS transdate, date(current_date + interval '${next_workday} days') AS reqdate|;
+    $form->{transdate} = DateTime->today_local->to_kivitendo;
+    $form->{reqdate}   = $next_workday->to_kivitendo;
   }
 
   # get default accounts
@@ -890,8 +921,8 @@ sub retrieve {
            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
-           o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id
-           , o.order_probability, o.expected_billing_date
+           o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
+           o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
          FROM oe o
          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
          LEFT JOIN employee e ON (o.employee_id = e.id)
@@ -1047,7 +1078,9 @@ sub retrieve {
       }
 
       # delete orderitems_id in collective orders, so that they get cloned no matter what
-      delete $ref->{orderitems_id} if $is_collective_order;
+      # is this correct? or is the following meant?
+      # remember orderitems_ids in converted_from_orderitems_ids, so that they may be linked
+      $ref->{converted_from_orderitems_id} = delete $ref->{orderitems_id} if $is_collective_order;
 
       # get tax rates and description
       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
@@ -1203,25 +1236,29 @@ sub order_details {
 
   $form->{discount} = [];
 
+  # get some values of parts from db on store them in extra array,
+  # 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;
+
   $form->{TEMPLATE_ARRAYS} = { };
-  IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
   my $project_cvar_configs = CVar->get_configs(module => 'Projects');
 
   my @arrays =
-    qw(runningnumber number description longdescription qty ship unit bin
-       partnotes serialnumber reqdate sellprice listprice netprice
-       discount p_discount discount_sub nodiscount_sub
-       linetotal  nodiscount_linetotal tax_rate projectnumber projectdescription
-       price_factor price_factor_name partsgroup weight lineweight);
+    qw(runningnumber number description longdescription qty qty_nofmt ship ship_nofmt unit bin
+       partnotes serialnumber reqdate sellprice sellprice_nofmt listprice listprice_nofmt netprice netprice_nofmt
+       discount discount_nofmt p_discount discount_sub discount_sub_nofmt nodiscount_sub nodiscount_sub_nofmt
+       linetotal linetotal_nofmt nodiscount_linetotal nodiscount_linetotal_nofmt tax_rate projectnumber projectdescription
+       price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt);
 
   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
   push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
 
   my @tax_arrays = qw(taxbase tax taxdescription taxrate taxnumber);
 
-  map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays);
+  map { $form->{TEMPLATE_ARRAYS}->{$_} = [] } (@arrays, @tax_arrays, @prepared_arrays);
 
   my $totalweight = 0;
   my $sameitem = "";
@@ -1229,10 +1266,11 @@ sub order_details {
     $i = $item->[0];
 
     if ($item->[1] ne $sameitem) {
+      push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'partsgroup');
       push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, qq|$item->[1]|);
       $sameitem = $item->[1];
 
-      map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
+      map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
     }
 
     $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
@@ -1257,6 +1295,9 @@ sub order_details {
 
       my $price_factor = $price_factors{$form->{"price_factor_id_$i"}} || { 'factor' => 1 };
 
+      push(@{ $form->{TEMPLATE_ARRAYS}->{$_} },                $prepared_template_arrays{$_}[$i - 1]) for @prepared_arrays;
+
+      push @{ $form->{TEMPLATE_ARRAYS}->{entry_type} },        'normal';
       push @{ $form->{TEMPLATE_ARRAYS}->{runningnumber} },     $position;
       push @{ $form->{TEMPLATE_ARRAYS}->{number} },            $form->{"partnumber_$i"};
       push @{ $form->{TEMPLATE_ARRAYS}->{description} },       $form->{"description_$i"};
@@ -1272,7 +1313,8 @@ sub order_details {
       push @{ $form->{TEMPLATE_ARRAYS}->{reqdate} },           $form->{"reqdate_$i"};
       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice} },         $form->{"sellprice_$i"};
       push @{ $form->{TEMPLATE_ARRAYS}->{sellprice_nofmt} },   $form->parse_amount($myconfig, $form->{"sellprice_$i"});
-      push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->{"listprice_$i"};
+      push @{ $form->{TEMPLATE_ARRAYS}->{listprice} },         $form->format_amount($myconfig, $form->{"listprice_$i"}, 2);
+      push @{ $form->{TEMPLATE_ARRAYS}->{listprice_nofmt} },   $form->{"listprice_$i"};
       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor} },      $price_factor->{formatted_factor};
       push @{ $form->{TEMPLATE_ARRAYS}->{price_factor_name} }, $price_factor->{description};
       push @{ $form->{TEMPLATE_ARRAYS}->{partsgroup} },        $form->{"partsgroup_$i"};
@@ -1396,34 +1438,24 @@ sub order_details {
 
         while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
           if ($form->{groupitems} && $ref->{partsgroup} ne $sameitem) {
-            map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
+            map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
             $sameitem = ($ref->{partsgroup}) ? $ref->{partsgroup} : "--";
+            push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'assembly-item-partsgroup');
             push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $sameitem);
           }
 
+          push(@{ $form->{TEMPLATE_ARRAYS}->{entry_type}  }, 'assembly-item');
           push(@{ $form->{TEMPLATE_ARRAYS}->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq|, $ref->{partnumber}, $ref->{description}|);
-          map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } @arrays));
+          map({ push(@{ $form->{TEMPLATE_ARRAYS}->{$_} }, "") } grep({ $_ ne "description" } (@arrays, @prepared_arrays)));
         }
         $sth->finish;
       }
 
-      my $cvars;
-      if (! $form->{"orderitems_id_$i"}) {
-        # get values for CVars from master data for new items
-        $cvars = CVar->get_custom_variables(dbh      => $dbh,
-                                            module   => 'IC',
-                                            trans_id => $form->{"id_$i"},
-                                           );
-      } else {
-        # get values for CVars from custom_variables for existing items
-        $cvars = CVar->get_custom_variables(dbh        => $dbh,
-                                            module     => 'IC',
-                                            sub_module => 'orderitems',
-                                            trans_id   => $form->{"orderitems_id_$i"},
-                                           );
-      }
-      # map only non-editable CVars to form (editable ones are already there)
-      map { $form->{"ic_cvar_$_->{name}_$i"} = $_->{value} unless $_->{flag_editable} } @{ $cvars };
+      CVar->get_non_editable_ic_cvars(form               => $form,
+                                      dbh                => $dbh,
+                                      row                => $i,
+                                      sub_module         => 'orderitems',
+                                      may_converted_from => ['orderitems', 'invoice']);
 
       push @{ $form->{TEMPLATE_ARRAYS}->{"ic_cvar_$_->{name}"} },
         CVar->format_to_template(CVar->parse($form->{"ic_cvar_$_->{name}_$i"}, $_), $_)