Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 856a3b09

Von Werner Hahn vor mehr als 7 Jahren hinzugefügt

  • ID 856a3b09bbe4d64234219ad7f6aa540d0535f90a
  • Vorgänger e01550a6
  • Nachfolger 9ae85179

WebshopApi: Shopconnector für Shopware

Unterschiede anzeigen:

SL/ShopConnector/ALL.pm
3 3
use strict;
4 4

  
5 5
my %shop_connector_by_name = (
6
  shopware    => 'SL::ShopConnector::Shopware',
6 7
);
7 8

  
8 9
my %shop_connector_by_connector = (
10
  shopware   => 'SL::ShopConnector::Shopware',
9 11
);
10 12

  
11 13
my @shop_connector_order = qw(
......
13 15
);
14 16

  
15 17
my @shop_connectors = (
18
  { id => "shopware",   description => "Shopware" },
16 19
);
17 20

  
18 21

  
SL/ShopConnector/Shopware.pm
1
package SL::ShopConnector::Shopware;
2

  
3
use strict;
4

  
5
use parent qw(SL::ShopConnector::Base);
6

  
7

  
8
use SL::JSON;
9
use LWP::UserAgent;
10
use LWP::Authen::Digest;
11
use SL::DB::ShopOrder;
12
use SL::DB::ShopOrderItem;
13
use SL::DB::History;
14
use DateTime::Format::Strptime;
15
use SL::DB::File;
16
use Data::Dumper;
17
use Sort::Naturally ();
18
use SL::Helper::Flash;
19
use Encode qw(encode_utf8);
20
use SL::File;
21
use File::Slurp;
22

  
23
use Rose::Object::MakeMethods::Generic (
24
  'scalar --get_set_init' => [ qw(connector url) ],
25
);
26

  
27
sub get_new_orders {
28
  my ($self, $id) = @_;
29

  
30
  my $url              = $self->url;
31
  my $ordnumber        = $self->config->last_order_number + 1;
32
  my $otf              = $self->config->orders_to_fetch;
33
  my $of               = 0;
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);
37

  
38
  if ($orders_import->{success}){
39
    foreach my $shoporder(@{ $orders_import->{data} }){
40

  
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);
44

  
45
      $self->import_data_to_shop_order($import);
46

  
47
      $self->config->assign_attributes( last_order_number => $ordnumber);
48
      $self->config->save;
49
      $ordnumber++;
50
      $of++;
51
    }
52
  }
53
  my $shop           = $self->config->description;
54
  my %fetched_orders = (shop_id => $self->config->description, number_of_orders => $of);
55
  return \%fetched_orders;
56
}
57

  
58
sub import_data_to_shop_order {
59
  my ( $self, $import ) = @_;
60
  my $shop_order = $self->map_data_to_shoporder($import);
61

  
62
  $shop_order->save;
63
  my $id = $shop_order->id;
64

  
65
  my @positions = sort { Sort::Naturally::ncmp($a->{"partnumber"}, $b->{"partnumber"}) } @{ $import->{data}->{details} };
66
  my $position = 1;
67
  my $active_price_source = $self->config->price_source;
68
  #Mapping Positions
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},
73
                        price                => $price,
74
                        quantity             => $pos->{quantity},
75
                        position             => $position,
76
                        tax_rate             => $pos->{taxRate},
77
                        shop_trans_id        => $pos->{articleId},
78
                        shop_order_id        => $id,
79
                        active_price_source  => $active_price_source,
80
                      );
81
    my $pos_insert = SL::DB::ShopOrderItem->new(%pos_columns);
82
    $pos_insert->save;
83
    $position++;
84
  }
85
  $shop_order->{positions} = $position-1;
86

  
87
  my $customer = $shop_order->get_customer;
88

  
89
  if(ref($customer)){
90
    $shop_order->kivi_customer_id($customer->id);
91
    $shop_order->save;
92
  }
93
}
94

  
95
sub map_data_to_shoporder {
96
  my ($self, $import) = @_;
97

  
98
  my $parser = DateTime::Format::Strptime->new( pattern   => '%Y-%m-%dT%H:%M:%S',
99
                                                  locale    => 'de_DE',
100
                                                  time_zone => 'local'
101
                                                );
102
  my $orderdate = $parser->parse_datetime($import->{data}->{orderTime});
103

  
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
107
  my %columns = (
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},
166
    shop_id                 => $shop_id,
167
    shop_ordernumber        => $import->{data}->{number},
168
    shop_trans_id           => $import->{data}->{id},
169
    tax_included            => $tax_included eq "brutto" ? 1 : 0,
170
  );
171

  
172
  my $shop_order = SL::DB::ShopOrder->new(%columns);
173
  return $shop_order;
174
}
175

  
176
sub get_categories {
177
  my ($self) = @_;
178

  
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;
185

  
186
  for(@daten) {
187
    my $parent = $categories{$_->{parentId}};
188
    $parent->{children} ||= [];
189
    push @{$parent->{children}},$_;
190
  }
191

  
192
  return \@daten;
193
}
194

  
195
sub get_version {
196
  my ($self) = @_;
197

  
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;
202

  
203
  if($data->is_success && $type eq 'application/json'){
204
    my $data_json = $data->content;
205
    return SL::JSON::decode_json($data_json);
206
  }else{
207
    my %return = ( success => 0,
208
                   data    => { version => $url . ": " . $status_line, revision => $type },
209
                   message => "Server not found or wrong data type",
210
                );
211
    return \%return;
212
  }
213
}
214

  
215
sub update_part {
216
  my ($self, $shop_part, $todo) = @_;
217

  
218
  #shop_part is passed as a param
219
  die unless ref($shop_part) eq 'SL::DB::ShopPart';
220

  
221
  my $url = $self->url;
222
  my $part = SL::DB::Part->new(id => $shop_part->{part_id})->load;
223

  
224
  # CVARS to map
225
  my $cvars = { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $part->cvars_by_config } };
226

  
227
  my @cat = ();
228
  foreach my $row_cat ( @{ $shop_part->shop_category } ) {
229
    my $temp = { ( id => @{$row_cat}[0], ) };
230
    push ( @cat, $temp );
231
  }
232

  
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
238
  my %shop_data;
239

  
240
  if($todo eq "price"){
241
    %shop_data = ( mainDetail => { number   => $part->{partnumber},
242
                                   prices   =>  [ { from             => 1,
243
                                                    price            => $price,
244
                                                    customerGroupKey => 'EK',
245
                                                  },
246
                                                ],
247
                                  },
248
                 );
249
  }elsif($todo eq "stock"){
250
    %shop_data = ( mainDetail => { number   => $part->{partnumber},
251
                                   inStock  => $part->{onhand},
252
                                 },
253
                 );
254
  }elsif($todo eq "price_stock"){
255
    %shop_data =  ( mainDetail => { number   => $part->{partnumber},
256
                                    inStock  => $part->{onhand},
257
                                    prices   =>  [ { from             => 1,
258
                                                     price            => $price,
259
                                                     customerGroupKey => 'EK',
260
                                                   },
261
                                                 ],
262
                                   },
263
                   );
264
  }elsif($todo eq "active"){
265
    %shop_data =  ( mainDetail => { number   => $part->{partnumber},
266
                                   },
267
                    active => ($part->{partnumber} == 1 ? 0 : 1),
268
                   );
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,
275
                                                                       price  => $price,
276
                                                            customerGroupKey  => 'EK',
277
                                                            },
278
                                                          ],
279
                                             #attribute => { attr1  => $cvars->{CVARNAME}->{value}, } , #HowTo handle attributes
280
                                       },
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 ],
289
                      tax               => $taxrate,
290
                    )
291
                  ;
292
  }
293

  
294
  my $dataString = SL::JSON::to_json(\%shop_data);
295
  $dataString    = encode_utf8($dataString);
296

  
297
  my $upload_content;
298
  my $upload;
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}){
307
    #update
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);
312
  }else{
313
    #upload
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);
317
  }
318
  # don't know if this is needed
319
  if(@upload_img) {
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");
322
  }
323

  
324
  return $upload_content->{success};
325
}
326

  
327
sub get_article {
328
  my ($self,$partnumber) = @_;
329

  
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);
335
}
336

  
337
sub init_url {
338
  my ($self) = @_;
339
  $self->url($self->config->protocol . "://" . $self->config->server . ":" . $self->config->port . $self->config->path);
340
}
341

  
342
sub init_connector {
343
  my ($self) = @_;
344
  my $ua = LWP::UserAgent->new;
345
  $ua->credentials(
346
      $self->config->server . ":" . $self->config->port,
347
      $self->config->realm,
348
      $self->config->login => $self->config->password
349
  );
350

  
351
  return $ua;
352

  
353
}
354

  
355
1;
356

  
357
__END__
358

  
359
=encoding utf-8
360

  
361
=head1 NAME
362

  
363
SL::Shopconnecter::Shopware - connector for shopware 5
364

  
365
=head1 SYNOPSIS
366

  
367

  
368
=head1 DESCRIPTION
369

  
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.
374

  
375
=head1 METHODS
376

  
377
=over 4
378

  
379
=item C<get_new_orders>
380

  
381
=item C<import_data_to_shop_order>
382

  
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
387

  
388
=item C<map_data_to_shoporder>
389

  
390
Here is the mapping for the order data.
391
see https://developers.shopware.com/developers-guide/rest-api/
392
for detailed information
393

  
394
=item C<get_categories>
395

  
396
=item C<get_version>
397

  
398
Use this for test Connection
399
see SL::Shop
400

  
401
=item C<update_part>
402

  
403
Here is the mapping for the article data.
404
see https://developers.shopware.com/developers-guide/rest-api/
405
for detailed information
406

  
407
=item C<get_article>
408

  
409
=back
410

  
411
=head1 INITS
412

  
413
=over 4
414

  
415
=item init_url
416

  
417
build an url for LWP
418

  
419
=item init_connector
420

  
421
=back
422

  
423
=head1 TODO
424

  
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)
427

  
428
=head1 BUGS
429

  
430
None yet. :)
431

  
432
=head1 AUTHOR
433

  
434
W. Hahn E<lt>wh@futureworldsearch.netE<gt>
435

  
436
=cut

Auch abrufbar als: Unified diff