Volltext-Suche im Auftragsbericht
[kivitendo-erp.git] / SL / OE.pm
index d1eaa56..07fde70 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -36,7 +36,6 @@
 package OE;
 
 use List::Util qw(max first);
-use YAML;
 
 use SL::AM;
 use SL::Common;
@@ -45,6 +44,7 @@ use SL::DB::Order;
 use SL::DB::PeriodicInvoicesConfig;
 use SL::DB::Project;
 use SL::DB::ProjectType;
+use SL::DB::RequirementSpecOrder;
 use SL::DB::Status;
 use SL::DB::Tax;
 use SL::DBUtils;
@@ -53,6 +53,7 @@ use SL::IC;
 use SL::TransNumber;
 use SL::Util qw(trim);
 use SL::DB;
+use SL::YAML;
 use Text::ParseWords;
 
 use strict;
@@ -100,6 +101,12 @@ sub transactions {
         FROM record_links rl1
         LEFT JOIN record_links rl2 ON (rl1.to_table = rl2.from_table AND rl1.to_id = rl2.from_id)
         WHERE rl1.from_table = 'oe' AND rl2.to_table = 'ar'
+        UNION
+        SELECT rl1.from_id, rl3.to_id
+        FROM record_links rl1
+        JOIN record_links rl2 ON (rl1.to_table = rl2.from_table AND rl1.to_id = rl2.from_id)
+        JOIN record_links rl3 ON (rl2.to_table = rl3.from_table AND rl2.to_id = rl3.from_id)
+        WHERE rl1.from_table = 'oe' AND rl2.to_table = 'ar' AND rl3.to_table = 'ar'
       ) rl
       LEFT JOIN ar ON ar.id = rl.to_id
 
@@ -109,20 +116,31 @@ sub transactions {
     }
   }
 
+  my ($phone_notes_columns, $phone_notes_join);
+  $form->{phone_notes} = trim($form->{phone_notes});
+  if ($form->{phone_notes}) {
+    $phone_notes_columns = qq| , phone_notes.subject AS phone_notes_subject, phone_notes.body AS phone_notes_body |;
+    $phone_notes_join    = qq| JOIN notes phone_notes ON (o.id = phone_notes.trans_id AND phone_notes.trans_module LIKE 'oe') |;
+  }
+
   $query =
     qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate, | .
     qq|  o.amount, ct.${vc}number, ct.name, o.netamount, o.${vc}_id, o.globalproject_id, | .
     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.exchangerate, | .
     qq|  o.itime::DATE AS insertdate, | .
-    qq|  ex.$rate AS exchangerate, | .
+    qq|  o.intnotes, | .
+    qq|  department.description as department, | .
+    qq|  ex.$rate AS daily_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,  | .
     qq|  tz.description AS taxzone | .
     $periodic_invoices_columns .
+    $phone_notes_columns .
     qq|  , o.order_probability, o.expected_billing_date, (o.netamount * o.order_probability / 100) AS expected_netamount | .
     qq|FROM oe o | .
     qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
@@ -134,7 +152,9 @@ sub transactions {
     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|LEFT JOIN department   ON (o.department_id = department.id) | .
     qq|$periodic_invoices_joins | .
+    $phone_notes_join .
     qq|WHERE (o.quotation = ?) |;
   push(@values, $quotation);
 
@@ -181,7 +201,8 @@ SQL
     push(@values, (like($form->{"cp_name"}))x2);
   }
 
-  if (!$main::auth->assert('sales_all_edit', 1)) {
+  if ( !(    ($vc eq 'customer' && ($main::auth->assert('sales_all_edit',    1) || $main::auth->assert('sales_order_view',    1)))
+          || ($vc eq 'vendor'   && ($main::auth->assert('purchase_all_edit', 1) || $main::auth->assert('purchase_order_view', 1))) ) ) {
     $query .= " AND o.employee_id = (select id from employee where login= ?)";
     push @values, $::myconfig{login};
   }
@@ -287,6 +308,35 @@ SQL
     push @values, conv_date($form->{expected_billing_date_to});
   }
 
+  if ($form->{intnotes}) {
+    $query .= qq| AND o.intnotes ILIKE ?|;
+    push(@values, like($form->{intnotes}));
+  }
+
+  if ($form->{phone_notes}) {
+    $query .= qq| AND (phone_notes.subject ILIKE ? OR phone_notes.body ILIKE ?)|;
+    push(@values, like($form->{phone_notes}), like($form->{phone_notes}));
+  }
+
+  $form->{fulltext} = trim($form->{fulltext});
+  if ($form->{fulltext}) {
+    my @fulltext_fields = qw(o.notes
+                             o.intnotes
+                             o.shippingpoint
+                             o.shipvia
+                             o.transaction_description
+                             o.quonumber
+                             o.ordnumber
+                             o.cusordnumber);
+    $query .= ' AND (';
+    $query .= join ' ILIKE ? OR ', @fulltext_fields;
+    $query .= ' ILIKE ?';
+    $query .= qq| OR EXISTS (SELECT files.id FROM files LEFT JOIN file_full_texts ON (file_full_texts.file_id = files.id) WHERE files.object_id = o.id AND files.object_type = 'sales_order' AND file_full_texts.full_text ILIKE ?)|;
+    $query .= qq| OR EXISTS (SELECT notes.id FROM notes WHERE notes.trans_id = o.id AND notes.trans_module LIKE 'oe' AND (notes.subject ILIKE ? OR notes.body ILIKE ?))|;
+    $query .= ')';
+    push(@values, like($form->{fulltext})) for 1 .. (scalar @fulltext_fields) + 3;
+  }
+
   if ($form->{parts_partnumber}) {
     $query .= <<SQL;
       AND EXISTS (
@@ -353,6 +403,8 @@ SQL
     "insertdate"              => "o.itime",
     "taxzone"                 => "tz.description",
     "payment_terms"           => "pt.description",
+    "department"              => "department.description",
+    "intnotes"                => "o.intnotes",
   );
   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}"  . ", o.itime ${sortdir}";
@@ -368,9 +420,15 @@ SQL
   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
     $ref->{billed_amount}    = $billed_amount{$ref->{id}};
     $ref->{billed_netamount} = $billed_netamount{$ref->{id}};
-    $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
-    $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
-    $ref->{exchangerate} = 1 unless $ref->{exchangerate};
+    if ($ref->{billed_amount} < 0) { # case: credit note(s) higher than invoices
+      $ref->{remaining_amount} = $ref->{amount} + $ref->{billed_amount};
+      $ref->{remaining_netamount} = $ref->{netamount} + $ref->{billed_netamount};
+    } else {
+      $ref->{remaining_amount} = $ref->{amount} - $ref->{billed_amount};
+      $ref->{remaining_netamount} = $ref->{netamount} - $ref->{billed_netamount};
+    }
+    $ref->{exchangerate} ||= $ref->{daily_exchangerate};
+    $ref->{exchangerate} ||= 1;
     push @{ $form->{OE} }, $ref if $ref->{id} != $id{ $ref->{id} };
     $id{ $ref->{id} } = $ref->{id};
   }
@@ -456,6 +514,7 @@ sub _save {
   my $number_field         = $form->{type} =~ m{order} ? 'ordnumber' : 'quonumber';
   my $trans_number         = SL::TransNumber->new(type => $form->{type}, dbh => $dbh, number => $form->{$number_field}, id => $form->{id});
   $form->{$number_field} ||= $trans_number->create_unique; # set $form->{ordnumber} or $form->{quonumber}
+  my $is_new               = !$form->{id};
 
   if ($form->{id}) {
     $query = qq|DELETE FROM custom_variables
@@ -596,6 +655,9 @@ sub _save {
         require SL::DB::Customer;
         my $customer = SL::DB::Manager::Customer->find_by(id => $form->{customer_id});
         die "Can't find customer" unless $customer;
+        die $main::locale->text("Error while creating project with project number of new order number, project number #1 already exists!", $form->{ordnumber})
+          if SL::DB::Manager::Project->find_by(projectnumber => $form->{ordnumber});
+
         my $new_project = SL::DB::Project->new(
           projectnumber     => $form->{ordnumber},
           description       => $customer->name,
@@ -606,7 +668,7 @@ sub _save {
         );
         $new_project->save;
         $form->{"globalproject_id"} = $new_project->id;
-      };
+      }
 
       CVar->get_non_editable_ic_cvars(form               => $form,
                                       dbh                => $dbh,
@@ -724,10 +786,10 @@ SQL
   $query =
     qq|UPDATE oe SET
          ordnumber = ?, quonumber = ?, cusordnumber = ?, transdate = ?, vendor_id = ?,
-         customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
+         customer_id = ?, amount = ?, netamount = ?, reqdate = ?, tax_point = ?, taxincluded = ?,
          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
-         taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
+         taxzone_id = ?, shipto_id = ?, billing_address_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
          , order_probability = ?, expected_billing_date = ?
        WHERE id = ?|;
@@ -735,14 +797,14 @@ SQL
   @values = ($form->{ordnumber} || '', $form->{quonumber},
              $form->{cusordnumber}, conv_date($form->{transdate}),
              conv_i($form->{vendor_id}), conv_i($form->{customer_id}),
-             $amount, $netamount, conv_date($reqdate),
+             $amount, $netamount, conv_date($reqdate), conv_date($form->{tax_point}),
              $form->{taxincluded} ? 't' : 'f', $form->{shippingpoint},
              $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}),
              conv_i($form->{language_id}), conv_i($form->{taxzone_id}),
-             conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
+             conv_i($form->{shipto_id}), conv_i($form->{billing_address_id}), conv_i($form->{payment_id}),
              conv_i($form->{delivery_vendor_id}),
              conv_i($form->{delivery_customer_id}),
              conv_i($form->{delivery_term_id}),
@@ -806,17 +868,83 @@ SQL
                                        config_yaml => $form->{periodic_invoices_config})
     if ($form->{type} eq 'sales_order');
 
+  $self->_link_created_sales_order_to_requirement_specs_for_sales_quotations(
+    type               => $form->{type},
+    converted_from_ids => \@convert_from_oe_ids,
+    sales_order_id     => $form->{id},
+    is_new             => $is_new,
+  );
+
+  $self->_set_project_in_linked_requirement_spec(
+    type           => $form->{type},
+    project_id     => $form->{globalproject_id},
+    sales_order_id => $form->{id},
+  );
+
   $main::lxdebug->leave_sub();
 
   return 1;
 }
 
+sub _link_created_sales_order_to_requirement_specs_for_sales_quotations {
+  my ($self, %params) = @_;
+
+  # If this is a sales order created from a sales quotation and if
+  # that sales quotation was created from a requirement spec document
+  # then link the newly created sales order to the requirement spec
+  # document, too.
+
+  return if !$params{is_new};
+  return if  $params{type} ne 'sales_order';
+  return if !@{ $params{converted_from_ids} };
+
+  my $oe_objects       = SL::DB::Manager::Order->get_all(where => [ id => $params{converted_from_ids} ]);
+  my @sales_quotations = grep { $_->is_type('sales_quotation') } @{ $oe_objects };
+
+  return if !@sales_quotations;
+
+  my $rs_orders = SL::DB::Manager::RequirementSpecOrder->get_all(where => [ order_id => [ map { $_->id } @sales_quotations ] ]);
+
+  return if !@{ $rs_orders };
+
+  $rs_orders->[0]->db->with_transaction(sub {
+    foreach my $rs_order (@{ $rs_orders }) {
+      SL::DB::RequirementSpecOrder->new(
+        order_id            => $params{sales_order_id},
+        requirement_spec_id => $rs_order->requirement_spec_id,
+        version_id          => $rs_order->version_id,
+      )->save;
+    }
+
+    1;
+  });
+}
+
+sub _set_project_in_linked_requirement_spec {
+  my ($self, %params) = @_;
+
+  return if  $params{type} ne 'sales_order';
+  return if !$params{project_id} || !$params{sales_order_id};
+
+  my $query = <<SQL;
+    UPDATE requirement_specs
+    SET project_id = ?
+    WHERE id IN (
+      SELECT so.requirement_spec_id
+      FROM requirement_spec_orders so
+      WHERE so.order_id = ?
+    )
+SQL
+
+  do_query($::form, $::form->get_standard_dbh, $query, $params{project_id}, $params{sales_order_id});
+}
+
 sub save_periodic_invoices_config {
   my ($self, %params) = @_;
 
   return if !$params{oe_id};
 
-  my $config = $params{config_yaml} ? YAML::Load($params{config_yaml}) : undef;
+  my $config = $params{config_yaml} ? SL::YAML::Load($params{config_yaml}) : undef;
   return if 'HASH' ne ref $config;
 
   my $obj  = SL::DB::Manager::PeriodicInvoicesConfig->find_by(oe_id => $params{oe_id})
@@ -836,7 +964,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 send_email email_recipient_contact_id email_recipient_address email_sender email_subject email_body) };
-      $form->{periodic_invoices_config} = YAML::Dump($config);
+      $form->{periodic_invoices_config} = SL::YAML::Dump($config);
     }
   }
 }
@@ -958,8 +1086,14 @@ sub _retrieve {
   $form->{useasnew} = 1 if $is_collective_order == 1;
 
   if (!$form->{id}) {
-    my $extra_days     = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval : 1;
-    $form->{reqdate}   = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
+    my $extra_days = $form->{type} eq 'sales_quotation' ? $::instance_conf->get_reqdate_interval       :
+                     $form->{type} eq 'sales_order'     ? $::instance_conf->get_delivery_date_interval : 1;
+    if (   ($form->{type} eq 'sales_order'     &&  !$::instance_conf->get_deliverydate_on)
+        || ($form->{type} eq 'sales_quotation' &&  !$::instance_conf->get_reqdate_on)) {
+      $form->{reqdate}   = '';
+    } else {
+      $form->{reqdate}   = DateTime->today_local->next_workday(extra_days => $extra_days)->to_kivitendo;
+    }
     $form->{transdate} = DateTime->today_local->to_kivitendo;
   }
 
@@ -997,10 +1131,10 @@ sub _retrieve {
            o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
            (SELECT cu.name FROM currencies cu WHERE cu.id=o.currency_id) AS currency, e.name AS employee, o.employee_id, o.salesman_id,
            o.${vc}_id, cv.name AS ${vc}, o.amount AS invtotal,
-           o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
+           o.closed, o.reqdate, o.tax_point, o.quonumber, o.department_id, o.cusordnumber,
            o.mtime, o.itime,
            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.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id, o.billing_address_id,
            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
@@ -1076,9 +1210,10 @@ sub _retrieve {
       map { $form->{$_} =~ s/ +$//g } qw(printed emailed queued);
     }    # if !@ids
 
-    my $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
+    my $transdate = $form->{tax_point} ? $dbh->quote($form->{tax_point}) : $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
 
     $form->{taxzone_id} = 0 unless ($form->{taxzone_id});
+    unshift @values, ($form->{taxzone_id}) x 2;
 
     # retrieve individual items
     # this query looks up all information about the items
@@ -1090,6 +1225,7 @@ sub _retrieve {
            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.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,
@@ -1100,8 +1236,8 @@ sub _retrieve {
          JOIN parts p ON (o.parts_id = p.id)
          JOIN oe ON (o.trans_id = oe.id)
          LEFT JOIN chart c1 ON ((SELECT inventory_accno_id                   FROM buchungsgruppen WHERE id=p.buchungsgruppen_id) = c1.id)
-         LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$form->{taxzone_id}' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id)
-         LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = '$form->{taxzone_id}' and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id)
+         LEFT JOIN chart c2 ON ((SELECT tc.income_accno_id  FROM taxzone_charts tc WHERE tc.taxzone_id = ? and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c2.id)
+         LEFT JOIN chart c3 ON ((SELECT tc.expense_accno_id FROM taxzone_charts tc WHERE tc.taxzone_id = ? and tc.buchungsgruppen_id = p.buchungsgruppen_id) = c3.id)
          LEFT JOIN project pr ON (o.project_id = pr.id)
          LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id) | .
       ($form->{id}
@@ -1175,8 +1311,9 @@ sub _retrieve {
       # get tax rates and description
       my $accno_id = ($form->{vc} eq "customer") ? $ref->{income_accno} : $ref->{expense_accno};
       $query =
-        qq|SELECT c.accno, t.taxdescription, t.rate, t.taxnumber | .
-        qq|FROM tax t LEFT JOIN chart c on (c.id = t.chart_id) | .
+        qq|SELECT c.accno, t.taxdescription, t.rate, t.id as tax_id, c.accno as taxnumber | .
+        qq|FROM tax t | .
+        qq|LEFT JOIN chart c on (c.id = t.chart_id) | .
         qq|WHERE t.id IN (SELECT tk.tax_id FROM taxkeys tk | .
         qq|               WHERE tk.chart_id = (SELECT id FROM chart WHERE accno = ?) | .
         qq|                 AND startdate <= $transdate ORDER BY startdate DESC LIMIT 1) | .
@@ -1194,6 +1331,7 @@ sub _retrieve {
           $form->{"$ptr->{accno}_rate"}        = $ptr->{rate};
           $form->{"$ptr->{accno}_description"} = $ptr->{taxdescription};
           $form->{"$ptr->{accno}_taxnumber"}   = $ptr->{taxnumber};
+          $form->{"$ptr->{accno}_tax_id"}      = $ptr->{tax_id};
           $form->{taxaccounts} .= "$ptr->{accno} ";
         }
 
@@ -1278,7 +1416,6 @@ sub order_details {
   push(@project_ids, $form->{"globalproject_id"}) if ($form->{"globalproject_id"});
 
   $form->get_lists('price_factors' => 'ALL_PRICE_FACTORS');
-  $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
   my %price_factors;
 
   foreach my $pfac (@{ $form->{ALL_PRICE_FACTORS} }) {
@@ -1319,6 +1456,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} = { };
 
@@ -1330,7 +1468,7 @@ sub order_details {
        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);
+       price_factor price_factor_name partsgroup weight weight_nofmt lineweight lineweight_nofmt optional);
 
   push @arrays, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
   push @arrays, map { "project_cvar_$_->{name}" } @{ $project_cvar_configs };
@@ -1397,6 +1535,7 @@ sub order_details {
       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"};
+      push @{ $form->{TEMPLATE_ARRAYS}->{optional} },          $form->{"optional_$i"};
 
       my $sellprice     = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
       my ($dec)         = ($sellprice =~ /\.(\d+)/);
@@ -1425,7 +1564,18 @@ sub order_details {
       push @{ $form->{TEMPLATE_ARRAYS}->{discount_nofmt} }, ($discount != 0) ? $discount * -1 : '';
       push @{ $form->{TEMPLATE_ARRAYS}->{p_discount} },     $form->{"discount_$i"};
 
-      $form->{ordtotal}         += $linetotal;
+      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 unless $form->{"optional_$i"};
       $form->{nodiscount_total} += $nodiscount_linetotal;
       $form->{discount_total}   += $discount;
 
@@ -1473,14 +1623,16 @@ sub order_details {
 
       map { $taxrate += $form->{"${_}_rate"} } split(/ /, $form->{"taxaccounts_$i"});
 
-      if ($form->{taxincluded}) {
+      unless ($form->{"optional_$i"}) {
+        if ($form->{taxincluded}) {
 
-        # calculate tax
-        $taxamount = $linetotal * $taxrate / (1 + $taxrate);
-        $taxbase = $linetotal / (1 + $taxrate);
-      } else {
-        $taxamount = $linetotal * $taxrate;
-        $taxbase   = $linetotal;
+          # calculate tax
+          $taxamount = $linetotal * $taxrate / (1 + $taxrate);
+          $taxbase = $linetotal / (1 + $taxrate);
+        } else {
+          $taxamount = $linetotal * $taxrate;
+          $taxbase   = $linetotal;
+        }
       }
 
       if ($taxamount != 0) {
@@ -1493,15 +1645,15 @@ sub order_details {
       $tax_rate = $taxrate * 100;
       push(@{ $form->{TEMPLATE_ARRAYS}->{tax_rate} }, qq|$tax_rate|);
 
-      if ($form->{"assembly_$i"}) {
+      if ($form->{"part_type_$i"} eq 'assembly') {
         $sameitem = "";
 
         # get parts and push them onto the stack
         my $sortorder = "";
         if ($form->{groupitems}) {
-          $sortorder = qq|ORDER BY pg.partsgroup, a.oid|;
+          $sortorder = qq|ORDER BY pg.partsgroup, a.position|;
         } else {
-          $sortorder = qq|ORDER BY a.oid|;
+          $sortorder = qq|ORDER BY a.position|;
         }
 
         $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty, | .
@@ -1560,10 +1712,13 @@ sub order_details {
     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate} },        $form->format_amount($myconfig, $form->{"${item}_rate"} * 100));
     push(@{ $form->{TEMPLATE_ARRAYS}->{taxrate_nofmt} },  $form->{"${item}_rate"} * 100);
     push(@{ $form->{TEMPLATE_ARRAYS}->{taxnumber} },      $form->{"${item}_taxnumber"});
+    push(@{ $form->{TEMPLATE_ARRAYS}->{tax_id} },         $form->{"${item}_tax_id"});
 
-    my $tax_obj     = SL::DB::Manager::Tax->find_by(taxnumber => $form->{"${item}_taxnumber"});
-    my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription',  $form->{language_id}, 0) : '';
-    push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
+    if ( $form->{"${item}_tax_id"} ) {
+      my $tax_obj = SL::DB::Manager::Tax->find_by(id => $form->{"${item}_tax_id"}) or die "Can't find tax with id " . $form->{"${item}_tax_id"};
+      my $description = $tax_obj ? $tax_obj->translated_attribute('taxdescription',  $form->{language_id}, 0) : '';
+      push(@{ $form->{TEMPLATE_ARRAYS}->{taxdescription} }, $description . q{ } . 100 * $form->{"${item}_rate"} . q{%});
+    }
   }
 
   $form->{nodiscount_subtotal} = $form->format_amount($myconfig, $form->{nodiscount_total}, 2);
@@ -1594,27 +1749,16 @@ sub order_details {
 
   $form->{username} = $myconfig->{name};
 
+  $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();
 }
 
-sub project_description {
-  $main::lxdebug->enter_sub();
-
-  my ($self, $dbh, $id) = @_;
-
-  my $query = qq|SELECT description FROM project WHERE id = ?|;
-  my ($value) = selectrow_query($main::form, $dbh, $query, $id);
-
-  $main::lxdebug->leave_sub();
-
-  return $value;
-}
-
 1;
 
 __END__