commit bc8c26f36837b8f7e7ded304cdf475b479999277
Author: Sven Schöling <s.schoeling@googlemail.com>
Date:   Thu Oct 16 14:40:11 2014 +0200

    PriceRule: Preisregeln können jetzt auch Rabatte
    
    ausserdem Doku

diff --git a/SL/Controller/PriceRule.pm b/SL/Controller/PriceRule.pm
index c77a2da48..23dacc03c 100644
--- a/SL/Controller/PriceRule.pm
+++ b/SL/Controller/PriceRule.pm
@@ -90,6 +90,10 @@ sub action_add_item_row {
     ->render($self);
 }
 
+sub action_price_type_help {
+  $_[0]->render('price_rule/price_type_help', { layout => 0 });
+}
+
 #
 # filters
 #
@@ -145,13 +149,14 @@ sub prepare_report {
   my $report      = SL::ReportGenerator->new(\%::myconfig, $::form);
   $self->{report} = $report;
 
-  my @columns     = qw(name type priority price discount items);
-  my @sortable    = qw(name type priority price discount      );
+  my @columns     = qw(name type priority price reduction discount items);
+  my @sortable    = qw(name type priority price reduction discount      );
 
   my %column_defs = (
     name          => { obj_link => sub { $self->url_for(action => 'edit', 'price_rule.id' => $_[0]->id, callback => $callback) } },
     priority      => { sub  => sub { $_[0]->priority_as_text } },
     price         => { sub  => sub { $_[0]->price_as_number } },
+    reduction     => { sub  => sub { $_[0]->reduction_as_number } },
     discount      => { sub  => sub { $_[0]->discount_as_number } },
     obsolete      => { sub  => sub { $_[0]->obsolete_as_bool_yn } },
     items         => { sub  => sub { $_[0]->item_summary } },
@@ -258,6 +263,10 @@ sub init_partsgroups {
   SL::DB::Manager::PartsGroup->get_all;
 }
 
+sub all_price_types {
+  SL::DB::Manager::PriceRule->all_price_types;
+}
+
 sub init_models {
   my ($self) = @_;
 
@@ -269,6 +278,7 @@ sub init_models {
       priority => t8('Priority'),
       price    => t8('Price'),
       discount => t8('Discount'),
+      reduction => t8('Reduced Master Data'),
       obsolete => t8('Obsolete'),
       items    => t8('Rule Details'),
     },
diff --git a/SL/DB/Manager/PriceRule.pm b/SL/DB/Manager/PriceRule.pm
index 99065dbb5..d5b5489d6 100644
--- a/SL/DB/Manager/PriceRule.pm
+++ b/SL/DB/Manager/PriceRule.pm
@@ -7,6 +7,10 @@ use strict;
 
 use parent qw(SL::DB::Helper::Manager);
 
+use constant PRICE_NEW                 => 0;
+use constant PRICE_REDUCED_MASTER_DATA => 1;
+use constant PRICE_DISCOUNT            => 2;
+
 use SL::DB::Helper::Filtered;
 use SL::DB::Helper::Paginated;
 use SL::DB::Helper::Sorted;
@@ -76,6 +80,12 @@ sub get_all_matching {
   $self->get_all(query => [ id => \@ids ]);
 }
 
+sub all_price_types {
+  [ PRICE_NEW,                 t8('Price')               ],
+  [ PRICE_REDUCED_MASTER_DATA, t8('Reduced Master Data') ],
+  [ PRICE_DISCOUNT,            t8('Discount')            ],
+}
+
 sub _sort_spec {
   return ( columns => { SIMPLE => 'ALL', },
            default => [ 'name', 1 ],
diff --git a/SL/DB/MetaSetup/PriceRule.pm b/SL/DB/MetaSetup/PriceRule.pm
index 00469ad12..a3e70288a 100644
--- a/SL/DB/MetaSetup/PriceRule.pm
+++ b/SL/DB/MetaSetup/PriceRule.pm
@@ -9,15 +9,16 @@ use base qw(SL::DB::Object);
 __PACKAGE__->meta->table('price_rules');
 
 __PACKAGE__->meta->columns(
-  discount => { type => 'numeric', precision => 15, scale => 5 },
-  id       => { type => 'serial', not_null => 1 },
-  itime    => { type => 'timestamp' },
-  mtime    => { type => 'timestamp' },
-  name     => { type => 'text' },
-  obsolete => { type => 'boolean', default => 'false', not_null => 1 },
-  price    => { type => 'numeric', precision => 15, scale => 5 },
-  priority => { type => 'integer', default => 3, not_null => 1 },
-  type     => { type => 'text' },
+  discount  => { type => 'numeric', precision => 15, scale => 5 },
+  id        => { type => 'serial', not_null => 1 },
+  itime     => { type => 'timestamp' },
+  mtime     => { type => 'timestamp' },
+  name      => { type => 'text' },
+  obsolete  => { type => 'boolean', default => 'false', not_null => 1 },
+  price     => { type => 'numeric', precision => 15, scale => 5 },
+  priority  => { type => 'integer', default => 3, not_null => 1 },
+  reduction => { type => 'numeric', precision => 15, scale => 5 },
+  type      => { type => 'text' },
 );
 
 __PACKAGE__->meta->primary_key_columns([ 'id' ]);
diff --git a/SL/DB/PriceRule.pm b/SL/DB/PriceRule.pm
index 0a9f09d85..3deac758f 100644
--- a/SL/DB/PriceRule.pm
+++ b/SL/DB/PriceRule.pm
@@ -44,15 +44,19 @@ sub is_sales {
   : $_[0]->type eq 'vendor'   ? 0 : do { die 'wrong type' };
 }
 
-sub price_or_discount {
+sub price_type {
   my ($self, $value) = @_;
 
   if (@_ > 1) {
     my $number = $self->price || $self->discount;
-    if ($value) {
+    if ($value == SL::DB::Manager::PriceRule::PRICE_NEW()) {
+      $self->price($number);
+    } elsif ($value == SL::DB::Manager::PriceRule::PRICE_REDUCED_MASTER_DATA()) {
+      $self->reduction($number);
+    } elsif ($value == SL::DB::Manager::PriceRule::PRICE_DISCOUNT()) {
       $self->discount($number);
     } else {
-      $self->price($number);
+      die 'unknown price_or_discount value';
     }
     $self->price_or_discount_state($value);
   }
@@ -61,14 +65,29 @@ sub price_or_discount {
 
 sub price_or_discount_as_number {
   my ($self, @slurp) = @_;
+  my $type = $self->price_type;
+
+  $self->price(undef)     unless $type == SL::DB::Manager::PriceRule::PRICE_NEW();
+  $self->reduction(undef) unless $type == SL::DB::Manager::PriceRule::PRICE_REDUCED_MASTER_DATA();
+  $self->discount(undef)  unless $type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT();
 
-  $self->price_or_discount ? $self->price(undef)               : $self->discount(undef);
-  $self->price_or_discount ? $self->discount_as_number(@slurp) : $self->price_as_number(@slurp);
+
+  if ($type == SL::DB::Manager::PriceRule::PRICE_NEW()) {
+    return $self->price_as_number(@slurp)
+  } elsif ($type == SL::DB::Manager::PriceRule::PRICE_REDUCED_MASTER_DATA()) {
+    return $self->reduction_as_number(@slurp);
+  } elsif ($type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT()) {
+    return $self->discount_as_number(@slurp)
+  } else {
+    die 'unknown price_or_discount';
+  }
 }
 
 sub init_price_or_discount_state {
-    defined $_[0]->price ? 0
-  : defined $_[0]->discount ? 1 : 0
+    defined $_[0]->price     ? SL::DB::Manager::PriceRule::PRICE_NEW()
+  : defined $_[0]->reduction ? SL::DB::Manager::PriceRule::PRICE_REDUCED_MASTER_DATA()
+  : defined $_[0]->discount  ? SL::DB::Manager::PriceRule::PRICE_DISCOUNT()
+  :                            SL::DB::Manager::PriceRule::PRICE_NEW();
 }
 
 sub validate {
@@ -76,7 +95,7 @@ sub validate {
 
   my @errors;
   push @errors, $::locale->text('The name must not be empty.')              if !$self->name;
-  push @errors, $::locale->text('Price or discount must not be zero.')      if !$self->price && !$self->discount;
+  push @errors, $::locale->text('Price or discount must not be zero.')      if !$self->price && !$self->discount && !$self->reduction;
   push @errors, $::locale->text('Pirce rules must have at least one rule.') if !@{[ $self->items ]};
 
   return @errors;
diff --git a/SL/PriceSource/PriceRules.pm b/SL/PriceSource/PriceRules.pm
index 697369d0f..c9cecf931 100644
--- a/SL/PriceSource/PriceRules.pm
+++ b/SL/PriceSource/PriceRules.pm
@@ -4,6 +4,7 @@ use strict;
 use parent qw(SL::PriceSource::Base);
 
 use SL::PriceSource::Price;
+use SL::PriceSource::Discount;
 use SL::Locale::String;
 use SL::DB::PriceRule;
 use List::UtilsBy qw(min_by max_by);
@@ -15,45 +16,72 @@ sub description { t8('Price Rule') }
 sub available_rules {
   my ($self, %params) = @_;
 
-  SL::DB::Manager::PriceRule->get_all_matching(record => $self->record, record_item => $self->record_item);
+  $self->{available} ||= SL::DB::Manager::PriceRule->get_all_matching(record => $self->record, record_item => $self->record_item);
+}
+
+sub available_price_rules {
+  my $rules = $_[0]->available_rules;
+  grep { $_->price_type != SL::DB::Manager::PriceRule::PRICE_DISCOUNT() } @$rules
+}
+
+sub available_discount_rules {
+  my $rules = $_[0]->available_rules;
+  grep { $_->price_type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT() } @$rules
 }
 
 sub available_prices {
   my ($self, %params) = @_;
 
-  my $rules = $self->available_rules;
-
-  map { $self->make_price_from_rule($_) } @$rules;
+  map { $self->make_price_from_rule($_) } $self->available_price_rules;
 }
 
-sub available_discounts { }
+sub available_discounts {
+  my ($self, %params) = @_;
+
+  map { $self->make_discount_from_rule($_) } $self->available_discount_rules;
+}
 
 sub price_from_source {
   my ($self, $source, $spec) = @_;
 
   my $rule = SL::DB::Manager::PriceRule->find_by(id => $spec);
-  $self->make_price_from_rule($rule);
+  if ($rule->price_type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT()) {
+    return $self->make_discount_from_rule($rule);
+  } else {
+    return $self->make_price_from_rule($rule);
+  }
 }
 
 sub best_price {
   my ($self) = @_;
 
-  my $rules     = $self->available_rules;
+  my @rules     = $self->available_price_rules;
 
-  return unless @$rules;
+  return unless @rules;
 
-  my @max_prio  = max_by { $_->priority } @$rules;
+  my @max_prio  = max_by { $_->priority } @rules;
   my $min_price = min_by { $self->price_for_rule($_) } @max_prio;
 
   $self->make_price_from_rule($min_price);
 }
 
-sub best_discount { }
+sub best_discount {
+  my ($self) = @_;
+
+  my @rules     = $self->available_discount_rules;
+
+  return unless @rules;
+
+  my @max_prio     = max_by { $_->priority } @rules;
+  my $max_discount = max_by { $_->discount } @max_prio;
+
+  $self->make_discount_from_rule($max_discount);
+}
 
 sub price_for_rule {
   my ($self, $rule) = @_;
-  $rule->price_or_discount
-    ? (1 - $rule->discount / 100) * ($rule->is_sales ? $self->part->sellprice : $self->part->lastcost)
+  $rule->price_type != SL::DB::Manager::PriceRule::PRICE_NEW()
+    ? (1 - $rule->reduction / 100) * ($rule->is_sales ? $self->part->sellprice : $self->part->lastcost)
     : $rule->price;
 }
 
@@ -68,4 +96,15 @@ sub make_price_from_rule {
   )
 }
 
+sub make_discount_from_rule {
+  my ($self, $rule) = @_;
+
+  SL::PriceSource::Discount->new(
+    discount     => $rule->discount / 100,
+    spec         => $rule->id,
+    description  => $rule->name,
+    price_source => $self,
+  )
+}
+
 1;
diff --git a/js/kivi.PriceRule.js b/js/kivi.PriceRule.js
index 736f3f21a..5c56b87b0 100644
--- a/js/kivi.PriceRule.js
+++ b/js/kivi.PriceRule.js
@@ -8,6 +8,13 @@ namespace('kivi.PriceRule', function(ns) {
     $.post('controller.pl', data, kivi.eval_json_result);
   }
 
+  ns.open_price_type_help_popup = function() {
+    kivi.popup_dialog({
+      url:    'controller.pl?action=PriceRule/price_type_help',
+      dialog: { title: kivi.t8('Price Types') },
+    });
+  }
+
   $(function() {
     $('#price_rule_item_add').click(function() {
       ns.add_new_row($('#price_rules_empty_item_select').val());
@@ -15,5 +22,6 @@ namespace('kivi.PriceRule', function(ns) {
     $('#price_rule_items').on('click', 'a.price_rule_remove_line', function(){
       $(this).closest('div').remove();
     })
+    $('#price_rule_price_type_help').click(ns.open_price_type_help_popup);
   });
 });
diff --git a/js/locale/de.js b/js/locale/de.js
index 34be75d99..a525b21fa 100644
--- a/js/locale/de.js
+++ b/js/locale/de.js
@@ -45,6 +45,7 @@ namespace("kivi").setupLocale({
 "Part picker":"Artikelauswahl",
 "Paste":"Einfügen",
 "Paste template":"Vorlage einfügen",
+"Price Types":"Preistypen",
 "Project link actions":"Projektverknüpfungs-Aktionen",
 "Quotations/Orders actions":"Aktionen für Angebote/Aufträge",
 "Re-numbering all sections and function blocks in the order they are currently shown cannot be undone.":"Das Neu-Nummerieren aller Abschnitte und Funktionsblöcke kann nicht rückgängig gemacht werden.",
diff --git a/locale/de/all b/locale/de/all
index 6da14086d..d96cfe7cb 100755
--- a/locale/de/all
+++ b/locale/de/all
@@ -551,6 +551,7 @@ $self->{texts} = {
   'Content'                     => 'Inhalt',
   'Continue'                    => 'Weiter',
   'Contra'                      => 'gegen',
+  'Contrary to Reduced Master Data this will be shown as discount in records.' => 'Im Gegensatz zu Abschlag wird der Rabatt in Belegen ausgewiesen',
   'Conversion of "birthday" contact person attribute' => 'Umstellung des Kontaktpersonenfeldes "Geburtstag"',
   'Conversion to PDF failed: #1' => 'Konvertierung zu PDF schlug fehl: #1',
   'Copies'                      => 'Kopien',
@@ -1070,6 +1071,7 @@ $self->{texts} = {
   'EuR'                         => 'EuR',
   'Everyone can log in.'        => 'Alle können sich anmelden.',
   'Exact'                       => 'Genau',
+  'Example'                     => 'Beispiel',
   'Example: http://kivitendo.de' => 'Beispiel:  http://kivitendo.de',
   'Excel'                       => 'Excel',
   'Exch'                        => 'Wechselkurs.',
@@ -1349,6 +1351,7 @@ $self->{texts} = {
   'It is not allowed that a summary account occurs in a drop-down menu!' => 'Ein Sammelkonto darf nicht in Aufklappmenüs aufgenommen werden!',
   'It is possible that even after such a correction there is something wrong with this transaction (e.g. taxes that don\'t match the selected taxkey). Therefore you should re-run the general ledger analysis.' => 'Auch nach einer Korrektur kann es mit dieser Buchung noch weitere Probleme geben (z.B. nicht zum Steuerschlüssel passende Steuern), weshalb ein erneutes Ausführen der Hauptbuchanalyse empfohlen wird.',
   'It is possible to make a quick DATEV export everytime you post a record to ensure things work nicely with their data requirements. This will result in a slight overhead though you can enable this for each type of record independantly.' => 'Es ist möglich, bei jeder Buchung einen schnellen DATEV-Export durchzuführen, um sicherzustellen, dass die Datensätze den DATEV-Anforderungen genügen. Da dies einen kleinen Overhead bedeutet, lässt sich die Einstellung für jeden Buchungstyp getrennt einstellen.',
+  'It will not be further modified by any other source, and will be offered in records like this.' => 'Er wird nicht weiter verändert werden und genau so im Beleg vorgeschlagen werden.',
   'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => 'Es wird einfach die Steuerschlüssel auf  0 setzen, was "keine Steuer" bedeutet und für solche Warenbestandsbuchungen der richtige Wert ist.',
   'Item deleted!'               => 'Artikel gelöscht!',
   'Item mode'                   => 'Artikelmodus',
@@ -1484,6 +1487,7 @@ $self->{texts} = {
   'Marked entries printed!'     => 'Markierte Einträge wurden gedruckt!',
   'Master Data'                 => 'Stammdaten',
   'Master Data Bin Text Deleted' => 'Gelöschte Stammdaten Freitext-Lagerplätze',
+  'Matching Price Rules can apply in one of three types:' => 'Preisregeln können Preise in drei Varianten vorschlagen:',
   'Max. Dunning Level'          => 'höchste Mahnstufe',
   'Maximal amount difference'   => 'maximale Betragsabweichung',
   'Maximum future booking interval' => 'Maximale Anzahl von Tagen an denen Buchungen in der Zukunft erlaubt sind.',
@@ -1529,6 +1533,7 @@ $self->{texts} = {
   'Name and Street'             => 'Name und Straße',
   'Name does not make sense without any bsooqr options' => 'Option "Name in gewählten Belegen" wird ignoriert.',
   'Name in Selected Records'    => 'Name in gewählten Belegen',
+  'Negative reductions are possible to model price increases.' => 'Negative Abschläge sind möglich um Aufschläge zu modellieren.',
   'Neither sections nor function blocks have been created yet.' => 'Es wurden bisher weder Abschnitte noch Funktionsblöcke angelegt.',
   'Net Income Statement'        => 'Einnahmenüberschußrechnung',
   'Net amount'                  => 'Nettobetrag',
@@ -1865,6 +1870,7 @@ $self->{texts} = {
   'Price Rules'                 => 'Preisregeln',
   'Price Source'                => 'Preisquelle',
   'Price Sources to be disabled in this client' => 'Preisquellen die in diesem Mandanten deaktiviert werden sollen',
+  'Price Types'                 => 'Preistypen',
   'Price factor (database ID)'  => 'Preisfaktor (Datenbank-ID)',
   'Price factor (name)'         => 'Preisfaktor (Name)',
   'Price factor deleted!'       => 'Preisfaktor gel&ouml;scht.',
@@ -1874,6 +1880,7 @@ $self->{texts} = {
   'Price information'           => 'Preisinformation',
   'Price or discount must not be zero.' => 'Preis/Rabatt darf nicht 0,00 sein',
   'Price sources deactivated in this client' => 'Preisquellen die in diesem Mandanten deaktiviert sind',
+  'Price type explanation'      => 'Preistyp Erklärung',
   'Pricegroup'                  => 'Preisgruppe',
   'Pricegroup deleted!'         => 'Preisgruppe gelöscht!',
   'Pricegroup missing!'         => 'Preisgruppe fehlt!',
@@ -1992,6 +1999,7 @@ $self->{texts} = {
   'Record type to create'       => 'Anzulegender Belegtyp',
   'Recorded Tax'                => 'Gespeicherte Steuern',
   'Recorded taxkey'             => 'Gespeicherter Steuerschlüssel',
+  'Reduced Master Data'         => 'Abschlag',
   'Reference'                   => 'Referenz',
   'Reference / Invoice Number'  => 'Referenz / Rechnungsnummer',
   'Reference day'               => 'Stichtag',
@@ -2505,6 +2513,7 @@ $self->{texts} = {
   'The discount in percent'     => 'Der prozentuale Rabatt',
   'The discount must be less than 100%.' => 'Der Rabatt muss kleiner als 100% sein.',
   'The discount must not be negative.' => 'Der Rabatt darf nicht negativ sein.',
+  'The discounted amount will be shown in documents.' => 'Der Rabattbetrag wird in Belegen ausgewiesen.',
   'The dunning process started' => 'Der Mahnprozess ist gestartet.',
   'The dunnings have been printed.' => 'Die Mahnung(en) wurden gedruckt.',
   'The end date is the last day for which invoices will possibly be created.' => 'Das Enddatum ist das letztmögliche Datum, an dem eine Rechnung erzeugt wird.',
@@ -2739,6 +2748,11 @@ $self->{texts} = {
   'This user is a member in the following groups' => 'Dieser Benutzer ist Mitglied in den folgenden Gruppen',
   'This user will have access to the following clients' => 'Dieser Benutzer wird Zugriff auf die folgenden Mandanten haben',
   'This vendor number is already in use.' => 'Diese Lieferantennummer wird bereits verwendet.',
+  'This will apply a 3% reduction to the master data price before entering it into the record item.' => 'Diese Zeile zieht vom Stammdatenpreis 3% ab, und schlägt den resultierenden Preis vor.',
+  'This will be treated as a discount in percent points.' => 'Diese Option schlägt den Wert in Prozentpunkten als Rabatt vor.',
+  'This will happen before the price is offered, and the reduction will not be printed in documents.' => 'Das passiert, bevor der Preis vorgeschlagen wird, und der Abschlag wird nicht in Belegen ausgewiesen.',
+  'This will reduce the appropriate Master Data price by this in percent points.' => 'Diese Option reduziert den zugehörigen Stammdatenpreis um den angegebenen Wert in Prozentpunkten.',
+  'This will set an exact price.' => 'Diese Option setzt einen festen Preis.',
   'Three Options:'              => 'Drei Optionen:',
   'Time Format'                 => 'Uhrzeitformat',
   'Time Tracking'               => 'Zeiterfassung',
diff --git a/sql/Pg-upgrade2/price_rules_discount.sql b/sql/Pg-upgrade2/price_rules_discount.sql
new file mode 100644
index 000000000..2f9d06b6d
--- /dev/null
+++ b/sql/Pg-upgrade2/price_rules_discount.sql
@@ -0,0 +1,6 @@
+-- @tag: price_rules_discount
+-- @description:  Preisregeln: Beim Löschen items mitlöschen
+-- @depends: release_3_1_0 price_rules_cascade_delete
+
+ALTER TABLE price_rules RENAME COLUMN discount TO reduction;
+ALTER TABLE price_rules ADD COLUMN discount NUMERIC(15,5);
diff --git a/templates/webpages/price_rule/_filter.html b/templates/webpages/price_rule/_filter.html
index 3dfc3b3e8..8dc346d36 100644
--- a/templates/webpages/price_rule/_filter.html
+++ b/templates/webpages/price_rule/_filter.html
@@ -59,6 +59,10 @@
    <th align="right">[% 'Price' | $T8 %]</th>
    <td>[% L.input_tag('filter.price:number', filter.price_number, size=20, style='width: 300px') %]</td>
   </tr>
+  <tr>
+   <th align="right">[% 'Reduced Master Data' | $T8 %]</th>
+   <td>[% L.input_tag('filter.reduction:number', filter.reduction_number, size=20, style='width: 300px') %]</td>
+  </tr>
   <tr>
    <th align="right">[% 'Discount' | $T8 %]</th>
    <td>[% L.input_tag('filter.discount:number', filter.discount_number, size=20, style='width: 300px') %]</td>
diff --git a/templates/webpages/price_rule/form.html b/templates/webpages/price_rule/form.html
index 62d798841..0d6dbab32 100644
--- a/templates/webpages/price_rule/form.html
+++ b/templates/webpages/price_rule/form.html
@@ -46,7 +46,7 @@
 </div>
 
 <h3>[% 'Then' | $T8 %]:</h3>
-<div>[% 'Set (set to)' | $T8 %] [% L.select_tag('price_rule.price_or_discount', [ [0, LxERP.t8('Price') ], [1, LxERP.t8('Discount') ]], default=SELF.price_rule.price_or_discount) %] [% 'to (set to)' | $T8 %] [% L.input_tag('price_rule.price_or_discount_as_number', SELF.price_rule.price_or_discount_as_number) %]
+<div>[% 'Set (set to)' | $T8 %] [% L.select_tag('price_rule.price_type', SELF.all_price_types, default=SELF.price_rule.price_type) %] [% 'to (set to)' | $T8 %] [% L.input_tag('price_rule.price_or_discount_as_number', SELF.price_rule.price_or_discount_as_number) %] <a id='price_rule_price_type_help' title='[% 'Price type explanation' | $T8 %]'>[?]</a>
 </div>
 
   <p>
diff --git a/templates/webpages/price_rule/price_type_help.html b/templates/webpages/price_rule/price_type_help.html
new file mode 100644
index 000000000..608d34541
--- /dev/null
+++ b/templates/webpages/price_rule/price_type_help.html
@@ -0,0 +1,33 @@
+[% USE T8 %]
+[% 'Matching Price Rules can apply in one of three types:' | $T8 %]
+
+<h3>[% 'Price' | $T8 %]</h3>
+
+<p>
+[% 'This will set an exact price.' | $T8 %]
+[% 'It will not be further modified by any other source, and will be offered in records like this.' | $T8 %]
+</p>
+
+<h3>[% 'Reduced Master Data' | $T8 %]</h3>
+
+<p>
+[% 'This will reduce the appropriate Master Data price by this in percent points.' | $T8 %]
+[% 'This will happen before the price is offered, and the reduction will not be printed in documents.' | $T8 %]
+[% 'Negative reductions are possible to model price increases.' | $T8 %]
+</p>
+
+<p>[% 'Example' | $T8 %]:</p>
+
+<pre>[% 'Set (set to)' | $T8 %] [% 'Reduced Master Data' | $T8 %] [% 'to (set to)' | $T8 %] 3</pre>
+
+<p>
+[% 'This will apply a 3% reduction to the master data price before entering it into the record item.' | $T8 %]
+</p>
+
+<h3>[% 'Discount' | $T8 %]</h3>
+
+<p>
+[% 'This will be treated as a discount in percent points.' | $T8 %]
+[% 'Contrary to Reduced Master Data this will be shown as discount in records.' | $T8 %]
+[% 'The discounted amount will be shown in documents.' | $T8 %]
+</p>
