diff --git a/www/pages/shopimporter_mirakl.php b/www/pages/shopimporter_mirakl.php index 48537af5..2f369bd6 100644 --- a/www/pages/shopimporter_mirakl.php +++ b/www/pages/shopimporter_mirakl.php @@ -29,6 +29,7 @@ class Shopimporter_Mirakl extends ShopimporterBase { private $category_identifier; private $offer_field_map; + private $product_field_map; public function __construct($app, $intern = false) { $this->app = $app; @@ -81,6 +82,31 @@ class Shopimporter_Mirakl extends ShopimporterBase { 'info' => '', 'default' => 'kategoriename' ], + 'product_field_map' => [ + 'typ' => 'textarea', + 'bezeichnung' => '{|Zuordnung Produkt-Felder je Kategorie (JSON)|}:', + 'cols' => 80, + 'rows' => 20, + 'info' => 'Die Felder werden vom Mirakl-Betreiber vorgegeben. Ist keine Kategorie definiert, gilt der Eintrag für alle Artikel. Jedes Feld kann wie folgt zugeordnet werden:
Artikelfeld: "Mirakel-Feldname": {"feld": "xyz"} oder kurz "Mirakel-Feldname": "xyz",
Freifeld: "Mirakel-Feldname": {"freifeld": "Bezeichnung in Shop"} (Siehe Reiter "Freifelder"),
Eigenschaft: "Mirakel-Feldname": {"eigenschaft": "Eigenschaftenname xyz"},
Fester Wert: "Mirakel-Feldname": {"wert": "xyz"}

Zusatzfelder zusätzlich mit der Eigenschaft "zusatzfeld": true versehen: z.B. "Mirakel-Feldname": {"feld": "name_de", "zusatzfeld": true}', + 'placeholder' => '[ + { + "kategorien": [ + "Schuhe", "Hosen" + ], + "felder": { + "category": {"freifeld": "Kategorie"}, + "Product.SellerProductID": {"feld": "nummer"}, + "SHOP.PRODUCT.TITLE": {"feld": "nummer"}, + "ATT.GLOBAL.Brandname": {"feld": "preis"}, + "Product.BaseUnit": {"freifeld": "Kategorie"}, + "ATT.GLOBAL.NoCUperOU": {"eigenschaft": "Mirakl Steuertext"}, + "ATT.GLOBAL.NoCUperOU__UNIT": {"wert": "false","zusatzfeld": true}, + "Product.TaxIndicator": {"wert": "1","zusatzfeld": true} + } + } +]' + ], + 'offer_field_map' => [ 'typ' => 'textarea', 'bezeichnung' => '{|Zuordnung Angebots-Felder je Kategorie (JSON)|}:', @@ -145,10 +171,11 @@ class Shopimporter_Mirakl extends ShopimporterBase { $this->category_identifier = array($einstellungen['felder']['category_identifier_source'] => $einstellungen['felder']['category_identifier_source_value']); - $this->offer_field_map = json_decode($einstellungen['felder']['offer_field_map'], true, flags: JSON_THROW_ON_ERROR); + $this->offer_field_map = json_decode($einstellungen['felder']['offer_field_map'], true, flags: JSON_THROW_ON_ERROR); + $this->product_field_map = json_decode($einstellungen['felder']['product_field_map'], true, flags: JSON_THROW_ON_ERROR); } - private function miraklRequest(string $endpoint, $postdata = null, array $getdata = null, string $content_type = null, bool $raw = false) { + private function miraklRequest(string $endpoint, $postdata = null, array $getdata = null, string $content_type = null, bool $raw = false, $debug = false, $debugurl = null) { $ch = curl_init(); $url_addition = ""; @@ -171,10 +198,17 @@ class Shopimporter_Mirakl extends ShopimporterBase { if (!empty($postdata)) { curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); + $headers[] = 'Content-Type: ' . $content_type; } - curl_setopt($ch, CURLOPT_URL, $this->shopUrl . $endpoint . $url_addition); + if ($debugurl) { + $url = $debugurl; + } else { + $url = $this->shopUrl; + } + + curl_setopt($ch, CURLOPT_URL, $url . $endpoint . $url_addition); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLINFO_HEADER_OUT, true); curl_setopt($ch, CURLOPT_VERBOSE, true); @@ -187,11 +221,12 @@ class Shopimporter_Mirakl extends ShopimporterBase { $information = curl_getinfo($ch); -/* print_r($information); - print_r($postdata); - print_r($response); - exit(); -*/ + if ($debug) { + print_r($information); + print_r($postdata); + print_r($response); + exit(); + } if ($raw) return $response; @@ -287,6 +322,8 @@ class Shopimporter_Mirakl extends ShopimporterBase { public function ImportSendList() { $articleList = $this->CatchRemoteCommand('data'); + + $this->Log('ImportSendList start', print_r($articleList,true)); // First gather all articles as offers and send them // Wait for import to finish @@ -378,12 +415,15 @@ class Shopimporter_Mirakl extends ShopimporterBase { $json_for_mirakl = json_encode($data_for_mirakl); + $this->Log('posting offer data', $json_for_mirakl); + $result = []; $response = $this->miraklRequest('offers', postdata: $json_for_mirakl, content_type: 'application/json', raw: true); $result = json_decode($response); if (!isset($result->import_id)) { + $this->Log('posting offer data error', print_r($response,true)); return(array('status' => false, 'message' => "Angebotsimport in Mirakl abgelehnt: ".print_r($response,true))); } @@ -405,16 +445,120 @@ class Shopimporter_Mirakl extends ShopimporterBase { } if ($status == 'FAILED') { + $this->Log('importing of offer data failed in mirakl', print_r($response,true)); return(array('status' => false, 'message' => "Angebotsimport in Mirakl fehlgeschlagen: ".print_r($response,true))); } - + if ($result->lines_in_error == 0) { + $this->Log('importing of offer data ok', print_r($response,true)); return($result->lines_in_success); } - // Check errors - $response = $this->miraklRequest('offers/imports/'.$import_id.'/error_report', raw: true); - return(array('status' => false, 'message' => "Angebotsimport in Mirakl hat Fehler: ".print_r($response,true))); + $this->Log('importing of offer returned with '.$result->lines_in_error.' lines', print_r($response,true)); + + $result = array(); + // Check errors with CSV unfucking... + $response = $this->miraklRequest('offers/imports/'.$import_id.'/error_report', raw: true); + $response_lines = preg_split('/\R/', $response, flags: PREG_SPLIT_NO_EMPTY); + + $error_message_key = null; + $firstline = true; + + foreach ($response_lines as $response_line) { + + $response_array = str_getcsv($response_line,';','"'); + + if ($firstline) { + $error_message_key = array_search("error-message",$response_array); + $firstline = false; + continue; + } + + switch ($response_array[$error_message_key]) { + case 'The product does not exist': + + // Try to create the product + $product_for_mirakl = array(); + + print_r($this->product_field_map); + + foreach ($this->product_field_map as $product_field_entry) { + foreach ($product_field_entry['felder'] as $product_field => $product_field_source) { + if (!is_array($product_field_source)) { + $product_field_source = array('feld' => $product_field_source); + } + $product_field_value = null; + $product_field_value = $this->GetFieldValue($article, $product_field_source); + $product_for_mirakl[$product_field] = $product_field_value; + } + } + + $this->Log('creating product', print_r($product_for_mirakl,true)); + + // Create CSV from array + $csv1 .= '"'.implode('";"',array_keys($product_for_mirakl)).'"'; + $csv2 .= '"'.implode('";"',$product_for_mirakl).'"'; + $csv = $csv1."\r\n".$csv2; + + $result = []; + + $this->Log('creating product csv', print_r($csv,true)); + + $postdata = array('file' => new CURLStringFile(postname: 'import.csv', data: $csv)); + + $response = $this->miraklRequest('products/imports', postdata: $postdata, content_type: 'multipart/form-data', raw: true); + + $result = json_decode($response); + + $this->Log('posting product data posted', print_r($result,true)); + + if (!isset($result->import_id)) { + $this->Log('posting product data error', print_r($response,true)); + return(array('status' => false, 'message' => "Produktimport in Mirakl abgelehnt: ".print_r($response,true))); + } + + $import_id = $result->import_id; + + // Wait for import to finish + + $status = null; + + /* + WAITING_SYNCHRONIZATION_PRODUCT, WAITING, RUNNING, COMPLETE, FAILED + */ + + while ($status != 'COMPLETE' && $status != 'FAILED') { + sleep(5); + $response = $this->miraklRequest('products/imports/'.$import_id, raw: true); + $result = json_decode($response); + $status = $result->import_status; + } + + if ($status == 'FAILED') { + $this->Log('importing of product data failed in mirakl', print_r($response,true)); + return(array('status' => false, 'message' => "Produktimport in Mirakl fehlgeschlagen: ".print_r($response,true))); + } + + if ($result->transform_lines_in_error == 0) { + $this->Log('importing of product data ok', print_r($response,true)); + return($result->lines_in_success); + } + + $this->Log('importing of product returned with '.$result->transform_lines_in_error.' error lines', print_r($response,true)); + + $response = $this->miraklRequest('products/imports/'.$import_id.'/transformation_error_report', raw: true); + + $this->Log('product import error report', print_r($response,true)); + + + break; + default: + $result[] = array('Unhandled error' => $response_array); + break; + } + } + + return(array('status' => false, 'message' => "Angebotsimport in Mirakl hat Fehler: ".print_r($result,true))); } @@ -424,7 +568,7 @@ class Shopimporter_Mirakl extends ShopimporterBase { private function Log($message, $dump = '') { if ($this->protocol) { - $this->app->erp->Logfile($message, print_r($dump, true)); + $this->app->erp->Logfile('Mirakl (Shop '.$this->shopid.') '.$message, print_r($dump, true)); } }