Merge branch 'debian' into b-3.6.1
[kivitendo-erp.git] / SL / ShopConnector / Shopware.pm
index fdfd141..65c42fd 100644 (file)
@@ -11,6 +11,7 @@ use LWP::Authen::Digest;
 use SL::DB::ShopOrder;
 use SL::DB::ShopOrderItem;
 use SL::DB::History;
+use SL::DB::PaymentTerm;
 use DateTime::Format::Strptime;
 use SL::DB::File;
 use Data::Dumper;
@@ -24,34 +25,100 @@ use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(connector url) ],
 );
 
+sub get_one_order {
+  my ($self, $ordnumber) = @_;
+
+  my $dbh       = SL::DB::client;
+  my $of        = 0;
+  my $url       = $self->url;
+  my $data      = $self->connector->get($url . "api/orders/$ordnumber?useNumberAsId=true");
+  my @errors;
+
+  my %fetched_orders;
+  if ($data->is_success && $data->content_type eq 'application/json'){
+    my $data_json = $data->content;
+    my $import    = SL::JSON::decode_json($data_json);
+    my $shoporder = $import->{data};
+    $dbh->with_transaction( sub{
+      $self->import_data_to_shop_order($import);
+      1;
+    })or do {
+      push @errors,($::locale->text('Saving failed. Error message from the database: #1', $dbh->error));
+    };
+
+    if(!@errors){
+      $self->set_orderstatus($import->{data}->{id}, "fetched");
+      $of++;
+    }else{
+      flash_later('error', $::locale->text('Database errors: #1', @errors));
+    }
+    %fetched_orders = (shop_description => $self->config->description, number_of_orders => $of);
+  } else {
+    my %error_msg  = (
+      shop_id          => $self->config->id,
+      shop_description => $self->config->description,
+      message          => "Error: $data->status_line",
+      error            => 1,
+    );
+    %fetched_orders = %error_msg;
+  }
+
+  return \%fetched_orders;
+}
+
 sub get_new_orders {
   my ($self, $id) = @_;
 
   my $url              = $self->url;
-  my $ordnumber        = $self->config->last_order_number + 1;
+  my $last_order_number = $self->config->last_order_number;
   my $otf              = $self->config->orders_to_fetch;
   my $of               = 0;
-  my $orders_data      = $self->connector->get($url . "api/orders?limit=$otf&filter[0][property]=number&filter[0][expression]=>&filter[0][value]=" . $self->config->last_order_number);
-  my $orders_data_json = $orders_data->content;
-  my $orders_import    = SL::JSON::decode_json($orders_data_json);
-
-  if ($orders_import->{success}){
+  my $last_data      = $self->connector->get($url . "api/orders/$last_order_number?useNumberAsId=true");
+  my $last_data_json = $last_data->content;
+  my $last_import    = SL::JSON::decode_json($last_data_json);
+
+  my $orders_data      = $self->connector->get($url . "api/orders?limit=$otf&filter[1][property]=status&filter[1][value]=0&filter[0][property]=id&filter[0][expression]=>&filter[0][value]=" . $last_import->{data}->{id});
+
+  my $dbh = SL::DB->client;
+  my @errors;
+  my %fetched_orders;
+  if ($orders_data->is_success && $orders_data->content_type eq 'application/json'){
+    my $orders_data_json = $orders_data->content;
+    my $orders_import    = SL::JSON::decode_json($orders_data_json);
     foreach my $shoporder(@{ $orders_import->{data} }){
 
       my $data      = $self->connector->get($url . "api/orders/" . $shoporder->{id});
       my $data_json = $data->content;
       my $import    = SL::JSON::decode_json($data_json);
 
-      $self->import_data_to_shop_order($import);
-
-      $self->config->assign_attributes( last_order_number => $ordnumber);
-      $self->config->save;
-      $ordnumber++;
-      $of++;
+      $dbh->with_transaction( sub{
+          $self->import_data_to_shop_order($import);
+
+          $self->config->assign_attributes( last_order_number => $shoporder->{number});
+          $self->config->save;
+          1;
+      })or do {
+        push @errors,($::locale->text('Saving failed. Error message from the database: #1', $dbh->error));
+      };
+
+      if(!@errors){
+        $self->set_orderstatus($shoporder->{id}, "fetched");
+        $of++;
+      }else{
+        flash_later('error', $::locale->text('Database errors: #1', @errors));
+      }
     }
+    %fetched_orders = (shop_description => $self->config->description, number_of_orders => $of);
+  } else {
+    my %error_msg  = (
+      shop_id          => $self->config->id,
+      shop_description => $self->config->description,
+      message          => "Error: $orders_data->status_line",
+      error            => 1,
+    );
+    %fetched_orders = %error_msg;
   }
-  my $shop           = $self->config->description;
-  my %fetched_orders = (shop_id => $self->config->description, number_of_orders => $of);
+
   return \%fetched_orders;
 }
 
@@ -62,7 +129,8 @@ sub import_data_to_shop_order {
   $shop_order->save;
   my $id = $shop_order->id;
 
-  my @positions = sort { Sort::Naturally::ncmp($a->{"partnumber"}, $b->{"partnumber"}) } @{ $import->{data}->{details} };
+  my @positions = sort { Sort::Naturally::ncmp($a->{"articleNumber"}, $b->{"articleNumber"}) } @{ $import->{data}->{details} };
+  #my @positions = sort { Sort::Naturally::ncmp($a->{"partnumber"}, $b->{"partnumber"}) } @{ $import->{data}->{details} };
   my $position = 1;
   my $active_price_source = $self->config->price_source;
   #Mapping Positions
@@ -82,14 +150,28 @@ sub import_data_to_shop_order {
     $pos_insert->save;
     $position++;
   }
-  $shop_order->{positions} = $position-1;
+  $shop_order->positions($position-1);
+
+  if ( $self->config->shipping_costs_parts_id ) {
+    my $shipping_part = SL::DB::Part->find_by( id => $self->config->shipping_costs_parts_id);
+    my %shipping_pos = ( description    => $import->{data}->{dispatch}->{name},
+                         partnumber     => $shipping_part->partnumber,
+                         price          => $import->{data}->{invoiceShipping},
+                         quantity       => 1,
+                         position       => $position,
+                         shop_trans_id  => 0,
+                         shop_order_id  => $id,
+                       );
+    my $shipping_pos_insert = SL::DB::ShopOrderItem->new(%shipping_pos);
+    $shipping_pos_insert->save;
+  }
 
   my $customer = $shop_order->get_customer;
 
   if(ref($customer)){
     $shop_order->kivi_customer_id($customer->id);
-    $shop_order->save;
   }
+  $shop_order->save;
 }
 
 sub map_data_to_shoporder {
@@ -103,6 +185,13 @@ sub map_data_to_shoporder {
 
   my $shop_id      = $self->config->id;
   my $tax_included = $self->config->pricetype;
+
+  # Mapping Zahlungsmethoden muss an Firmenkonfiguration angepasst werden
+  my %payment_ids_methods = (
+    # shopware_paymentId => kivitendo_payment_id
+  );
+  my $default_payment    = SL::DB::Manager::PaymentTerm->get_first();
+  my $default_payment_id = $default_payment ? $default_payment->id : undef;
   # Mapping to table shoporders. See http://community.shopware.com/_detail_1690.html#GET_.28Liste.29
   my %columns = (
     amount                  => $import->{data}->{invoiceAmount},
@@ -150,7 +239,7 @@ sub map_data_to_shoporder {
     netamount               => $import->{data}->{invoiceAmountNet},
     order_date              => $orderdate,
     payment_description     => $import->{data}->{payment}->{description},
-    payment_id              => $import->{data}->{paymentId},
+    payment_id              => $payment_ids_methods{$import->{data}->{paymentId}} || $default_payment_id,
     remote_ip               => $import->{data}->{remoteAddress},
     sepa_account_holder     => $import->{data}->{paymentIntances}->{accountHolder},
     sepa_bic                => $import->{data}->{paymentIntances}->{bic},
@@ -183,13 +272,22 @@ sub get_categories {
   my @daten      = @{$import->{data}};
   my %categories = map { ($_->{id} => $_) } @daten;
 
+  my @categories_tree;
   for(@daten) {
+    # ignore root with id=1
+    if( $_->{id} == 1) {
+      next;
+    }
     my $parent = $categories{$_->{parentId}};
-    $parent->{children} ||= [];
-    push @{$parent->{children}},$_;
+    if($parent && $parent->{id} != 1) {
+      $parent->{children} ||= [];
+      push @{$parent->{children}},$_;
+    } else {
+      push @categories_tree, $_;
+    }
   }
 
-  return \@daten;
+  return \@categories_tree;
 }
 
 sub get_version {
@@ -219,7 +317,7 @@ sub update_part {
   die unless ref($shop_part) eq 'SL::DB::ShopPart';
 
   my $url = $self->url;
-  my $part = SL::DB::Part->new(id => $shop_part->{part_id})->load;
+  my $part = SL::DB::Part->new(id => $shop_part->part_id)->load;
 
   # CVARS to map
   my $cvars = { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $part->cvars_by_config } };
@@ -238,7 +336,7 @@ sub update_part {
   my %shop_data;
 
   if($todo eq "price"){
-    %shop_data = ( mainDetail => { number   => $part->{partnumber},
+    %shop_data = ( mainDetail => { number   => $part->partnumber,
                                    prices   =>  [ { from             => 1,
                                                     price            => $price,
                                                     customerGroupKey => 'EK',
@@ -247,13 +345,13 @@ sub update_part {
                                   },
                  );
   }elsif($todo eq "stock"){
-    %shop_data = ( mainDetail => { number   => $part->{partnumber},
-                                   inStock  => $part->{onhand},
+    %shop_data = ( mainDetail => { number   => $part->partnumber,
+                                   inStock  => $part->onhand,
                                  },
                  );
   }elsif($todo eq "price_stock"){
-    %shop_data =  ( mainDetail => { number   => $part->{partnumber},
-                                    inStock  => $part->{onhand},
+    %shop_data =  ( mainDetail => { number   => $part->partnumber,
+                                    inStock  => $part->onhand,
                                     prices   =>  [ { from             => 1,
                                                      price            => $price,
                                                      customerGroupKey => 'EK',
@@ -262,15 +360,15 @@ sub update_part {
                                    },
                    );
   }elsif($todo eq "active"){
-    %shop_data =  ( mainDetail => { number   => $part->{partnumber},
+    %shop_data =  ( mainDetail => { number   => $part->partnumber,
                                    },
-                    active => ($part->{partnumber} == 1 ? 0 : 1),
+                    active => ($part->partnumber == 1 ? 0 : 1),
                    );
   }elsif($todo eq "all"){
   # mapping to shopware still missing attributes,metatags
-    %shop_data =  (   name              => $part->{description},
-                      mainDetail        => { number   => $part->{partnumber},
-                                             inStock  => $part->{onhand},
+    %shop_data =  (   name              => $part->description,
+                      mainDetail        => { number   => $part->partnumber,
+                                             inStock  => $part->onhand,
                                              prices   =>  [ {          from   => 1,
                                                                        price  => $price,
                                                             customerGroupKey  => 'EK',
@@ -280,12 +378,12 @@ sub update_part {
                                              #attribute => { attr1  => $cvars->{CVARNAME}->{value}, } , #HowTo handle attributes
                                        },
                       supplier          => 'AR', # Is needed by shopware,
-                      descriptionLong   => $shop_part->{shop_description},
+                      descriptionLong   => $shop_part->shop_description,
                       active            => $shop_part->active,
                       images            => [ @upload_img ],
                       __options_images  => { replace => 1, },
                       categories        => [ @cat ],
-                      description       => $shop_part->{shop_description},
+                      description       => $shop_part->shop_description,
                       categories        => [ @cat ],
                       tax               => $taxrate,
                     )
@@ -298,7 +396,7 @@ sub update_part {
   my $upload_content;
   my $upload;
   my ($import,$data,$data_json);
-  my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
+  my $partnumber = $::form->escape($part->partnumber);#shopware don't accept / in articlenumber
   # Shopware RestApi sends an erroremail if configured and part not found. But it needs this info to decide if update or create a new article
   # LWP->post = create LWP->put = update
     $data       = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
@@ -306,7 +404,7 @@ sub update_part {
     $import     = SL::JSON::decode_json($data_json);
   if($import->{success}){
     #update
-    my $partnumber  = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
+    my $partnumber  = $::form->escape($part->partnumber);#shopware don't accept / in articlenumber
     $upload         = $self->connector->put($url . "api/articles/$partnumber?useNumberAsId=true", Content => $dataString);
     my $data_json   = $upload->content;
     $upload_content = SL::JSON::decode_json($data_json);
@@ -318,7 +416,7 @@ sub update_part {
   }
   # don't know if this is needed
   if(@upload_img) {
-    my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
+    my $partnumber = $::form->escape($part->partnumber);#shopware don't accept / in articlenumber
     my $imgup      = $self->connector->put($url . "api/generatearticleimages/$partnumber?useNumberAsId=true");
   }
 
@@ -335,6 +433,15 @@ sub get_article {
   return SL::JSON::decode_json($data_json);
 }
 
+sub set_orderstatus {
+  my ($self,$order_id, $status) = @_;
+  if ($status eq "fetched") { $status = 1; }
+  if ($status eq "completed") { $status = 2; }
+  my %new_status = (orderStatusId => $status);
+  my $new_status_json = SL::JSON::to_json(\%new_status);
+  $self->connector->put($self->url . "api/orders/$order_id", Content => $new_status_json);
+}
+
 sub init_url {
   my ($self) = @_;
   $self->url($self->config->protocol . "://" . $self->config->server . ":" . $self->config->port . $self->config->path);
@@ -361,7 +468,7 @@ __END__
 
 =head1 NAME
 
-SL::Shopconnecter::Shopware - connector for shopware 5
+SL::Shopconnector::Shopware - connector for shopware 5
 
 =head1 SYNOPSIS
 
@@ -377,8 +484,14 @@ for more information.
 
 =over 4
 
+=item C<get_one_order>
+
+Fetches one order specified by ordnumber
+
 =item C<get_new_orders>
 
+Fetches new order by parameters from shop configuration
+
 =item C<import_data_to_shop_order>
 
 Creates on shoporder object from json