commit 64ab69bcb3cfd58cd26a2f1be959b5232d3376e8
Author: Werner Hahn <wh@futureworldsearch.net>
Date:   Tue May 10 23:26:23 2016 +0200

    Shopmodul: Shoppart - Alle Daten werden angezeigt (Lager Lokal/Shop, Preisquelle, Preis) Lastupload Zeit wird in DB geschrieben und auch angezeigt
    
    Shoppart: Alle Daten werden angezeigt (Lager Lokal/Shop, Preisquelle, Preis) Lastupload Zeit wird in DB geschrieben und auch angezeigt.
    Formatierungen in den js für Zahlen fehlen noch Kleinere TODOs
    
    Conflicts:
            SL/Controller/ShopPart.pm
            SL/ShopConnector/Shopware.pm
            templates/webpages/ic/tabs/_shop.html
    
    Shoppart: Last Upload article is written in db
    
    Conflicts:
            SL/ShopConnector/Shopware.pm
    
    Conflicts:
            SL/Controller/ShopPart.pm
            SL/ShopConnector/Shopware.pm
            js/kivi.shop_part.js

diff --git a/SL/Controller/Shop.pm b/SL/Controller/Shop.pm
index a79a361c2..c3d202917 100644
--- a/SL/Controller/Shop.pm
+++ b/SL/Controller/Shop.pm
@@ -127,16 +127,16 @@ sub load_types {
   # whether the shop presents its prices as brutto or netto
   $self->price_types( [ { id => "brutto", name => t8('brutto')}, { id => "netto", name => t8('netto') } ] );
 
-  # the possible price sources to use for the shops: sellprice, lastcost,
+  # the possible default price sources to use for the shops: sellprice, lastcost,
   # listprice, or one of the pricegroups
   my $pricesources;
-  push( @{ $pricesources } , { id => "sellprice", name => t8("Sellprice") },
-                             { id => "listprice", name => t8("Listprice") },
-                             { id => "lastcost",  name => t8("Lastcost") }
+  push( @{ $pricesources } , { id => "master_data/sellprice", name => t8("Master Data")." - ".t8("Sellprice") },
+                             { id => "master_data/listprice", name => t8("Master Data")." - ".t8("Listprice") },
+                             { id => "master_data/lastcost",  name => t8("Master Data")." - ".t8("Lastcost") }
                              );
   my $pricegroups = SL::DB::Manager::Pricegroup->get_all;
   foreach my $pg ( @$pricegroups ) {
-    push( @{ $pricesources } , { id => $pg->id, name => $pg->pricegroup} );
+    push( @{ $pricesources } , { id => "pricegroup/".$pg->id, name => t8("Pricegroup") . " - " . $pg->pricegroup} );
   };
 
   $self->price_sources( $pricesources );
diff --git a/SL/Controller/ShopPart.pm b/SL/Controller/ShopPart.pm
index d04194a7a..c506aeaa7 100644
--- a/SL/Controller/ShopPart.pm
+++ b/SL/Controller/ShopPart.pm
@@ -8,16 +8,21 @@ use parent qw(SL::Controller::Base);
 use Data::Dumper;
 use SL::Locale::String qw(t8);
 use SL::DB::ShopPart;
+use SL::DB::File;
+use SL::Controller::FileUploader;
 use SL::DB::Default;
 use SL::Helper::Flash;
 
 use Rose::Object::MakeMethods::Generic
 (
-  'scalar --get_set_init' => [ qw(shop_part js) ],
+   scalar                 => [ qw(price_sources) ],
+  'scalar --get_set_init' => [ qw(shop_part file) ],
 );
 
 __PACKAGE__->run_before('check_auth');
 __PACKAGE__->run_before('add_javascripts', only => [ qw(edit_popup) ]);
+__PACKAGE__->run_before('load_pricesources',    only => [ qw(create_or_edit_popup) ]);
+
 #
 # actions
 #
@@ -36,15 +41,17 @@ sub action_update_shop {
   require SL::Shop;
   my $shop = SL::Shop->new( config => $shop_part->shop );
 
-  # TODO: generate data to upload to shop
+  # data to upload to shop. Goes to SL::Connector::XXXConnector.
   my $part_hash = $shop_part->part->as_tree;
   my $json      = SL::JSON::to_json($part_hash);
   my $return    = $shop->connector->update_part($self->shop_part, $json);
 
   # the connector deals with parsing/result verification, just needs to return success or failure
   if ( $return == 1 ) {
-    # TODO: write update time to DB
     my $now = DateTime->now;
+    my $attributes->{last_update} = $now;
+    $self->shop_part->assign_attributes(%{ $attributes });
+    $self->shop_part->save;
     $self->js->html('#shop_part_last_update_' . $shop_part->id, $now->to_kivitendo('precision' => 'minute'))
            ->flash('info', t8("Updated part [#1] in shop [#2] at #3", $shop_part->part->displayable_name, $shop_part->shop->description, $now->to_kivitendo('precision' => 'minute') ) )
            ->render;
@@ -61,14 +68,76 @@ sub action_show_files {
   my $images = SL::DB::Manager::File->get_all_sorted( where => [ trans_id => $::form->{id}, modul => $::form->{modul}, file_content_type => { like => 'image/%' } ], sort_by => 'position' );
 
   $self->render('shop_part/_list_images', { header => 0 }, IMAGES => $images);
+}
+
+sub action_ajax_upload_file{
+  my ($self, %params) = @_;
+
+  my $attributes                   = $::form->{ $::form->{form_prefix} } || die "Missing attributes";
+
+  $attributes->{filename} = ((($::form->{ATTACHMENTS} || {})->{ $::form->{form_prefix} } || {})->{file_content} || {})->{filename};
+
+  my @errors;
+  my @file_errors = SL::DB::File->new(%{ $attributes })->validate;
+  push @errors,@file_errors if @file_errors;
+
+  my @type_error = SL::Controller::FileUploader->validate_filetype($attributes->{filename},$::form->{aft});
+  push @errors,@type_error if @type_error;
+
+  return $self->js->error(@errors)->render($self) if @errors;
+
+  $self->file->assign_attributes(%{ $attributes });
+  $self->file->file_update_type_and_dimensions;
+  $self->file->save;
+
+  $self->js
+    ->dialog->close('#jqueryui_popup_dialog')
+    ->run('kivi.shop_part.show_images',$self->file->trans_id)
+    ->render();
+}
+
+sub action_ajax_update_file{
+  my ($self, %params) = @_;
+
+  my $attributes                   = $::form->{ $::form->{form_prefix} } || die "Missing attributes";
+
+  if (!$attributes->{file_content}) {
+    delete $attributes->{file_content};
+  } else {
+    $attributes->{filename} = ((($::form->{ATTACHMENTS} || {})->{ $::form->{form_prefix} } || {})->{file_content} || {})->{filename};
+  }
+
+  my @errors;
+  my @type_error = SL::Controller::FileUploader->validate_filetype($attributes->{filename},$::form->{aft});
+  push @errors,@type_error if @type_error;
+  $self->file->assign_attributes(%{ $attributes });
+  my @file_errors = $self->file->validate if $attributes->{file_content};;
+  push @errors,@file_errors if @file_errors;
+
+
+  return $self->js->error(@errors)->render($self) if @errors;
+
+  $self->file->file_update_type_and_dimensions if $attributes->{file_content};
+  $self->file->save;
+
+  $self->js
+    ->dialog->close('#jqueryui_popup_dialog')
+    ->run('kivi.shop_part.show_images',$self->file->trans_id)
+    ->render();
+}
+
+sub action_ajax_delete_file {
+  my ( $self ) = @_;
+  $self->file->delete;
 
+  $self->js
+    ->run('kivi.shop_part.show_images',$self->file->trans_id)
+    ->render();
 }
 
 sub action_get_categories {
   my ($self) = @_;
 
-#  my $shop_part = SL::DB::Manager::ShopPart->find_by(id => $::form->{shop_part_id});
-#  die unless $shop_part;
   require SL::Shop;
   my $shop = SL::Shop->new( config => $self->shop_part->shop );
   my $categories = $shop->connector->get_categories;
@@ -77,7 +146,7 @@ sub action_get_categories {
     ->run(
       'kivi.shop_part.shop_part_dialog',
       t8('Shopcategories'),
-      $self->render('shop_part/categories', { output => 0 }, CATEGORIES => $categories ) #, shop_part => $self->shop_part)
+      $self->render('shop_part/categories', { output => 0 }, CATEGORIES => $categories )
     )
     ->reinit_widgets;
 
@@ -99,6 +168,33 @@ sub action_update {
   $self->create_or_update;
 }
 
+sub action_show_price_n_pricesource {
+  my ($self) = @_;
+
+  my ( $price, $price_src_str ) = $self->get_price_n_pricesource($::form->{pricesource});
+
+  #TODO Price must be formatted. $price_src_str must be translated
+  $self->js->html('#price_' . $self->shop_part->id, $price)
+           ->html('#active_price_source_' . $self->shop_part->id, $price_src_str)
+           ->render;
+}
+
+sub action_show_stock {
+  my ($self) = @_;
+  my ( $stock_local, $stock_onlineshop );
+
+  require SL::Shop;
+  my $shop = SL::Shop->new( config => $self->shop_part->shop );
+  my $shop_article = $shop->connector->get_article($self->shop_part->part->partnumber);
+
+  $stock_local = $self->shop_part->part->onhand;
+  $stock_onlineshop = $shop_article->{data}->{mainDetail}->{inStock};
+
+  $self->js->html('#stock_' . $self->shop_part->id, $stock_local."/".$stock_onlineshop)
+           ->render;
+}
+
+
 sub create_or_update {
   my ($self) = @_;
 
@@ -111,10 +207,15 @@ sub create_or_update {
 
   $self->shop_part->save;
 
+  my ( $price, $price_src_str ) = $self->get_price_n_pricesource($self->shop_part->active_price_source);
+
+  #TODO Price must be formatted. $price_src_str must be translated
   flash('info', $is_new ? t8('The shop part has been created.') : t8('The shop part has been saved.'));
   # $self->js->val('#partnumber', 'ladida');
   $self->js->html('#shop_part_description_' . $self->shop_part->id, $self->shop_part->shop_description)
            ->html('#shop_part_active_' . $self->shop_part->id, $self->shop_part->active)
+           ->html('#price_' . $self->shop_part->id, $price)
+           ->html('#active_price_source_' . $self->shop_part->id, $price_src_str)
            ->run('kivi.shop_part.close_dialog')
            ->flash('info', t8("Updated shop part"))
            ->render;
@@ -183,6 +284,47 @@ sub add_javascripts  {
   $::request->{layout}->add_javascripts(qw(kivi.shop_part.js));
 }
 
+sub load_pricesources {
+  my ($self) = @_;
+
+  # the price sources to use for the article: sellprice, lastcost,
+  # listprice, or one of the pricegroups. It overwrites the default pricesource from the shopconfig.
+  # TODO: implement valid pricerules for the article
+  my $pricesources;
+  push( @{ $pricesources } , { id => "master_data/sellprice", name => t8("Master Data")." - ".t8("Sellprice") },
+                             { id => "master_data/listprice", name => t8("Master Data")." - ".t8("Listprice") },
+                             { id => "master_data/lastcost",  name => t8("Master Data")." - ".t8("Lastcost") }
+                             );
+  my $pricegroups = SL::DB::Manager::Pricegroup->get_all;
+  foreach my $pg ( @$pricegroups ) {
+    push( @{ $pricesources } , { id => "pricegroup/".$pg->id, name => t8("Pricegroup") . " - " . $pg->pricegroup} );
+  };
+
+  $self->price_sources( $pricesources );
+}
+
+sub get_price_n_pricesource {
+  my ($self,$pricesource) = @_;
+
+  my ( $price_src_str, $price_src_id ) = split(/\//,$pricesource);
+
+  require SL::DB::Pricegroup;
+  require SL::DB::Part;
+  #TODO Price must be formatted. Translations for $price_grp_str
+  my $price;
+  if ($price_src_str eq "master_data") {
+    my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
+    $price = $part->$price_src_id;
+    $price_src_str = $price_src_id;
+  }else{
+    my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
+    my $pricegrp = SL::DB::Manager::Pricegroup->find_by( id => $price_src_id )->pricegroup;
+    $price =  $part->prices->[0]->price;
+    $price_src_str = $pricegrp;
+  }
+  return($price,$price_src_str);
+}
+
 sub check_auth {
   return 1; # TODO: implement shop rights
   # $::auth->assert('shop');
diff --git a/SL/DB/MetaSetup/Shop.pm b/SL/DB/MetaSetup/Shop.pm
index c4fad3bb2..bab10f68a 100644
--- a/SL/DB/MetaSetup/Shop.pm
+++ b/SL/DB/MetaSetup/Shop.pm
@@ -10,17 +10,19 @@ use base qw(SL::DB::Object);
 __PACKAGE__->meta->table('shops');
 
 __PACKAGE__->meta->columns(
-  connector    => { type => 'text' },
-  description  => { type => 'text' },
-  id           => { type => 'serial', not_null => 1 },
-  login        => { type => 'text' },
-  obsolete     => { type => 'boolean', default => 'false', not_null => 1 },
-  password     => { type => 'text' },
-  port         => { type => 'integer' },
-  price_source => { type => 'text' },
-  pricetype    => { type => 'text' },
-  sortkey      => { type => 'integer' },
-  url          => { type => 'text' },
+  connector         => { type => 'text' },
+  description       => { type => 'text' },
+  id                => { type => 'serial', not_null => 1 },
+  last_order_number => { type => 'integer' },
+  login             => { type => 'text' },
+  obsolete          => { type => 'boolean', default => 'false', not_null => 1 },
+  orders_to_fetch   => { type => 'integer' },
+  password          => { type => 'text' },
+  port              => { type => 'integer' },
+  price_source      => { type => 'text' },
+  pricetype         => { type => 'text' },
+  sortkey           => { type => 'integer' },
+  url               => { type => 'text' },
 );
 
 __PACKAGE__->meta->primary_key_columns([ 'id' ]);
diff --git a/SL/ShopConnector/Base.pm b/SL/ShopConnector/Base.pm
index e6259abd3..61199daf2 100644
--- a/SL/ShopConnector/Base.pm
+++ b/SL/ShopConnector/Base.pm
@@ -13,6 +13,8 @@ sub get_new_orders { die 'get_order needs to be implemented' }
 
 sub update_part { die 'update_part needs to be implemented' }
 
+sub get_article { die 'get_article needs to be implemented' }
+
 sub get_categories { die 'get_order needs to be implemented' }
 1;
 
diff --git a/SL/ShopConnector/Shopware.pm b/SL/ShopConnector/Shopware.pm
index e08fed73c..aaa29403b 100644
--- a/SL/ShopConnector/Shopware.pm
+++ b/SL/ShopConnector/Shopware.pm
@@ -12,6 +12,9 @@ use LWP::Authen::Digest;
 use SL::DB::ShopOrder;
 use SL::DB::ShopOrderItem;
 use Data::Dumper;
+use Sort::Naturally ();
+use Encode qw(encode_utf8);
+use SL::Controller::ShopPart;
 
 use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(connector url) ],
@@ -185,10 +188,9 @@ sub update_part {
   $main::lxdebug->dump(0, 'WH: UPDATE JSON: ', \$json);
   my $url = $self->url;
   my $part = SL::DB::Part->new(id => $shop_part->{part_id})->load;
-  #my $part = $shop_part->part;
-  $main::lxdebug->dump(0, 'WH: Part',\$part);
+$main::lxdebug->dump(0, 'WH: SHOPPART: ',\$part);
 
-  # TODO: Prices (pricerules, pricegroups,
+  # TODO: Prices (pricerules, pricegroups, multiple prices)
   my $cvars = { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $part->cvars_by_config } };
   #my $categories = { map { ( name => $_) } @{ $shop_part->{shop_category} } };
   my @cat = ();
@@ -199,33 +201,18 @@ sub update_part {
     $main::lxdebug->dump(0, 'WH: TEMP: ', \$temp);
 
     push ( @cat, $temp );
-    #push ( @cat, map { ( name => $_[1]) } @{ $row_cat } );
   }
-  $main::lxdebug->dump(0, 'WH: CATEGORIES',\@cat);
+
   my $images = SL::DB::Manager::File->get_all( where => [ modul => 'shop_part', trans_id => $part->{id} ]);
-  $main::lxdebug->dump(0, 'WH: IMAGES',\@{ $images } );
-  my $images2 = { map {
-                    ( link        => 'data:' . $_->{file_content_type} . ';base64,' . MIME::Base64::encode($_->{file_content},''),
-                      description => $_->{title},
-                      position    => $_->{position},
-                      extension   => 'jpg', # muss $extionsion sein
-                      path        => $_->{filename}, # muss $path sein
-                    ) } @{ $images } };
-  $main::lxdebug->dump(0, 'WH: IMAGES 2 ',\$images2);
-
-  my @images3 = ();
+  my @upload_img = ();
   foreach my $img (@{ $images }) {
     $main::lxdebug->dump(0, 'WH: FOR: ', \$img);
 
     my ($path, $extension) = (split /\./, $img->{filename});
-    $main::lxdebug->message(0, "WH: PATH: $path Ext: $extension");
-
-    my $temp ={
-                      ( link        => 'data:' . $img->{file_content_type} . ';base64,' . MIME::Base64::encode($img->{file_content},''),
-                        description => $img->{title},
-                        position    => $img->{position},
-                        extension   => $extension,
-                        path        => $path,
+    my $temp ={ ( link        => 'data:' . $img->{file_content_type} . ';base64,' . MIME::Base64::encode($img->{file_content},''),
+                  description => $img->{title},
+                  position    => $img->{position},
+                  extension   => $extension,
                       )}    ;
     push( @images3, $temp);
   }
@@ -234,16 +221,25 @@ sub update_part {
   my $data = $self->connector->get("http://$url/api/articles/$part->{partnumber}?useNumberAsId=true");
   my $data_json = $data->content;
   my $import = SL::JSON::decode_json($data_json);
-  $main::lxdebug->dump(0, 'WH: IMPORT', \$import);
-  $main::lxdebug->dump(0, 'WH: Active', $shop_part->active);
-
 
-  my %shop_data =  (  name          => $part->{description},
-                      taxId         => 4, # TODO Hardcoded kann auch der taxwert sein zB. tax => 19.00
-                      mainDetail    => { number   => $part->{partnumber},
+  # get the right price
+  my ( $price_src_str, $price_src_id ) = split(/\//,$shop_part->active_price_source);
+  require SL::DB::Part;
+  my $price;
+  if ($price_src_str eq "master_data") {
+    my $part = SL::DB::Manager::Part->get_all( where => [id => $shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
+    $price = $part->$price_src_id;
+  }else{
+    my $part = SL::DB::Manager::Part->get_all( where => [id => $shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
+    $price =  $part->prices->[0]->price;
+  }
+  # mapping to shopware still missing attributes,metatags
+  my %shop_data =  (  name              => $part->{description},
+                      taxId             => 4, # TODO Hardcoded kann auch der taxwert sein zB. tax => 19.00
+                      mainDetail        => { number   => $part->{partnumber},
                                          inStock  => $part->{onhand},
                                          prices   =>  [ {          from   => 1,
-                                                                   price  => $part->{sellprice},
+                                                                   price  => $price,
                                                         customerGroupKey  => 'EK',
                                                       },
                                                     ],
@@ -257,10 +253,10 @@ sub update_part {
 
                     )
                   ;
-$main::lxdebug->dump(0, 'WH: SHOPDATA', \%shop_data );
-my $dataString = SL::JSON::to_json(\%shop_data);
-$dataString = encode_utf8($dataString);
-$main::lxdebug->message(0, 'WH: JSONDATA2 '.$dataString);
+  my $dataString = SL::JSON::to_json(\%shop_data);
+  $dataString = encode_utf8($dataString);
+
+  my $upload_content;
   if($import->{success}){
 $main::lxdebug->message(0, "WH: if success: ". $import->{success});
   my %del_img =  ( images        => [ {} ], ) ;
@@ -269,20 +265,28 @@ $main::lxdebug->message(0, "WH: if success: ". $import->{success});
     #update
     my $upload = $self->connector->put("http://$url/api/articles/$part->{partnumber}?useNumberAsId=true",Content => $dataString);
     my $data_json = $upload->content;
-    my $upload_content = SL::JSON::decode_json($data_json);
-$main::lxdebug->dump(0, "WH:2 else success: ", \$upload);
-    return $upload_content->{success};
+    $upload_content = SL::JSON::decode_json($data_json);
   }else{
     #upload
 $main::lxdebug->message(0, "WH: else success: ". $import->{success});
     my $upload = $self->connector->post("http://$url/api/articles/",Content => $dataString);
     my $data_json = $upload->content;
-    my $upload_content = SL::JSON::decode_json($data_json);
-$main::lxdebug->dump(0, "WH:2 else success: ", \$upload);
-    return $upload_content->{success};
+    $upload_content = SL::JSON::decode_json($data_json);
+  }
+  if(@upload_img) {
+    $self->connector->put("http://$url/api/generateArticleImages/$part->{partnumber}?useNumberAsId=true");
+    #$self->connector->delete("http://$url/api/caches/");
   }
+  return $upload_content->{success};
+}
+
+sub get_article {
+  my ($self,$partnumber) = @_;
 
->>>>>>> b616804... Kategorien werden mit ID und Name in die DB geschrieben. Ein Komma im Name erzeugt noch Fehler. Der Pfad muss noch gemacht werden
+  my $url = $self->url;
+  my $data = $self->connector->get("http://$url/api/articles/$partnumber?useNumberAsId=true");
+  my $data_json = $data->content;
+  return SL::JSON::decode_json($data_json);
 }
 
 sub init_url {
diff --git a/js/kivi.shop_part.js b/js/kivi.shop_part.js
index 3b775a923..fc65520a5 100644
--- a/js/kivi.shop_part.js
+++ b/js/kivi.shop_part.js
@@ -102,4 +102,23 @@ namespace('kivi.shop_part', function(ns) {
     if (discount_str) $('#discount_' + row).val(discount_str);
     $('#update_button').click();
   }
+
+  ns.show_images = function(id) {
+    var url = 'controller.pl?action=ShopPart/show_files&modul=shop_part&id='+id;
+    $('#shop_images').load(url);
+  }
+
+  //shows the Name and price in _shop.html. Pricerules not implemented yet, just master_data and pricegroups
+  ns.update_price_n_price_source = function(shop_part_id,price_source) {
+    $.post('controller.pl', { action: 'ShopPart/show_price_n_pricesource', shop_part_id: shop_part_id, pricesource: price_source }, function(data) {
+      kivi.eval_json_result(data);
+    });
+  }
+  //shows the local and the online stock
+  ns.update_stock = function(shop_part_id) {
+    $.post('controller.pl', { action: 'ShopPart/show_stock', shop_part_id: shop_part_id }, function(data) {
+      kivi.eval_json_result(data);
+    });
+  }
+
 });
diff --git a/sql/Pg-upgrade2/shop_parts_4.sql b/sql/Pg-upgrade2/shop_parts_4.sql
new file mode 100644
index 000000000..093c41c94
--- /dev/null
+++ b/sql/Pg-upgrade2/shop_parts_4.sql
@@ -0,0 +1,10 @@
+-- @tag: shop_parts_4
+-- @description: Add tables for part information for shop
+-- @charset: UTF-8
+-- @depends: release_3_3_0 shops
+-- @ignore: 0
+ALTER TABLE shop_parts ADD COLUMN active_price_source text;
+ALTER TABLE shop_parts ADD COLUMN metatag_keywords text;
+ALTER TABLE shop_parts ADD COLUMN metatag_description text;
+ALTER TABLE shop_parts ADD COLUMN metatag_title text;
+ALTER TABLE shop_parts DROP COLUMN meta_tags;
diff --git a/sql/Pg-upgrade2/shops2.sql b/sql/Pg-upgrade2/shops2.sql
new file mode 100644
index 000000000..2f39cc12d
--- /dev/null
+++ b/sql/Pg-upgrade2/shops2.sql
@@ -0,0 +1,8 @@
+-- @tag: shops2
+-- @description: Alter table shops more columns for configuration
+-- @charset: UTF-8
+-- @depends: release_3_4_0 shops
+-- @ignore: 0
+
+ALTER TABLE shops ADD COLUMN last_order_number integer;
+ALTER TABLE shops ADD COLUMN orders_to_fetch integer;
diff --git a/templates/webpages/shop_part/edit.html b/templates/webpages/shop_part/edit.html
index 333fd3358..9695f44a3 100644
--- a/templates/webpages/shop_part/edit.html
+++ b/templates/webpages/shop_part/edit.html
@@ -3,10 +3,11 @@
 [%- USE L -%]
 [%- USE P -%]
 [%- USE LxERP -%]
+[%- USE Dumper -%]
 
 [% LxERP.t8("Part") %]: [% HTML.escape(SELF.shop_part.part.displayable_name) %]<br>
 [% LxERP.t8("Shop") %]: [% HTML.escape(SELF.shop_part.shop.description) %]
-
+[% Dumper.dump_html(SELF) %]
 <form action="controller.pl" method="post">
   <div>
     [% IF SELF.shop_part.id %]
@@ -26,6 +27,15 @@
      <td>[% LxERP.t8("Active") %]</td>
      <td>[% L.yes_no_tag("shop_part.active", SELF.shop_part.active) %]</td>
     </tr>
+    <tr>
+      <th align="right">[% 'Price Source' | $T8 %]</th>
+      [% IF SELF.shop_part.active_price_source %]
+        [% SET price_source = SELF.shop_part.active_price_source %]
+      [% ELSE %]
+        [% SET price_source = SELF.shop_part.shop.price_source %]
+      [% END %]
+      <td>[% L.select_tag('shop_part.active_price_source', SELF.price_sources, value_key = 'id', title_key = 'name', with_empty = 0, default = price_source, default_value_key='id' ) %]</td>
+    </tr>
     <tr>
      <td>[% LxERP.t8("Sort order") %]</td>
      <td>[% L.input_tag("shop_part.sortorder", SELF.shop_part.sortorder, size=2) %]</td>
@@ -39,8 +49,16 @@
      <td>[% L.yes_no_tag('shop_part.front_page', SELF.shop_part.front_page) %]</td>
     </tr>
     <tr>
-     <td>[% LxERP.t8("Meta Tags") %]</td>
-     <td>[% L.input_tag("shop_part.meta_tags", SELF.shop_part.meta_tags, size=2) %]</td>
+     <td>[% LxERP.t8("Meta tag title") %]</td>
+     <td>[% L.input_tag("shop_part.metatag_title", SELF.shop_part.metatag_title, size=12) %]</td>
+    </tr>
+    <tr>
+     <td>[% LxERP.t8("Meta tag keywords") %]</td>
+     <td>[% L.input_tag("shop_part.metatag_keywords", SELF.shop_part.metatag_keywords, size=22) %]</td>
+    </tr>
+    <tr>
+     <td>[% LxERP.t8("Meta tag description") %]</td>
+     <td>[% L.textarea_tag("shop_part.metatag_description", SELF.shop_part.metatag_description, rows=4) %]</td>
     </tr>
     </table>
     [% # L.dump(SELF.shop_part) %]
diff --git a/templates/webpages/shops/form.html b/templates/webpages/shops/form.html
index a2d74308b..a21f7d1ec 100644
--- a/templates/webpages/shops/form.html
+++ b/templates/webpages/shops/form.html
@@ -1,10 +1,11 @@
 [%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE P -%][%- USE T8 -%]
+[%- USE Dumper -%]
 
 [% SET style="width: 400px" %]
 [% SET size=34 %]
 
 <h1>[% HTML.escape(title) %]</h1>
-
+[% # Dumper.dump_html(SELF.shop) %]
 <form action="controller.pl" method="post">
 
 [%- INCLUDE 'common/flash.html' %]
@@ -26,7 +27,7 @@
   </tr>
   <tr>
     <th align="right">[% 'Price Source' | $T8 %]</th>
-    <td>[% L.select_tag('shop.price_source', SELF.price_sources, value_key = 'id', title_key = 'name', with_empty = 1, default = SELF.shop.price_source, default_value_key='id' ) %]</td>
+    <td>[% L.select_tag('shop.price_source', SELF.price_sources, value_key = 'id', title_key = 'name', with_empty = 0, default = SELF.shop.price_source, default_value_key='id' ) %]</td>
   </tr>
   <tr>
     <th align="right">[% 'URL' | $T8 %]</th>
@@ -39,11 +40,18 @@
     <th align="right">[% 'User' | $T8 %]</th>
     <td>[%- L.input_tag("shop.login", SELF.shop.login, size=12) %]</td>
   </tr>
-    <tr>
+  <tr>
     <th align="right">[% 'Password' | $T8 %]</th>
     <td>[%- L.input_tag("shop.password", SELF.shop.password, size=12) %]</td>
   </tr>
- </tr>
+  <tr>
+    <th align="right">[% 'Last ordernumber' | $T8 %]</th>
+    <td>[%- L.input_tag("shop.last_order_number", SELF.shop.last_order_number, size=12) %]</td>
+  </tr>
+  <tr>
+    <th align="right">[% 'Orders to fetch' | $T8 %]</th>
+    <td>[%- L.input_tag("shop.orders_to_fetch", SELF.shop.orders_to_fetch, size=12) %]</td>
+  </tr>
   <tr>
     <th align="right">[% 'Obsolete' | $T8 %]</th>
     <td>[% L.checkbox_tag('shop.obsolete', checked = SELF.shop.obsolete, for_submit=1) %]</td>
