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 active => $shop_part->active,
280 #attribute => { attr1 => $cvars->{CVARNAME}->{value}, } , #HowTo handle attributes
282 supplier => 'AR', # Is needed by shopware,
283 descriptionLong => $shop_part->{shop_description},
284 active => $shop_part->active,
285 images => [ @upload_img ],
286 __options_images => { replace => 1, },
287 categories => [ @cat ],
288 description => $shop_part->{shop_description},
289 categories => [ @cat ],
295 my $dataString = SL::JSON::to_json(\%shop_data);
296 $dataString = encode_utf8($dataString);
300 my ($import,$data,$data_json);
301 my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
302 # 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
303 # LWP->post = create LWP->put = update
304 $data = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
305 $data_json = $data->content;
306 $import = SL::JSON::decode_json($data_json);
307 if($import->{success}){
309 my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
310 $upload = $self->connector->put($url . "api/articles/$partnumber?useNumberAsId=true", Content => $dataString);
311 my $data_json = $upload->content;
312 $upload_content = SL::JSON::decode_json($data_json);
315 $upload = $self->connector->post($url . "api/articles/", Content => $dataString);
316 my $data_json = $upload->content;
317 $upload_content = SL::JSON::decode_json($data_json);
319 # don't know if this is needed
321 my $partnumber = $::form->escape($part->{partnumber});#shopware don't accept / in articlenumber
322 my $imgup = $self->connector->put($url . "api/generatearticleimages/$partnumber?useNumberAsId=true");
325 return $upload_content->{success};
329 my ($self,$partnumber) = @_;
331 my $url = $self->url;
332 $partnumber = $::form->escape($partnumber);#shopware don't accept / in articlenumber
333 my $data = $self->connector->get($url . "api/articles/$partnumber?useNumberAsId=true");
334 my $data_json = $data->content;
335 return SL::JSON::decode_json($data_json);
340 $self->url($self->config->protocol . "://" . $self->config->server . ":" . $self->config->port . $self->config->path);
345 my $ua = LWP::UserAgent->new;
347 $self->config->server . ":" . $self->config->port,
348 $self->config->realm,
349 $self->config->login => $self->config->password
364 SL::Shopconnecter::Shopware - connector for shopware 5
371 This is the connector to shopware.
372 In this file you can do the mapping to your needs.
373 see https://developers.shopware.com/developers-guide/rest-api/
374 for more information.
380 =item C<get_new_orders>
382 =item C<import_data_to_shop_order>
384 Creates on shoporder object from json
385 Here is the mapping for the positions.
386 see https://developers.shopware.com/developers-guide/rest-api/
387 for detailed information
389 =item C<map_data_to_shoporder>
391 Here is the mapping for the order data.
392 see https://developers.shopware.com/developers-guide/rest-api/
393 for detailed information
395 =item C<get_categories>
399 Use this for test Connection
404 Here is the mapping for the article data.
405 see https://developers.shopware.com/developers-guide/rest-api/
406 for detailed information
426 Pricesrules, pricessources aren't fully implemented yet.
427 Payments aren't implemented( need to map payments from Shopware like invoice, paypal etc. to payments in kivitendo)
435 W. Hahn E<lt>wh@futureworldsearch.netE<gt>