1 package SL::ShopConnector::Shopware;
 
   5 use parent qw(SL::ShopConnector::Base);
 
  10 use LWP::Authen::Digest;
 
  11 use SL::DB::ShopOrder;
 
  12 use SL::DB::ShopOrderItem;
 
  14 use DateTime::Format::Strptime;
 
  17 use Sort::Naturally ();
 
  18 use SL::Helper::Flash;
 
  19 use Encode qw(encode_utf8);
 
  23 use Rose::Object::MakeMethods::Generic (
 
  24   'scalar --get_set_init' => [ qw(connector url) ],
 
  31   my $ordnumber        = $self->config->last_order_number + 1;
 
  32   my $otf              = $self->config->orders_to_fetch;
 
  34   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);
 
  35   my $orders_data_json = $orders_data->content;
 
  36   my $orders_import    = SL::JSON::decode_json($orders_data_json);
 
  38   if ($orders_import->{success}){
 
  39     foreach my $shoporder(@{ $orders_import->{data} }){
 
  41       my $data      = $self->connector->get($url . "api/orders/" . $shoporder->{id});
 
  42       my $data_json = $data->content;
 
  43       my $import    = SL::JSON::decode_json($data_json);
 
  45       $self->import_data_to_shop_order($import);
 
  47       $self->config->assign_attributes( last_order_number => $ordnumber);
 
  53   my $shop           = $self->config->description;
 
  54   my %fetched_orders = (shop_id => $self->config->description, number_of_orders => $of);
 
  55   return \%fetched_orders;
 
  58 sub import_data_to_shop_order {
 
  59   my ( $self, $import ) = @_;
 
  60   my $shop_order = $self->map_data_to_shoporder($import);
 
  63   my $id = $shop_order->id;
 
  65   my @positions = sort { Sort::Naturally::ncmp($a->{"partnumber"}, $b->{"partnumber"}) } @{ $import->{data}->{details} };
 
  67   my $active_price_source = $self->config->price_source;
 
  69   foreach my $pos(@positions) {
 
  70     my $price = $::form->round_amount($pos->{price},2);
 
  71     my %pos_columns = ( description          => $pos->{articleName},
 
  72                         partnumber           => $pos->{articleNumber},
 
  74                         quantity             => $pos->{quantity},
 
  75                         position             => $position,
 
  76                         tax_rate             => $pos->{taxRate},
 
  77                         shop_trans_id        => $pos->{articleId},
 
  79                         active_price_source  => $active_price_source,
 
  81     my $pos_insert = SL::DB::ShopOrderItem->new(%pos_columns);
 
  85   $shop_order->{positions} = $position-1;
 
  87   my $customer = $shop_order->get_customer;
 
  90     $shop_order->kivi_customer_id($customer->id);
 
  95 sub map_data_to_shoporder {
 
  96   my ($self, $import) = @_;
 
  98   my $parser = DateTime::Format::Strptime->new( pattern   => '%Y-%m-%dT%H:%M:%S',
 
 102   my $orderdate = $parser->parse_datetime($import->{data}->{orderTime});
 
 104   my $shop_id      = $self->config->id;
 
 105   my $tax_included = $self->config->pricetype;
 
 106   # Mapping to table shoporders. See http://community.shopware.com/_detail_1690.html#GET_.28Liste.29
 
 108     amount                  => $import->{data}->{invoiceAmount},
 
 109     billing_city            => $import->{data}->{billing}->{city},
 
 110     billing_company         => $import->{data}->{billing}->{company},
 
 111     billing_country         => $import->{data}->{billing}->{country}->{name},
 
 112     billing_department      => $import->{data}->{billing}->{department},
 
 113     billing_email           => $import->{data}->{customer}->{email},
 
 114     billing_fax             => $import->{data}->{billing}->{fax},
 
 115     billing_firstname       => $import->{data}->{billing}->{firstName},
 
 116     #billing_greeting        => ($import->{data}->{billing}->{salutation} eq 'mr' ? 'Herr' : 'Frau'),
 
 117     billing_lastname        => $import->{data}->{billing}->{lastName},
 
 118     billing_phone           => $import->{data}->{billing}->{phone},
 
 119     billing_street          => $import->{data}->{billing}->{street},
 
 120     billing_vat             => $import->{data}->{billing}->{vatId},
 
 121     billing_zipcode         => $import->{data}->{billing}->{zipCode},
 
 122     customer_city           => $import->{data}->{billing}->{city},
 
 123     customer_company        => $import->{data}->{billing}->{company},
 
 124     customer_country        => $import->{data}->{billing}->{country}->{name},
 
 125     customer_department     => $import->{data}->{billing}->{department},
 
 126     customer_email          => $import->{data}->{customer}->{email},
 
 127     customer_fax            => $import->{data}->{billing}->{fax},
 
 128     customer_firstname      => $import->{data}->{billing}->{firstName},
 
 129     #customer_greeting       => ($import->{data}->{billing}->{salutation} eq 'mr' ? 'Herr' : 'Frau'),
 
 130     customer_lastname       => $import->{data}->{billing}->{lastName},
 
 131     customer_phone          => $import->{data}->{billing}->{phone},
 
 132     customer_street         => $import->{data}->{billing}->{street},
 
 133     customer_vat            => $import->{data}->{billing}->{vatId},
 
 134     customer_zipcode        => $import->{data}->{billing}->{zipCode},
 
 135     customer_newsletter     => $import->{data}->{customer}->{newsletter},
 
 136     delivery_city           => $import->{data}->{shipping}->{city},
 
 137     delivery_company        => $import->{data}->{shipping}->{company},
 
 138     delivery_country        => $import->{data}->{shipping}->{country}->{name},
 
 139     delivery_department     => $import->{data}->{shipping}->{department},
 
 140     delivery_email          => "",
 
 141     delivery_fax            => $import->{data}->{shipping}->{fax},
 
 142     delivery_firstname      => $import->{data}->{shipping}->{firstName},
 
 143     #delivery_greeting       => ($import->{data}->{shipping}->{salutation} eq 'mr' ? 'Herr' : 'Frau'),
 
 144     delivery_lastname       => $import->{data}->{shipping}->{lastName},
 
 145     delivery_phone          => $import->{data}->{shipping}->{phone},
 
 146     delivery_street         => $import->{data}->{shipping}->{street},
 
 147     delivery_vat            => $import->{data}->{shipping}->{vatId},
 
 148     delivery_zipcode        => $import->{data}->{shipping}->{zipCode},
 
 149     host                    => $import->{data}->{shop}->{hosts},
 
 150     netamount               => $import->{data}->{invoiceAmountNet},
 
 151     order_date              => $orderdate,
 
 152     payment_description     => $import->{data}->{payment}->{description},
 
 153     payment_id              => $import->{data}->{paymentId},
 
 154     remote_ip               => $import->{data}->{remoteAddress},
 
 155     sepa_account_holder     => $import->{data}->{paymentIntances}->{accountHolder},
 
 156     sepa_bic                => $import->{data}->{paymentIntances}->{bic},
 
 157     sepa_iban               => $import->{data}->{paymentIntances}->{iban},
 
 158     shipping_costs          => $import->{data}->{invoiceShipping},
 
 159     shipping_costs_net      => $import->{data}->{invoiceShippingNet},
 
 160     shop_c_billing_id       => $import->{data}->{billing}->{customerId},
 
 161     shop_c_billing_number   => $import->{data}->{billing}->{number},
 
 162     shop_c_delivery_id      => $import->{data}->{shipping}->{id},
 
 163     shop_customer_id        => $import->{data}->{customerId},
 
 164     shop_customer_number    => $import->{data}->{billing}->{number},
 
 165     shop_customer_comment   => $import->{data}->{customerComment},
 
 167     shop_ordernumber        => $import->{data}->{number},
 
 168     shop_trans_id           => $import->{data}->{id},
 
 169     tax_included            => $tax_included eq "brutto" ? 1 : 0,
 
 172   my $shop_order = SL::DB::ShopOrder->new(%columns);
 
 179   my $url        = $self->url;
 
 180   my $data       = $self->connector->get($url . "api/categories");
 
 181   my $data_json  = $data->content;
 
 182   my $import     = SL::JSON::decode_json($data_json);
 
 183   my @daten      = @{$import->{data}};
 
 184   my %categories = map { ($_->{id} => $_) } @daten;
 
 187     my $parent = $categories{$_->{parentId}};
 
 188     $parent->{children} ||= [];
 
 189     push @{$parent->{children}},$_;
 
 198   my $url       = $self->url;
 
 199   my $data      = $self->connector->get($url . "api/version");
 
 200   my $type = $data->content_type;
 
 201   my $status_line = $data->status_line;
 
 203   if($data->is_success && $type eq 'application/json'){
 
 204     my $data_json = $data->content;
 
 205     return SL::JSON::decode_json($data_json);
 
 207     my %return = ( success => 0,
 
 208                    data    => { version => $url . ": " . $status_line, revision => $type },
 
 209                    message => "Server not found or wrong data type",
 
 216   my ($self, $shop_part, $todo) = @_;
 
 218   #shop_part is passed as a param
 
 219   die unless ref($shop_part) eq 'SL::DB::ShopPart';
 
 221   my $url = $self->url;
 
 222   my $part = SL::DB::Part->new(id => $shop_part->{part_id})->load;
 
 225   my $cvars = { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $part->cvars_by_config } };
 
 228   foreach my $row_cat ( @{ $shop_part->shop_category } ) {
 
 229     my $temp = { ( id => @{$row_cat}[0], ) };
 
 230     push ( @cat, $temp );
 
 233   my @upload_img = $shop_part->get_images;
 
 234   my $tax_n_price = $shop_part->get_tax_and_price;
 
 235   my $price = $tax_n_price->{price};
 
 236   my $taxrate = $tax_n_price->{tax};
 
 237   # mapping to shopware still missing attributes,metatags
 
 240   if($todo eq "price"){
 
 241     %shop_data = ( mainDetail => { number   => $part->{partnumber},
 
 242                                    prices   =>  [ { from             => 1,
 
 244                                                     customerGroupKey => 'EK',
 
 249   }elsif($todo eq "stock"){
 
 250     %shop_data = ( mainDetail => { number   => $part->{partnumber},
 
 251                                    inStock  => $part->{onhand},
 
 254   }elsif($todo eq "price_stock"){
 
 255     %shop_data =  ( mainDetail => { number   => $part->{partnumber},
 
 256                                     inStock  => $part->{onhand},
 
 257                                     prices   =>  [ { from             => 1,
 
 259                                                      customerGroupKey => 'EK',
 
 264   }elsif($todo eq "active"){
 
 265     %shop_data =  ( mainDetail => { number   => $part->{partnumber},
 
 267                     active => ($part->{partnumber} == 1 ? 0 : 1),
 
 269   }elsif($todo eq "all"){
 
 270   # mapping to shopware still missing attributes,metatags
 
 271     %shop_data =  (   name              => $part->{description},
 
 272                       mainDetail        => { number   => $part->{partnumber},
 
 273                                              inStock  => $part->{onhand},
 
 274                                              prices   =>  [ {          from   => 1,
 
 276                                                             customerGroupKey  => 'EK',
 
 279                                              #attribute => { attr1  => $cvars->{CVARNAME}->{value}, } , #HowTo handle attributes
 
 281                       supplier          => 'AR', # Is needed by shopware,
 
 282                       descriptionLong   => $shop_part->{shop_description},
 
 283                       active            => $shop_part->active,
 
 284                       images            => [ @upload_img ],
 
 285                       __options_images  => { replace => 1, },
 
 286                       categories        => [ @cat ],
 
 287                       description       => $shop_part->{shop_description},
 
 288                       categories        => [ @cat ],
 
 294   my $dataString = SL::JSON::to_json(\%shop_data);
 
 295   $dataString    = encode_utf8($dataString);
 
 299   my ($import,$data,$data_json);
 
 300   my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
 
 301   # 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
 
 302   # LWP->post = create LWP->put = update
 
 303     $data       = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
 
 304     $data_json  = $data->content;
 
 305     $import     = SL::JSON::decode_json($data_json);
 
 306   if($import->{success}){
 
 308     my $partnumber  = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
 
 309     $upload         = $self->connector->put($url . "api/articles/$partnumber?useNumberAsId=true", Content => $dataString);
 
 310     my $data_json   = $upload->content;
 
 311     $upload_content = SL::JSON::decode_json($data_json);
 
 314     $upload         = $self->connector->post($url . "api/articles/", Content => $dataString);
 
 315     my $data_json   = $upload->content;
 
 316     $upload_content = SL::JSON::decode_json($data_json);
 
 318   # don't know if this is needed
 
 320     my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
 
 321     my $imgup      = $self->connector->put($url . "api/generatearticleimages/$partnumber?useNumberAsId=true");
 
 324   return $upload_content->{success};
 
 328   my ($self,$partnumber) = @_;
 
 330   my $url       = $self->url;
 
 331   $partnumber   = $::form->escape($partnumber);#shopware don't accept / in articlenumber
 
 332   my $data      = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
 
 333   my $data_json = $data->content;
 
 334   return SL::JSON::decode_json($data_json);
 
 339   $self->url($self->config->protocol . "://" . $self->config->server . ":" . $self->config->port . $self->config->path);
 
 344   my $ua = LWP::UserAgent->new;
 
 346       $self->config->server . ":" . $self->config->port,
 
 347       $self->config->realm,
 
 348       $self->config->login => $self->config->password
 
 363 SL::Shopconnecter::Shopware - connector for shopware 5
 
 370 This is the connector to shopware.
 
 371 In this file you can do the mapping to your needs.
 
 372 see https://developers.shopware.com/developers-guide/rest-api/
 
 373 for more information.
 
 379 =item C<get_new_orders>
 
 381 =item C<import_data_to_shop_order>
 
 383 Creates on shoporder object from json
 
 384 Here is the mapping for the positions.
 
 385 see https://developers.shopware.com/developers-guide/rest-api/
 
 386 for detailed information
 
 388 =item C<map_data_to_shoporder>
 
 390 Here is the mapping for the order data.
 
 391 see https://developers.shopware.com/developers-guide/rest-api/
 
 392 for detailed information
 
 394 =item C<get_categories>
 
 398 Use this for test Connection
 
 403 Here is the mapping for the article data.
 
 404 see https://developers.shopware.com/developers-guide/rest-api/
 
 405 for detailed information
 
 425 Pricesrules, pricessources aren't fully implemented yet.
 
 426 Payments aren't implemented( need to map payments from Shopware like invoice, paypal etc. to payments in kivitendo)
 
 434 W. Hahn E<lt>wh@futureworldsearch.netE<gt>