commit 219d88ab03186a74e5dd474175e49bd74dacf15f
Author: Martin Helmling <martin.helmling@octosoft.eu>
Date:   Thu Dec 22 09:07:46 2016 +0100

    Artikel-Klassifizierung: "Preis separat ausweisen"
    
    Dieses neue Attribut an Artikelklassifizierung erlaubt in Aufträgen und Rechnungen
    bestimmte Artikel extra auszuweisen.
    Dazu werden diese als extra Variable der Dokumentengeneierung zur Verfügung gestellt.
    Siehe neue Dokumentation Kapitel 3.7
    
    Hintergrund:
           Preise von Artikeln wie "Verpackung" oder "Transport" müssen
           oftmals separat ausgewiesen werden, genauso wie der reine Warenwert

diff --git a/SL/Controller/PartClassification.pm b/SL/Controller/PartClassification.pm
index 5bc4e848f..da1cb626d 100644
--- a/SL/Controller/PartClassification.pm
+++ b/SL/Controller/PartClassification.pm
@@ -116,6 +116,7 @@ sub create_or_update {
 
   $::form->{part_classification}->{used_for_purchase} = 0 if ! $::form->{part_classification}->{used_for_purchase};
   $::form->{part_classification}->{used_for_sale}     = 0 if ! $::form->{part_classification}->{used_for_sale};
+  $::form->{part_classification}->{report_separate}   = 0 if ! $::form->{part_classification}->{report_separate};
 
   my $params = delete($::form->{part_classification}) || { };
 
diff --git a/SL/DB/Manager/PartClassification.pm b/SL/DB/Manager/PartClassification.pm
index 0924011e7..c00bea76a 100644
--- a/SL/DB/Manager/PartClassification.pm
+++ b/SL/DB/Manager/PartClassification.pm
@@ -9,16 +9,6 @@ sub object_class { 'SL::DB::PartClassification' }
 
 __PACKAGE__->make_manager_methods;
 
-#
-# get the one/two character shortcut of the parts classification
-#
-sub get_abbreviation {
-  my ($class,$id) = @_;
-  my $obj = $class->get_first(query => [ id => $id ]);
-  return '' unless $obj;
-  return $obj->abbreviation?$obj->abbreviation:'';
-}
-
 1;
 
 
@@ -30,27 +20,4 @@ __END__
 
 SL::DB::Manager::PartClassification
 
-
-=head1 SYNOPSIS
-
-This class has wrapper methodes to get the shortcuts
-
-=head1 METHODS
-
-=head2 get_abbreviation
-
- $class->get_abbreviation($classification_id);
-
-get the one/two character shortcut of the parts classification
-
-=head2 get_separate_abbreviation
-
- $class->get_separate_abbreviation($classification_id);
-
-get the one/two character shortcut of the parts classification if it is a separate article
-
-=head1 AUTHOR
-
-Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
-
 =cut
diff --git a/SL/DB/MetaSetup/PartClassification.pm b/SL/DB/MetaSetup/PartClassification.pm
index 676fa2148..0377ee557 100644
--- a/SL/DB/MetaSetup/PartClassification.pm
+++ b/SL/DB/MetaSetup/PartClassification.pm
@@ -12,6 +12,7 @@ __PACKAGE__->meta->columns(
   abbreviation      => { type => 'text' },
   description       => { type => 'text' },
   id                => { type => 'serial', not_null => 1 },
+  report_separate   => { type => 'boolean', default => 'false' },
   used_for_purchase => { type => 'boolean', default => 'true' },
   used_for_sale     => { type => 'boolean', default => 'true' },
 );
diff --git a/SL/DB/PartClassification.pm b/SL/DB/PartClassification.pm
index 73f11da3c..e7abe62c4 100644
--- a/SL/DB/PartClassification.pm
+++ b/SL/DB/PartClassification.pm
@@ -42,6 +42,12 @@ the parts classification specifies other ortogonal attributes
 The primary attributes are the rule
 of the article as "used_for_sales" or "used_for_purchase".
 
+Another attribute is "report_separate". This attribute may be used for some additional costs like
+transport, packaging. These article are reported separate in the list of an invoice if
+the print template is using the variables <%separate_XXX_subtotal%>  and XXX is the shortcut of the parts classification.
+The variables <%non_separate_subtotal%> has the sum of all other parts of an invoice.
+(See also LaTeX Documentation).
+
 Additional other attributes may follow
 
 To see this attributes in a short way there are shortcuts of one (or two characters, if needed for compare )
diff --git a/SL/IC.pm b/SL/IC.pm
index d352a560d..17867a070 100644
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -1135,6 +1135,7 @@ sub prepare_parts_for_printing {
     my $type_abbr = $::request->presenter->type_abbreviation($prt->part_type);
     push @{ $template_arrays{part_type} }, $type_abbr;
     push @{ $template_arrays{type_and_classific}},  $type_abbr.$::request->presenter->classification_abbreviation($prt->classification_id);
+    push @{ $template_arrays{separate}  },  $::request->presenter->separate_abbreviation($prt->classification_id);
   }
 
   $main::lxdebug->leave_sub();
diff --git a/SL/IS.pm b/SL/IS.pm
index bbb18951c..3759e5790 100644
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -153,6 +153,7 @@ sub invoice_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);
 
   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
   my $project_cvar_configs = CVar->get_configs(module => 'Projects');
@@ -336,6 +337,17 @@ sub invoice_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->{total}            += $linetotal;
       $form->{nodiscount_total} += $nodiscount_linetotal;
       $form->{discount_total}   += $discount;
@@ -543,6 +555,7 @@ sub invoice_details {
   $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
 
   $form->{username} = $myconfig->{name};
+  $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals;
 
   $main::lxdebug->leave_sub();
 }
diff --git a/SL/OE.pm b/SL/OE.pm
index a51d0cb2e..64cab296c 100644
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -1320,6 +1320,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} = { };
 
@@ -1426,6 +1427,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;
@@ -1599,6 +1611,7 @@ sub order_details {
   $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();
 }
diff --git a/SL/Presenter/Part.pm b/SL/Presenter/Part.pm
index c3b366a99..d8491ecb5 100644
--- a/SL/Presenter/Part.pm
+++ b/SL/Presenter/Part.pm
@@ -53,7 +53,6 @@ sub part_picker {
 #
 sub type_abbreviation {
   my ($self, $part_type) = @_;
-  $main::lxdebug->message(LXDebug->DEBUG2(),"parttype=".$part_type);
   return $::locale->text('Assembly (typeabbreviation)')   if $part_type eq 'assembly';
   return $::locale->text('Part (typeabbreviation)')       if $part_type eq 'part';
   return $::locale->text('Assortment (typeabbreviation)') if $part_type eq 'assortment';
@@ -85,6 +84,16 @@ sub classification_abbreviation {
   $obj && $obj->abbreviation ? t8($obj->abbreviation) : '';
 }
 
+#
+# shortcut for article type
+#
+sub separate_abbreviation {
+  my ($self, $id) = @_;
+  SL::DB::Manager::PartClassification->cache_all();
+  my $obj = SL::DB::PartClassification->load_cached($id);
+  $obj && $obj->abbreviation && $obj->report_separate ? t8($obj->abbreviation) : '';
+}
+
 #
 # generate selection tag
 #
@@ -152,6 +161,14 @@ Returns the shortcut of the classification
 
 =over 2
 
+=item C<separate_abbreviation $classification_id>
+
+Returns the shortcut of the classification if the classifiaction has the separate flag set.
+
+=back
+
+=over 2
+
 =item C<select_classification $name,%params>
 
 Returns a HTML Select Tag with all available Classifications
diff --git a/locale/de/all b/locale/de/all
index 5553b5fd6..72f60985b 100755
--- a/locale/de/all
+++ b/locale/de/all
@@ -1455,8 +1455,10 @@ $self->{texts} = {
   'If enabled a warning will be shown in sales and purchase orders if there are two or more positions of the same part (new controller only).' => 'Falls eingeschaltet, wird eine Warnung angezeigt, wenn der Auftrag mehrere gleiche Artikel enthält (nur neuer Controller).',
   'If enabled only those projects that are assigned to the currently selected customer are offered for selection in sales records.' => 'Wenn eingeschaltet, so werden in Verkaufsbelegen nur diejenigen Projekte zur Auswahl angeboten, die dem aktuell ausgewählten Kunden zugewiesen wurden.',
   'If enabled purchase and sales records cannot be saved if no transaction description has been entered.' => 'Wenn angeschaltet, so können Einkaufs- und Verkaufsbelege nicht gespeichert werden, solange keine Vorgangsbezeichnung eingegeben wurde.',
+  'If item not found, allow creation of new item' => 'Falls Artikel nicht gefunden, erlaube Erfassen eines Neuen',
   'If left empty the default sender from the kivitendo configuration will be used (key \'email_from\' in section \'periodic_invoices\'; current value: #1).' => 'Falls leer, so wird der Standardabsender aus der kivitendo-Konfiguration genutzt (Schlüssel »email_from« in Abschnitt »periodic_invoices«; aktueller Wert: #1).',
   'If missing then the start date will be used.' => 'Falls es fehlt, so wird die erste Rechnung für das Startdatum erzeugt.',
+  'If searching a part from a document and no part is found then offer to create a new part.' => 'Wenn bei der Artikelsuche aus einem Dokument heraus kein Artikel gefunden wird, dann wird ermöglicht, von dort aus einen neuen Artikel anzulegen.',
   'If the article type is set to \'mixed\' then a column called \'part_type\' or called \'pclass\' must be present.' => 'Falls der Rtikeltyp auf \'mixed\' gesetzt ist muss entweder eine Spalte \'part_type\' oder \'pclass\' im Import vorhanden sein',
   'If the automatic creation of invoices for fees and interest is switched on for a dunning level then the following accounts will be used for the invoice.' => 'Wenn das automatische Erstellen einer Rechnung &uuml;ber Mahngeb&uuml;hren und Zinsen f&uuml;r ein Mahnlevel aktiviert ist, so werden die folgenden Konten f&uuml;r die Rechnung benutzt.',
   'If the database user listed above does not have the right to create a database then enter the name and password of the superuser below:' => 'Falls der oben genannte Datenbankbenutzer nicht die Berechtigung zum Anlegen neuer Datenbanken hat, so k&ouml;nnen Sie hier den Namen und das Passwort des Datenbankadministratoraccounts angeben:',
@@ -2015,7 +2017,6 @@ $self->{texts} = {
   'POSTED'                      => 'Gebucht',
   'POSTED AS NEW'               => 'Als neu gebucht',
   'PRINTED'                     => 'Gedruckt',
-  'PType'                       => 'Typ',
   'Package name'                => 'Paketname',
   'Packing Lists'               => 'Lieferschein',
   'Page'                        => 'Seite',
@@ -2353,6 +2354,7 @@ $self->{texts} = {
   'Report and misc. Preferences' => 'Sonstige Einstellungen',
   'Report date'                 => 'Berichtsdatum',
   'Report for'                  => 'Bericht für',
+  'Report separately'           => 'Preis separat ausweisen',
   'Reports'                     => 'Berichte',
   'Representative'              => 'Vertreter',
   'Representative for Customer' => 'Vertreter für Kunden',
@@ -3480,6 +3482,7 @@ $self->{texts} = {
   'Weight unit'                 => 'Gewichtseinheit',
   'What <b>term</b> you are looking for?' => 'Nach welchem <b>Begriff</b> wollen Sie suchen?',
   'What this template contains' => 'Was diese Vorlage enthält',
+  'What type of item is this?'  => 'Was ist dieser Artikel?',
   'When converting a requirement spec into a quotation or an oder each section gets converted into a line position in the new record. This is the article used by default for this conversion.' => 'Wenn ein Pflichtenheft in ein Angebot oder Auftrag umgewandelt wird, wird für jeden Abschnitt eine Position im neuen Beleg angelegt. Dies ist der Artikel, der standardmäßig bei dieser Umwandlung genutzt wird.',
   'Which is located at doc/kivitendo-Dokumentation.pdf. Click here: ' => 'Diese befindet sich unter doc/kivitendo-Dokumentation.pdf. Klicken Sie hier:',
   'With Attachments'            => 'Journal mit Anhängen',
diff --git a/sql/Pg-upgrade2/part_classification_report_separate.sql b/sql/Pg-upgrade2/part_classification_report_separate.sql
new file mode 100644
index 000000000..9e0260e4b
--- /dev/null
+++ b/sql/Pg-upgrade2/part_classification_report_separate.sql
@@ -0,0 +1,4 @@
+-- @tag: part_classification_report_separate
+-- @description: "Artikelklassifikation mit weiterer boolschen Variable für separat ausweisen"
+-- @depends: part_classifications
+ALTER TABLE part_classifications ADD COLUMN report_separate BOOLEAN DEFAULT 'f' NOT NULL;
diff --git a/templates/webpages/part_classification/form.html b/templates/webpages/part_classification/form.html
index 0c52727dc..2725df2fe 100755
--- a/templates/webpages/part_classification/form.html
+++ b/templates/webpages/part_classification/form.html
@@ -22,6 +22,10 @@
     <td>[% LxERP.t8('Used for Sale') %]</td>
     <td>[% L.checkbox_tag("part_classification.used_for_sale", checked=(SELF.part_classification.used_for_sale ? 1:'')) %]</td>
    </tr>
+   <tr>
+    <td>[% LxERP.t8('Report separately') %]</td>
+    <td>[% L.checkbox_tag("part_classification.report_separate", checked=(SELF.part_classification.report_separate ? 1:'')) %]</td>
+   </tr>
   </table>
 
   <p>
diff --git a/templates/webpages/part_classification/list.html b/templates/webpages/part_classification/list.html
index 52ded516b..f7bb1c26c 100644
--- a/templates/webpages/part_classification/list.html
+++ b/templates/webpages/part_classification/list.html
@@ -18,6 +18,7 @@
      <th>[%- LxERP.t8('TypeAbbreviation') %]</th>
      <th>[%- LxERP.t8('Used for Purchase') %]</th>
      <th>[%- LxERP.t8('Used for Sale') %]</th>
+     <th>[%- LxERP.t8('Report separately') %]</th>
     </tr>
     </thead>
 
@@ -33,6 +34,7 @@
      <td>[%- HTML.escape(LxERP.t8(part_classification.abbreviation)) %]</td>
      <td>[% IF part_classification.used_for_purchase %][% LxERP.t8('Yes') %][% ELSE %][%  LxERP.t8('No') %][% END %]</td>
      <td>[% IF part_classification.used_for_sale     %][% LxERP.t8('Yes') %][% ELSE %][%  LxERP.t8('No') %][% END %]</td>
+     <td>[% IF part_classification.report_separate   %][% LxERP.t8('Yes') %][% ELSE %][%  LxERP.t8('No') %][% END %]</td>
     </tr>
     [%- END %]
     </tbody>
