]> wagnertech.de Git - kivitendo-erp.git/commitdiff
Kunden-Spezifische Artikeleigenschaften
authorMartin Helmling martin.helmling@octosoft.eu <martin.helmling@octosoft.eu>
Mon, 9 Jan 2017 10:55:21 +0000 (11:55 +0100)
committerSven Schöling <s.schoeling@linet-services.de>
Mon, 8 Jan 2018 17:02:26 +0000 (18:02 +0100)
neue Tabelle "PartCustomerPrices" mit SL/DB Dateien
in Artikelstammdaten eingebaut,

in Preisquellen analog zu den Lieferantenpreisen nun Kundenpreise eingebaut
(Unklar ist was bei Kundenpreisen der beste Preis ist !)

Analog zu Lieferanten "make" und "model" aus kundenspezifischen Daten
"customer" und "custnumber" zum Drucken pro Artikel anbieten

21 files changed:
SL/Controller/Part.pm
SL/DB/Helper/ALL.pm
SL/DB/Helper/Mappings.pm
SL/DB/Manager/PartCustomerPrice.pm [new file with mode: 0644]
SL/DB/MetaSetup/Part.pm
SL/DB/MetaSetup/PartCustomerPrice.pm [new file with mode: 0644]
SL/DB/Part.pm
SL/DB/PartCustomerPrice.pm [new file with mode: 0644]
SL/IR.pm
SL/IS.pm
SL/PriceSource/ALL.pm
SL/PriceSource/CustomerPrice.pm [new file with mode: 0644]
bin/mozilla/ic.pl
bin/mozilla/is.pl
js/kivi.Part.js
locale/de/all
sql/Pg-upgrade2/create_part_customerprices.sql [new file with mode: 0644]
templates/webpages/part/_basic_data.html
templates/webpages/part/_customerprice_row.html [new file with mode: 0644]
templates/webpages/part/_customerprices.html [new file with mode: 0644]
templates/webpages/part/_makemodel.html

index e4bb71226a2c39769fe8a257ecd03086cd18003f..7b4a2bc818b7ea28f930d99c6bf50353cc504e48 100644 (file)
@@ -24,6 +24,7 @@ use SL::Presenter::EscapedText qw(escape is_escaped);
 use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(parts models part p warehouses multi_items_models
                                   makemodels shops_not_assigned
+                                  customerprices
                                   orphaned
                                   assortment assortment_items assembly assembly_items
                                   all_pricegroups all_translations all_partsgroups all_units
@@ -455,6 +456,39 @@ sub action_add_makemodel_row {
     ->render;
 }
 
+sub action_add_customerprice_row {
+  my ($self) = @_;
+
+  my $customer_id = $::form->{add_customerprice};
+
+  my $customer = SL::DB::Manager::Customer->find_by(id => $customer_id)
+    or return $self->js->error(t8("No customer selected or found!"))->render;
+
+  if (grep { $customer_id == $_->customer_id } @{ $self->customerprices }) {
+    $self->js->flash('info', t8("This customer has already been added."));
+  }
+
+  my $position = scalar @{ $self->customerprices } + 1;
+
+  my $cu = SL::DB::PartCustomerPrice->new(
+                      customer_id         => $customer->id,
+                      customer_partnumber => '',
+                      price               => 0,
+                      sortorder           => $position,
+  ) or die "Can't create Customerprice object";
+
+  my $row_as_html = $self->p->render(
+                                     'part/_customerprice_row',
+                                      customerprice => $cu,
+                                      listrow       => $position % 2 ? 0
+                                                                     : 1,
+  );
+
+  $self->js->append('#customerprice_rows', $row_as_html)    # append in tbody
+           ->val('.add_customerprice_input', '')
+           ->run('kivi.Part.focus_last_customerprice_input')->render;
+}
+
 sub action_reorder_items {
   my ($self) = @_;
 
@@ -713,6 +747,7 @@ sub parse_form {
   $self->part->prices([]);
   $self->parse_form_prices;
 
+  $self->parse_form_customerprices;
   $self->parse_form_makemodels;
 }
 
@@ -782,6 +817,45 @@ sub parse_form_makemodels {
   };
 }
 
+sub parse_form_customerprices {
+  my ($self) = @_;
+
+  my $customerprices_map;
+  if ( $self->part->customerprices ) { # check for new parts or parts without customerprices
+    $customerprices_map = { map { $_->id => Rose::DB::Object::Helpers::clone($_) } @{$self->part->customerprices} };
+  };
+
+  $self->part->customerprices([]);
+
+  my $position = 0;
+  my $customerprices = delete($::form->{customerprices}) || [];
+  foreach my $customerprice ( @{$customerprices} ) {
+    next unless $customerprice->{customer_id};
+    $position++;
+    my $customer = SL::DB::Manager::Customer->find_by(id => $customerprice->{customer_id}) || die "Can't find customer from id";
+
+    my $cu = SL::DB::PartCustomerPrice->new( # parts_id   => $self->part->id, # will be assigned by row add_customerprices
+                                     id                   => $customerprice->{id},
+                                     customer_id          => $customerprice->{customer_id},
+                                     customer_partnumber  => $customerprice->{customer_partnumber} || '',
+                                     price                => $::form->parse_amount(\%::myconfig, $customerprice->{price_as_number}),
+                                     sortorder            => $position,
+                                   );
+    if ($customerprices_map->{$cu->id} && !$customerprices_map->{$cu->id}->lastupdate && $customerprices_map->{$cu->id}->price == 0 && $cu->price == 0) {
+      # lastupdate isn't set, original price is 0 and new lastcost is 0
+      # don't change lastupdate
+    } elsif ( !$customerprices_map->{$cu->id} && $cu->price == 0 ) {
+      # new customerprice, no lastcost entered, leave lastupdate empty
+    } elsif ($customerprices_map->{$cu->id} && $customerprices_map->{$cu->id}->price == $cu->price) {
+      # price hasn't changed, use original lastupdate
+      $cu->lastupdate($customerprices_map->{$cu->id}->lastupdate);
+    } else {
+      $cu->lastupdate(DateTime->now);
+    };
+    $self->part->add_customerprices($cu);
+  };
+}
+
 sub build_bin_select {
   $_[0]->p->select_tag('part.bin_id', [ $_[0]->warehouse->bins ],
     title_key => 'description',
@@ -806,7 +880,7 @@ sub init_part {
   # used by edit, save, delete and add
 
   if ( $::form->{part}{id} ) {
-    return SL::DB::Part->new(id => $::form->{part}{id})->load(with => [ qw(makemodels prices translations partsgroup shop_parts shop_parts.shop) ]);
+    return SL::DB::Part->new(id => $::form->{part}{id})->load(with => [ qw(makemodels customerprices prices translations partsgroup shop_parts shop_parts.shop) ]);
   } else {
     die "part_type missing" unless $::form->{part}{part_type};
     return SL::DB::Part->new(part_type => $::form->{part}{part_type});
@@ -885,6 +959,29 @@ sub init_makemodels {
   return \@makemodel_array;
 }
 
+sub init_customerprices {
+  my ($self) = @_;
+
+  my $position = 0;
+  my @customerprice_array = ();
+  my $customerprices = delete($::form->{customerprices}) || [];
+
+  foreach my $customerprice ( @{$customerprices} ) {
+    next unless $customerprice->{customer_id};
+    $position++;
+    my $cu = SL::DB::PartCustomerPrice->new( # parts_id   => $self->part->id, # will be assigned by row add_customerprices
+                                    id                  => $customerprice->{id},
+                                    customer_partnumber => $customerprice->{customer_partnumber},
+                                    customer_id         => $customerprice->{customer_id} || '',
+                                    price               => $::form->parse_amount(\%::myconfig, $customerprice->{price_as_number} || 0),
+                                    sortorder           => $position,
+                                  ) or die "Can't create cu";
+    # $cu->id($customerprice->{id}) if $customerprice->{id};
+    push(@customerprice_array, $cu);
+  };
+  return \@customerprice_array;
+}
+
 sub init_assembly_items {
   my ($self) = @_;
   my $position = 0;
index 4429f8f8b53b424a159882d751d6658648ecaf6c..0e6ed07394005f6621a12461e9c1962d08a0ee5f 100644 (file)
@@ -75,6 +75,7 @@ use SL::DB::Order;
 use SL::DB::OrderItem;
 use SL::DB::Part;
 use SL::DB::PartClassification;
+use SL::DB::PartCustomerPrice;
 use SL::DB::PartsGroup;
 use SL::DB::PartsPriceHistory;
 use SL::DB::PaymentTerm;
index f9d4745936422a0820fc05214bfa7dd610ec8896..6290bd3f43f4aa91e33a093866a09de3748b9539 100644 (file)
@@ -157,6 +157,7 @@ my %kivitendo_package_names = (
   parts                          => 'part',
   partsgroup                     => 'parts_group',
   part_classifications           => 'PartClassification',
+  part_customer_prices           => 'PartCustomerPrice',
   parts_price_history            => 'PartsPriceHistory',
   payment_terms                  => 'payment_term',
   periodic_invoices              => 'periodic_invoice',
diff --git a/SL/DB/Manager/PartCustomerPrice.pm b/SL/DB/Manager/PartCustomerPrice.pm
new file mode 100644 (file)
index 0000000..4f93ddc
--- /dev/null
@@ -0,0 +1,15 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::Manager::PartCustomerPrice;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+sub object_class { 'SL::DB::PartCustomerPrice' }
+
+__PACKAGE__->make_manager_methods;
+
+1;
index 6e758e499e3cb811a96f6212e68ce1485b30465a..ef6ed69f5aaee46b54c96f76608c40d3329b5c21 100644 (file)
@@ -37,14 +37,14 @@ __PACKAGE__->meta->columns(
   payment_id         => { type => 'integer' },
   price_factor_id    => { type => 'integer' },
   priceupdate        => { type => 'date', default => 'now' },
-  rop                => { type => 'float', scale => 4 },
+  rop                => { type => 'float', precision => 4, scale => 4 },
   sellprice          => { type => 'numeric', precision => 15, scale => 5 },
   shop               => { type => 'boolean', default => 'false' },
   stockable          => { type => 'boolean', default => 'false' },
   unit               => { type => 'varchar', length => 20, not_null => 1 },
   ve                 => { type => 'integer' },
   warehouse_id       => { type => 'integer' },
-  weight             => { type => 'float', scale => 4 },
+  weight             => { type => 'float', precision => 4, scale => 4 },
 );
 
 __PACKAGE__->meta->primary_key_columns([ 'id' ]);
diff --git a/SL/DB/MetaSetup/PartCustomerPrice.pm b/SL/DB/MetaSetup/PartCustomerPrice.pm
new file mode 100644 (file)
index 0000000..c3d4243
--- /dev/null
@@ -0,0 +1,38 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::PartCustomerPrice;
+
+use strict;
+
+use parent qw(SL::DB::Object);
+
+__PACKAGE__->meta->table('part_customer_prices');
+
+__PACKAGE__->meta->columns(
+  customer_id         => { type => 'integer', not_null => 1 },
+  customer_partnumber => { type => 'text', default => '' },
+  id                  => { type => 'serial', not_null => 1 },
+  lastupdate          => { type => 'date', default => 'now()' },
+  parts_id            => { type => 'integer', not_null => 1 },
+  price               => { type => 'numeric', default => '0', precision => 15, scale => 5 },
+  sortorder           => { type => 'integer', default => '0' },
+);
+
+__PACKAGE__->meta->primary_key_columns([ 'id' ]);
+
+__PACKAGE__->meta->allow_inline_column_values(1);
+
+__PACKAGE__->meta->foreign_keys(
+  customer => {
+    class       => 'SL::DB::Customer',
+    key_columns => { customer_id => 'id' },
+  },
+
+  parts => {
+    class       => 'SL::DB::Part',
+    key_columns => { parts_id => 'id' },
+  },
+);
+
+1;
+;
index 6dde98f4d388238458b10e36643b2d48e5e890e4..d7bc6db9ca0c4bfac662362da0324a58fc7acc52 100644 (file)
@@ -37,6 +37,11 @@ __PACKAGE__->meta->add_relationships(
     manager_args => { sort_by => 'sortorder' },
     column_map   => { id => 'parts_id' },
   },
+  customerprices => {
+    type         => 'one to many',
+    class        => 'SL::DB::PartCustomerPrice',
+    column_map   => { id => 'parts_id' },
+  },
   translations   => {
     type         => 'one to many',
     class        => 'SL::DB::Translation',
diff --git a/SL/DB/PartCustomerPrice.pm b/SL/DB/PartCustomerPrice.pm
new file mode 100644 (file)
index 0000000..f3fcde4
--- /dev/null
@@ -0,0 +1,13 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::PartCustomerPrice;
+
+use strict;
+
+use SL::DB::MetaSetup::PartCustomerPrice;
+use SL::DB::Manager::PartCustomerPrice;
+
+__PACKAGE__->meta->initialize;
+
+1;
index 45cd7de7d3f514205a281aebaade6f7571405928..ebb29fdc729aa6cdcf39665148a7aeb4e8255d1b 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -49,6 +49,7 @@ use SL::IO;
 use SL::MoreCommon;
 use SL::DB::Default;
 use SL::DB::TaxZone;
+use SL::DB::MakeModel;
 use SL::DB;
 use SL::Presenter::Part qw(type_abbreviation classification_abbreviation);
 use List::Util qw(min);
@@ -1377,8 +1378,19 @@ sub retrieve_item {
     $stw->finish();
     chop $ref->{taxaccounts};
 
-    $ref->{onhand} *= 1;
+    ## vendor_id ggf beim ersten mal noch nicht gesetzt nur vendor <name>--<id>
+    ($form->{vendor}, $form->{vendor_id}) = split(/--/, $form->{vendor}) if  ! $form->{vendor_id};
 
+    if ( $form->{vendor_id} ) {
+      my $mm = SL::DB::Manager::MakeModel->get_first(
+        query => [ parts_id => $ref->{id} , make => $form->{vendor_id} ] );
+      if ( $mm ) {
+        $::lxdebug->message(LXDebug->DEBUG2(), "mm id=".$mm->{id}." price=".$mm->{lastcost});
+        $ref->{lastcost} = $mm->{lastcost};
+      }
+    }
+
+    $ref->{onhand} *= 1;
     push @{ $form->{item_list} }, $ref;
 
   }
index 609d3c015fcfc3517102c848c3ec31f027911ffd..e1eb4438b7eb8eba93c3896511fc1f4362b916a5 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -2435,9 +2435,18 @@ sub retrieve_item {
         last;
       }
     }
+    ## customer_id ggf beim ersten mal noch nicht gesetzt nur customer <name>--<id>
+    ($form->{customer}, $form->{customer_id}) = split(/--/, $form->{customer}) if  ! $form->{customer_id};
+    if ( $form->{customer_id} ) {
+        my $cprice = SL::DB::Manager::PartCustomerPrice->get_first(
+            query => [ parts_id => $ref->{id} , customer_id => $form->{customer_id} ] );
+        if ( $cprice ) {
+            $::lxdebug->message(LXDebug->DEBUG2(), "cprice id=".$cprice->{id}." price=".$cprice->{price});
+            $ref->{sellprice} = $cprice->{price};
+        }
+    }
 
     $ref->{onhand} *= 1;
-
     push @{ $form->{item_list} }, $ref;
   }
   $sth->finish;
index dd6ee2faadd1aa5613c44cabea0139442faae52b..e7c553e0a148e90f378547212999905abbbd131f 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use SL::PriceSource::Pricegroup;
 use SL::PriceSource::MasterData;
 use SL::PriceSource::Makemodel;
+use SL::PriceSource::CustomerPrice;
 use SL::PriceSource::Customer;
 use SL::PriceSource::Vendor;
 use SL::PriceSource::Business;
@@ -15,6 +16,7 @@ my %price_sources_by_name = (
   vendor_discount   => 'SL::PriceSource::Vendor',
   pricegroup        => 'SL::PriceSource::Pricegroup',
   makemodel         => 'SL::PriceSource::Makemodel',
+  customerprice     => 'SL::PriceSource::CustomerPrice',
   business          => 'SL::PriceSource::Business',
   price_rules       => 'SL::PriceSource::PriceRules',
 );
@@ -25,6 +27,7 @@ my @price_sources_order = qw(
   vendor_discount
   pricegroup
   makemodel
+  customerprice
   business
   price_rules
 );
diff --git a/SL/PriceSource/CustomerPrice.pm b/SL/PriceSource/CustomerPrice.pm
new file mode 100644 (file)
index 0000000..666c37c
--- /dev/null
@@ -0,0 +1,62 @@
+package SL::PriceSource::CustomerPrice;
+
+use strict;
+use parent qw(SL::PriceSource::Base);
+
+use SL::PriceSource::Price;
+use SL::Locale::String;
+use SL::DB::PartCustomerPrice;
+# use List::UtilsBy qw(min_by max_by);
+
+sub name { 'customer_price' }
+
+sub description { t8('Customer specific Price') }
+
+sub available_prices {
+  my ($self, %params) = @_;
+
+  return () if !$self->part;
+  return () if !$self->record->is_sales;
+
+  map { $self->make_price_from_customerprice($_) }
+  grep { $_->customer_id == $self->record->customer_id }
+  $self->part->customerprices;
+}
+
+sub available_discounts { }
+
+sub price_from_source {
+  my ($self, $source, $spec) = @_;
+
+  my $customerprice = SL::DB::Manager::PartCustomerPrice->find_by(id => $spec);
+
+  return $self->make_price_from_customerprice($customerprice);
+
+}
+
+sub best_price {
+  my ($self, %params) = @_;
+
+  return () if !$self->record->is_sales;
+
+#  min_by { $_->price } $self->available_prices;
+#  max_by { $_->price } $self->available_prices;
+  &available_prices;
+
+}
+
+sub best_discount { }
+
+sub make_price_from_customerprice {
+  my ($self, $customerprice) = @_;
+
+  return SL::PriceSource::Price->new(
+    price        => $customerprice->price,
+    spec         => $customerprice->id,
+    description  => $customerprice->customer_partnumber,
+    price_source => $self,
+  );
+}
+
+
+1;
index 9fd05898d3b8de34f4491dd89396a887dfe28020..35affe9a000af3616b4dd931761b60f42fe7573b 100644 (file)
@@ -281,6 +281,8 @@ sub generate_report {
     description   => $locale->text('Part Description') . ": '$form->{description}'",
     make          => $locale->text('Make')             . ": '$form->{make}'",
     model         => $locale->text('Model')            . ": '$form->{model}'",
+    customername  => $locale->text('Customer')         . ": '$form->{customername}'",
+    customernumber=> $locale->text('Customer Part Number').": '$form->{customernumber}'",
     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
     l_soldtotal   => $locale->text('Qty in Selected Records'),
index 26fb0ea8e91e0725f2d91f832195a68450884edc..0b82cd0d5dfa8531f11c9bc40fa3314b3bdd0770 100644 (file)
@@ -776,7 +776,7 @@ sub update {
       # ask if it is a part or service item
 
       if (   $form->{"partsgroup_$i"}
-          && ($form->{"partsnumber_$i"} eq "")
+          && ($form->{"partnumber_$i" } eq "")
           && ($form->{"description_$i"} eq "")) {
         $form->{rowcount}--;
         $form->{"discount_$i"} = "";
@@ -784,6 +784,10 @@ sub update {
 
       } else {
         $form->{"id_$i"}   = 0;
+        if ( $form->{is_wrong_ptype} > 0 ) {
+          $form->{"partnumber_$i"}  = "";
+          $form->{"description_$i"} = "";
+        }
         new_item();
       }
     }
index dc05a9add04799aa3afe5049d1adef43bf9ff0f2..9e79932efec22fd6a9f2cb6a90b74912da1b0ed3 100644 (file)
@@ -253,6 +253,35 @@ namespace('kivi.Part', function(ns) {
     $("#makemodel_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
   };
 
+
+  // customerprice
+  ns.customerprice_renumber_positions = function() {
+    $('.customerprice_row [name="position"]').each(function(idx, elt) {
+      $(elt).html(idx+1);
+    });
+  };
+
+  ns.delete_customerprice_row = function(clicked) {
+    var row = $(clicked).closest('tr');
+    $(row).remove();
+
+    ns.customerprice_renumber_positions();
+  };
+
+  ns.add_customerprice_row = function() {
+    if ($('#add_customerpriceid').val() === '') return;
+
+    var data = $('#customerprice_table :input').serializeArray();
+    data.push({ name: 'action', value: 'Part/add_customerprice_row' });
+
+    $.post("controller.pl", data, kivi.eval_json_result);
+  };
+
+  ns.focus_last_customerprice_input = function () {
+    $("#customerprice_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
+  };
+
+
   ns.reload_bin_selection = function() {
     $.post("controller.pl", { action: 'Part/warehouse_changed', warehouse_id: function(){ return $('#part_warehouse_id').val() } },   kivi.eval_json_result);
   }
@@ -734,6 +763,14 @@ namespace('kivi.Part', function(ns) {
       }
     });
 
+    $('.add_customerprice_input').keydown(function(event) {
+      if(event.keyCode == 13) {
+        event.preventDefault();
+        ns.add_customerprice_row();
+        return false;
+      }
+    });
+
     $('#part_warehouse_id').change(kivi.Part.reload_bin_selection);
 
     ns.init();
index 843720d050555e73baa43485726833326b5cabd9..4a9ca5dae4d9fb69aa16302d92a4df4946b79e40 100755 (executable)
@@ -807,12 +807,15 @@ $self->{texts} = {
   'Customer Name'               => 'Kundenname',
   'Customer Number'             => 'Kundennummer',
   'Customer Order Number'       => 'Bestellnummer des Kunden',
+  'Customer Part Number'        => 'Kunden-Art-Nr.',
+  'Customer Price'              => 'Kundenpreis',
   'Customer deleted!'           => 'Kunde gelöscht!',
   'Customer details'            => 'Kundendetails',
   'Customer missing!'           => 'Kundenname fehlt!',
   'Customer not found'          => 'Kunde nicht gefunden',
   'Customer saved'              => 'Kunde gespeichert',
   'Customer saved!'             => 'Kunde gespeichert!',
+  'Customer specific Price'     => 'Kundenpreis',
   'Customer type'               => 'Kundentyp',
   'Customer variables'          => 'Kundenvariablen',
   'Customer\'s Mandate Date of Signature' => 'Mandatsunterschriftsdatum des Kunden',
@@ -1960,6 +1963,7 @@ $self->{texts} = {
   'No contra account selected!' => 'Kein Gegenkonto ausgewählt!',
   'No custom data exports have been created yet.' => 'Es wurden noch keine benutzerdefinierten Datenexporte angelegt.',
   'No customer has been selected yet.' => 'Es wurde noch kein Kunde ausgewählt.',
+  'No customer selected or found!' => 'Kein Kunde selektiert oder keinen gefunden!',
   'No data was found.'          => 'Es wurden keine Daten gefunden.',
   'No default currency'         => 'Keine Standardwährung',
   'No default value'            => 'Kein Standardwert',
@@ -2206,7 +2210,7 @@ $self->{texts} = {
   'Payable account'             => 'Verbindlichkeitskonto',
   'Payables'                    => 'Verbindlichkeiten',
   'Payment'                     => 'Zahlungsausgang',
-  'Payment / Delivery Options'  => 'Zahlungs- und Lieferoptionen',
+  'Payment / Delivery Options'  => '',
   'Payment Reminder'            => 'Zahlungserinnerung',
   'Payment Terms'               => 'Zahlungsbedingungen',
   'Payment Terms missing in row ' => 'Zahlungsfrist fehlt in Zeile ',
@@ -3442,6 +3446,7 @@ $self->{texts} = {
   'This Price Rule is no longer valid' => 'Diese Preisregel ist nicht mehr gültig',
   'This can be done with the following query:' => 'Dies kann mit der folgenden Datenbankabfrage erreicht werden:',
   'This could have happened for two reasons:' => 'Dies kann aus zwei Gründen geschehen sein:',
+  'This customer has already been added.' => 'Für diesen Kunden ist bereits ein Preis hinzugefügt.',
   'This customer number is already in use.' => 'Diese Kundennummer wird bereits verwendet.',
   'This discount has since gone down' => 'Dieser Rabatt ist mittlerweile niedriger',
   'This discount has since gone up' => 'Dieser Rabatt ist mittlerweile höher',
diff --git a/sql/Pg-upgrade2/create_part_customerprices.sql b/sql/Pg-upgrade2/create_part_customerprices.sql
new file mode 100644 (file)
index 0000000..98d2303
--- /dev/null
@@ -0,0 +1,18 @@
+-- @tag: create_part_customerprices
+-- @description: VK-Preis für jeden Kunden speichern und das Datum der Eingabe
+-- @depends: release_3_5_1
+
+CREATE TABLE part_customer_prices (
+  id          SERIAL PRIMARY KEY,
+  parts_id    integer NOT NULL,
+  customer_id integer NOT NULL,
+  customer_partnumber text DEFAULT '',
+  price      numeric(15,5) DEFAULT 0,
+  sortorder  integer DEFAULT 0,
+  lastupdate date DEFAULT now(),
+
+  FOREIGN KEY (parts_id)    REFERENCES parts (id),
+  FOREIGN KEY (customer_id) REFERENCES customer (id)
+);
+CREATE INDEX part_customer_prices_parts_id_key    ON part_customer_prices USING btree (parts_id);
+CREATE INDEX part_customer_prices_customer_id_key ON part_customer_prices USING btree (customer_id);
index 06a70418093d876d1c3f7e86050683030d44206f..322acff38fc2354e1ef45d5e5039b8ef5bbe0e05 100644 (file)
  [% PROCESS 'part/_pricegroup_prices.html' %]
 </div>
 
+<div id="customerprices">
+ [% PROCESS 'part/_customerprices.html' %]
+</div>
 [%- UNLESS SELF.part.is_assembly %]
 <div id="makemodel">
  [% PROCESS 'part/_makemodel.html' %]
diff --git a/templates/webpages/part/_customerprice_row.html b/templates/webpages/part/_customerprice_row.html
new file mode 100644 (file)
index 0000000..00ec8f0
--- /dev/null
@@ -0,0 +1,23 @@
+[%- USE T8 %]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE LxERP %]
+        <tr class="listrow[% listrow % 2 %] customerprice_row">
+         <td style='display:none'>
+         [% L.hidden_tag("customerprices[+].customer_id", customerprice.customer_id) %]
+         [% L.hidden_tag("customerprices[].id"   , customerprice.id) %]
+         </td>
+         <td align="center">
+           [%- L.button_tag("kivi.Part.delete_customerprice_row(this)",
+                            LxERP.t8("X")) %] [% # , confirm=LxERP.t8("Are you sure?")) %]
+         </td>
+         <td><span name="position" class="numeric">[% HTML.escape(customerprice.sortorder) %]</span></td>
+         <td align="center">
+           <img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]" class="dragdrop">
+         </td>
+         <td>[% customerprice.customer.customernumber | html %]</td>
+         <td>[% customerprice.customer.name         | html %] </td>
+         <td>[% L.input_tag('customerprices[].customer_partnumber', customerprice.customer_partnumber, size=30 ) %]</td>
+         <td>[% L.input_tag('customerprices[].price_as_number'    , customerprice.price_as_number    , size=15 , class="reformat_number numeric") %]</td>
+         <td>[% L.hidden_tag('customerprices[].lastupdate'         , customerprice.lastupdate.to_kivitendo) %][% customerprice.lastupdate.to_kivitendo | html %]</td>
+        </tr>
diff --git a/templates/webpages/part/_customerprices.html b/templates/webpages/part/_customerprices.html
new file mode 100644 (file)
index 0000000..d7c74bc
--- /dev/null
@@ -0,0 +1,50 @@
+[%- USE T8 %]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE LxERP %]
+  <tr>
+  </tr>
+  <tr>
+    <td>
+      <table id="customerprice_table">
+        <thead>
+          <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/close.png" alt="[%- LxERP.t8('delete item') %]"></th>
+          <th class="listheading">[% 'position'     | $T8 %]</th>
+          <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]"></th>
+          <th class="listheading" style='width:12em'>[% 'Customer Number'      | $T8 %]</th>
+          <th class="listheading">[% 'Customer'             | $T8 %]</th>
+          <th class="listheading">[% 'Customer Part Number' | $T8 %]</th>
+          <th class="listheading">[% 'Customer Price'       | $T8 %]</th>
+          <th class="listheading">[% 'Updated'              | $T8 %]</th>
+        </thead>
+        <tbody id="customerprice_rows">
+        [% SET listrow = 0 %]
+        [%- FOREACH customerprice = SELF.part.customerprices %]
+        [% listrow = listrow + 1 %]
+        [% PROCESS 'part/_customerprice_row.html' customerprice=customerprice listrow=listrow %]
+        [%- END %]
+       </tbody>
+       <tbody>
+        <tr>
+         <td></td>
+         <td></td>
+         <td></td>
+         <td align="right">[% 'Customer' | $T8 %]</td>
+         <td rowspan="2">[% P.customer_vendor.customer_picker('add_customerprice', '', style='width: 300px', class="add_customerprice_input") %]</td>
+         <td rowspan="2" align="right">[% L.button_tag('kivi.Part.add_customerprice_row()', LxERP.t8('Add')) %]</td>
+        </tr>
+       </tbody>
+      </table>
+    </td>
+  </tr>
+  [% L.sortable_element('#customerprice_rows') %]
+
+  <script type="text/javascript">
+  $(function() {
+
+    $('#customerprice_rows').on('sortstop', function(event, ui) {
+      kivi.Part.customerprice_renumber_positions();
+    });
+
+  })
+  </script>
index 6f3ecc7a86d52e886c67136eddfdf3bea2c6ef96..b6bd29f277ac401ab0b129dccdbfdc64aa242584 100644 (file)
@@ -12,7 +12,7 @@
           <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/close.png" alt="[%- LxERP.t8('delete item') %]"></th>
           <th class="listheading">[% 'position'     | $T8 %]</th>
           <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]"></th>
-          <th class="listheading">[% 'Vendor Number' | $T8 %]</th>
+          <th class="listheading" style='width:12em'>[% 'Vendor Number' | $T8 %]</th>
           <th class="listheading">[% 'Vendor'        | $T8 %]</th>
           <th class="listheading">[% 'Model'         | $T8 %]</th>
           <th class="listheading">[% 'Last Cost'     | $T8 %]</th>