Artikel-Klassifizierung: "Preis separat ausweisen"
authorMartin Helmling martin.helmling@octosoft.eu <martin.helmling@octosoft.eu>
Thu, 22 Dec 2016 08:07:46 +0000 (09:07 +0100)
committerMartin Helmling martin.helmling@octosoft.eu <martin.helmling@octosoft.eu>
Wed, 11 Jan 2017 07:20:38 +0000 (08:20 +0100)
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

12 files changed:
SL/Controller/PartClassification.pm
SL/DB/Manager/PartClassification.pm
SL/DB/MetaSetup/PartClassification.pm
SL/DB/PartClassification.pm
SL/IC.pm
SL/IS.pm
SL/OE.pm
SL/Presenter/Part.pm
locale/de/all
sql/Pg-upgrade2/part_classification_report_separate.sql [new file with mode: 0644]
templates/webpages/part_classification/form.html
templates/webpages/part_classification/list.html

index 5bc4e84..da1cb62 100644 (file)
@@ -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}) || { };
 
index 0924011..c00bea7 100644 (file)
@@ -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
index 676fa21..0377ee5 100644 (file)
@@ -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' },
 );
index 73f11da..e7abe62 100644 (file)
@@ -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 )
index d352a56..17867a0 100644 (file)
--- 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();
index bbb1895..3759e57 100644 (file)
--- 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();
 }
index a51d0cb..64cab29 100644 (file)
--- 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();
 }
index c3b366a..d8491ec 100644 (file)
@@ -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
index 5553b5f..72f6098 100755 (executable)
@@ -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 (file)
index 0000000..9e0260e
--- /dev/null
@@ -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;
index 0c52727..2725df2 100755 (executable)
     <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>
index 52ded51..f7bb1c2 100644 (file)
@@ -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>