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
     ->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) = @_;
 
   $self->part->prices([]);
   $self->parse_form_prices;
 
+  $self->parse_form_customerprices;
   $self->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',
   # 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});
   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;
 
 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;
 
   parts                          => 'part',
   partsgroup                     => 'parts_group',
   part_classifications           => 'PartClassification',
+  part_customer_prices           => 'PartCustomerPrice',
   parts_price_history            => 'PartsPriceHistory',
   payment_terms                  => 'payment_term',
   periodic_invoices              => 'periodic_invoice',
 
--- /dev/null
+# 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;
 
   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' ]);
 
--- /dev/null
+# 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;
+;
 
     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',
 
--- /dev/null
+# 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;
 
 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);
     $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;
 
   }
 
         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;
 
 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;
   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',
 );
   vendor_discount
   pricegroup
   makemodel
+  customerprice
   business
   price_rules
 );
 
--- /dev/null
+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;
 
     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'),
 
       # 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"} = "";
 
       } else {
         $form->{"id_$i"}   = 0;
+        if ( $form->{is_wrong_ptype} > 0 ) {
+          $form->{"partnumber_$i"}  = "";
+          $form->{"description_$i"} = "";
+        }
         new_item();
       }
     }
 
     $("#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);
   }
       }
     });
 
+    $('.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();
 
   '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',
   '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',
   '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 ',
   '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',
 
--- /dev/null
+-- @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);
 
  [% 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' %]
 
--- /dev/null
+[%- 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>
 
--- /dev/null
+[%- 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>
 
           <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>