2024-05-13 14:43:33 +02:00
< ? php
/*
* SPDX - FileCopyrightText : 2022 Andreas Palm
*
* SPDX - License - Identifier : LicenseRef - EGPL - 3.1
*/
2024-05-22 11:25:35 +02:00
class Shopimporter_Mirakl extends ShopimporterBase {
private $app ;
private $intern ;
private $shopid ;
private $protocol ;
private $apiKey ;
private $shopUrl ;
private $createManufacturerAllowed = false ;
private $idsabholen ;
private $idbearbeitung ;
private $idabgeschlossen ;
public $data ;
// TODO
private $langidToIso = [ 3 => 'de' , 1 => 'en' ];
private $taxationByDestinationCountry ;
private $orderSearchLimit ;
private $category_identifier_source ;
private $category_identifier_source_field ;
private $product_identifier_type ;
private $product_identifier_source ;
private $product_identifier_source_field ;
private $product_field_map ;
2024-05-23 20:12:05 +02:00
private $field_map ;
2024-05-22 11:25:35 +02:00
public function __construct ( $app , $intern = false ) {
$this -> app = $app ;
$this -> intern = $intern ;
if ( $intern )
return ;
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
/*
* See widget . shopexport . php
*/
public function EinstellungenStruktur () {
return [
'ausblenden' => [ 'abholmodus' => [ 'ab_nummer' , 'zeitbereich' ]],
'functions' => [ 'getarticlelist' ],
'felder' => [
'protokoll' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Protokollierung im Logfile|}:'
],
/* 'textekuerzen' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Texte bei Artikelexport auf Maximallänge kürzen|}:'
],
'useKeyAsParameter' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Shop Version ist mindestens 1.6.1.1|}:'
], */
'apikey' => [
'typ' => 'text' ,
'bezeichnung' => '{|API Key|}:' ,
'size' => 40 ,
],
'shopurl' => [
'typ' => 'text' ,
'bezeichnung' => '{|Shop URL|}:' ,
'size' => 40 ,
],
'shopid' => [
'typ' => 'text' ,
'bezeichnung' => '{|Shop ID des Shops|}:' ,
'size' => 40 ,
'info' => 'optional, int64'
],
2024-05-22 13:37:30 +02:00
/* 'category_identifier_source' => [
2024-05-22 11:25:35 +02:00
'typ' => 'select' ,
'bezeichnung' => '{|Kategorie-Identifizierer|}:' ,
'size' => 40 ,
'optionen' => [ 'Kategorie' => '{|Kategorie|}' , 'Freifeld' => '{|Freifeld|}' , 'Eigenschaft' => '{|Eigenschaft|}' ],
'info' => 'Feld in OpenXE für die Zuordnung der Artikel zu den Katalogkategorien in Mirakl'
],
'category_identifier_source_field' => [
'typ' => 'text' ,
'bezeichnung' => '{|Kategorie-Identifizierer Freifeld oder Eigenschaft|}:' ,
'size' => 40 ,
'info' => 'Wenn oben Freifeld oder Eigenschaft gewählt wurde'
],
'product_field_map' => [
'typ' => 'textarea' ,
'bezeichnung' => '{|Zuordnung Produkt-Felder je Kategorie (JSON)|}:' ,
'info' => 'Die Felder werden vom Mirakl-Betreiber vorgegeben. Mögliche Zuordnungen aus OpenXE sind: Artikelnummer, Artikelname, Einheit, Hersteller, Herstellernummer, EAN oder eine konkrete Artikeleigenschaft'
2024-05-22 13:37:30 +02:00
], */
2024-05-23 20:12:05 +02:00
'field_map' => [
2024-05-22 11:25:35 +02:00
'typ' => 'textarea' ,
'bezeichnung' => '{|Zuordnung Angebots-Felder je Kategorie (JSON)|}:' ,
2024-05-22 13:37:30 +02:00
'info' => 'Die Felder werden vom Mirakl-Betreiber vorgegeben. Zuordnung über "Mirakl-xyz": {"feld": "xyz"} oder kurz "Mirakl-xyz": "xyz" Mögliche Zuordnungen aus OpenXE sind: nummer, name_de, einheit, hersteller, herstellernummer, ean u.v.m. Freifelder wie im Reiter Freifelder mit Präfix \'freifeld_\', Eigenschaften: {"eigenschaft": "Eigenschaftenname xyz"}, Fester Wert: {"wert": "xyz"}, Zusatzfelder zusätzlich mit der Eigenschaft "zusatzfeld": true versehen: z.B. {"feld": "name_de", "zusatzfeld": true}'
2024-05-22 11:25:35 +02:00
],
/*
'steuergruppen' => [
'typ' => 'text' ,
'bezeichnung' => '{|Steuergruppenmapping|}:' ,
'size' => 40 ,
],
'zustand' => [
'typ' => 'text' ,
'bezeichnung' => '{|Freifeld Zustand|}:' ,
'size' => 40 ,
],
'abholen' => [
'typ' => 'text' ,
'bezeichnung' => '{|\'Abholen\' Status IDs|}:' ,
'size' => 40 ,
],
'bearbeitung' => [
'typ' => 'text' ,
'bezeichnung' => '{|\'In Bearbeitung\' Status IDs|}:' ,
'size' => 40 ,
],
'abgeschlossen' => [
'typ' => 'text' ,
'bezeichnung' => '{|\'Abgeschlossen\' Status IDs|}:' ,
'size' => 40 ,
],
'autoerstellehersteller' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Fehlende Hersteller automatisch anlegen|}:' ,
'col' => 2
],
'zeigezustand' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Artikelzustand im Shop anzeigen|}:' ,
'col' => 2
],
'zeigepreis' => [
'typ' => 'checkbox' ,
'bezeichnung' => '{|Artikelpreis im Shop anzeigen|}:' ,
'col' => 2
], */
]
];
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
public function getKonfig ( $shopid , $data ) {
$this -> shopid = $shopid ;
$this -> data = $data ;
$importerSettings = $this -> app -> DB -> SelectArr ( " SELECT `einstellungen_json` FROM `shopexport` WHERE `id` = ' $shopid ' LIMIT 1 " );
$importerSettings = reset ( $importerSettings );
$einstellungen = [];
if ( ! empty ( $importerSettings [ 'einstellungen_json' ])) {
$einstellungen = json_decode ( $importerSettings [ 'einstellungen_json' ], true );
}
$this -> protocol = $einstellungen [ 'felder' ][ 'protokoll' ];
$this -> apiKey = $einstellungen [ 'felder' ][ 'apikey' ];
$this -> shopUrl = rtrim ( $einstellungen [ 'felder' ][ 'shopurl' ], '/' ) . '/' ;
if ( $einstellungen [ 'felder' ][ 'autoerstellehersteller' ] === '1' ) {
$this -> createManufacturerAllowed = true ;
}
$this -> idsabholen = $einstellungen [ 'felder' ][ 'abholen' ];
$this -> idbearbeitung = $einstellungen [ 'felder' ][ 'bearbeitung' ];
$this -> idabgeschlossen = $einstellungen [ 'felder' ][ 'abgeschlossen' ];
$query = sprintf ( 'SELECT `steuerfreilieferlandexport` FROM `shopexport` WHERE `id` = %d' , $this -> shopid );
$this -> taxationByDestinationCountry = ! empty ( $this -> app -> DB -> Select ( $query ));
$this -> category_identifier_source = $einstellungen [ 'felder' ][ 'category_identifier_source' ];
$this -> category_identifier_source_field = $einstellungen [ 'felder' ][ 'category_identifier_source_field' ];
$this -> product_identifier_type = $einstellungen [ 'felder' ][ 'product_identifier_type' ];
$this -> product_identifier_source = $einstellungen [ 'felder' ][ 'product_identifier_source' ];
$this -> product_identifier_source_field = $einstellungen [ 'felder' ][ 'product_identifier_source_field' ];
$this -> product_field_map = json_decode ( $einstellungen [ 'felder' ][ 'product_field_map' ], true );
2024-05-23 20:12:05 +02:00
$this -> field_map = json_decode ( $einstellungen [ 'felder' ][ 'field_map' ], true );
2024-05-14 11:00:56 +02:00
}
2024-05-22 11:25:35 +02:00
private function miraklRequest ( string $endpoint , $postdata = null , array $getdata = null , string $content_type = null , bool $raw = false ) {
$ch = curl_init ();
$url_addition = " " ;
$headers = array ( " Authorization: " . $this -> apiKey );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
if ( ! empty ( $getdata )) {
$url_addition = " ? " ;
$ampersand = " " ;
foreach ( $getdata as $key => $value ) {
$url_addition .= $ampersand . $key . " = " . $value ;
$ampersand = " & " ;
}
} else 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 );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , $headers );
curl_setopt ( $ch , CURLINFO_HEADER_OUT , true );
curl_setopt ( $ch , CURLOPT_VERBOSE , true );
$response = curl_exec ( $ch );
if ( curl_error ( $ch )) {
$this -> error [] = curl_error ( $ch );
}
curl_close ( $ch );
$information = curl_getinfo ( $ch );
// print_r($information);
// print_r($postdata);
// print_r($response);
// exit();
if ( $raw )
return $response ;
return simplexml_load_string ( $response );
2024-05-14 11:00:56 +02:00
}
2024-05-22 11:25:35 +02:00
public function ImportAuth () {
$ch = curl_init ( $this -> shopUrl . " version " );
curl_setopt ( $ch , CURLOPT_HTTPHEADER , array ( " Authorization: " . $this -> apiKey ));
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
$response = curl_exec ( $ch );
$code = curl_getinfo ( $ch , CURLINFO_RESPONSE_CODE );
if ( $code == 200 ) {
return 'success ' . print_r ( $response , true );
}
return $response ;
}
/*
* Fetches article list from the shop , puts them into table shopexport_getarticles , starts the prozessstarter getarticles which fetches details for each article via ImportGetArticle ()
*/
2024-05-17 16:50:19 +02:00
2024-05-22 11:25:35 +02:00
public function ImportGetArticleList () {
$result = [];
2024-05-14 11:00:56 +02:00
2024-05-22 11:25:35 +02:00
$response = $this -> miraklRequest ( 'offers' , raw : true );
2024-05-13 14:43:33 +02:00
2024-05-22 11:25:35 +02:00
$result_array = json_decode ( $response );
foreach ( $result_array -> offers as $offer ) {
$result [] = $offer -> shop_sku ;
}
2024-05-14 11:00:56 +02:00
2024-05-22 11:25:35 +02:00
array_unique ( $result );
return $result ;
2024-05-14 11:00:56 +02:00
}
2024-05-13 14:43:33 +02:00
2024-05-22 11:25:35 +02:00
/*
* Fetches article details from the shop
*/
2024-05-17 16:50:19 +02:00
2024-05-22 11:25:35 +02:00
public function ImportGetArticle () {
2024-05-14 11:00:56 +02:00
2024-05-22 11:25:35 +02:00
$articleList = $this -> CatchRemoteCommand ( 'data' );
2024-05-14 11:00:56 +02:00
2024-05-22 11:25:35 +02:00
$parameters = array ( 'product_references' => 'productID' , 'product_' );
$response = $this -> miraklRequest ( 'products?' , raw : true );
throw new Exception ( " Not implemented " );
2024-05-13 14:43:33 +02:00
}
2024-05-23 20:12:05 +02:00
/*
* Gets a flexible mapped fieldvalue from feld , wert or eigenschaft
*/
public function GetFieldValue ( $article , $field_map_entry ) {
foreach ( $field_map_entry as $key => $value ) {
switch ( $key ) {
case 'feld' :
return ( $article [ $value ]);
break ;
case 'eigenschaft' :
$sql = " SELECT wert FROM artikeleigenschaften ae INNER JOIN artikeleigenschaftenwerte aew ON aew.artikeleigenschaften = ae.id WHERE aew.artikel = ' " . $article [ 'artikelid' ] . " ' AND ae.name = ' " . $value . " ' LIMIT 1 " ;
return ( $this -> app -> DB -> Select ( $sql ));
break ;
case 'wert' :
return ( $value );
break ;
}
}
return ( null );
}
2024-05-22 11:25:35 +02:00
/*
* Send articles to shop
*/
public function ImportSendList () {
2024-05-23 20:12:05 +02:00
2024-05-22 11:25:35 +02:00
$articleList = $this -> CatchRemoteCommand ( 'data' );
2024-05-23 20:12:05 +02:00
2024-05-22 11:25:35 +02:00
// First gather all articles as offers and send them
// Wait for import to finish
// Evaluate import
// Unimplemented (needed?)
// Select offers with no product
// Create products and send
// Wait for import to finish
// Evaluate import
foreach ( $articleList as $article ) {
/*
* Export offer
*/
2024-05-23 20:12:05 +02:00
$processed = false ;
2024-05-22 11:25:35 +02:00
$additional_fields = array ();
$offer_for_mirakl = array (
'state_code' => '11' , // ?!?!
'update_delete' => null // Update delete flag. Could be empty (means "update"), "update" or "delete".
);
2024-05-23 20:12:05 +02:00
foreach ( $this -> field_map [ 'angebotskonfiguration' ] as $offer_field_entry ) {
$kategorie = $this -> GetFieldValue ( $article , $this -> field_map [ 'katalogkonfiguration' ][ 'kategorie' ]);
if ( $offer_field_entry [ 'kategorie' ] != $kategorie && $offer_field_entry [ 'kategorie' ] != null ) {
continue ;
};
// Check Required attributes
$required = [
'product_id_type' ,
'product_id' ,
'shop_sku' ,
'price'
];
$missing = null ;
foreach ( $required as $key ) {
if ( ! isset ( $offer_field_entry [ 'felder' ][ $key ])) {
$missing [] = $key ;
}
}
if ( $missing ) {
return ( array ( 'status' => false , 'message' => " Pflichtfelder fehlen in Angebotskonfiguration von Kategorie \" " . $offer_field_entry [ 'kategorie' ] . " \" : " . implode ( ', ' , $missing )));
2024-05-22 13:37:30 +02:00
}
2024-05-23 20:12:05 +02:00
// Check Required attributes
foreach ( $offer_field_entry [ 'felder' ] as $offer_field => $offer_field_source ) {
if ( ! is_array ( $offer_field_source )) {
$offer_field_source = array ( 'feld' => $offer_field_source );
}
$offer_field_value = null ;
$is_additional_field = false ;
$offer_field_value = $this -> GetFieldValue ( $article , $offer_field_source );
if ( in_array ( 'zusatzfeld' , $offer_field_source )) {
$is_additional_field = true ;
}
if ( $is_additional_field ) {
$additional_field = array (
" code " => $offer_field ,
" value " => $offer_field_value
);
$additional_fields [] = $additional_field ;
} else {
$offer_for_mirakl [ $offer_field ] = $offer_field_value ;
}
}
$processed = true ;
}
2024-05-22 13:37:30 +02:00
2024-05-23 20:12:05 +02:00
if ( ! $processed ) {
return ( array ( 'status' => false , 'message' => " Angebotskonfiguration für Kategorie \" " . $kategorie . " \" nicht gefunden " ));
2024-05-22 11:25:35 +02:00
}
if ( ! empty ( $additional_fields )) {
$offer_for_mirakl [ 'offer_additional_fields' ] = $additional_fields ;
}
$offers_for_mirakl [] = $offer_for_mirakl ;
}
$data_for_mirakl = array ();
$data_for_mirakl [ 'offers' ] = $offers_for_mirakl ;
$json_for_mirakl = json_encode ( $data_for_mirakl );
// print_r($json_for_mirakl);
// exit();
$result = [];
$response = $this -> miraklRequest ( 'offers' , postdata : $json_for_mirakl , content_type : 'application/json' , raw : true );
$result = json_decode ( $response );
if ( ! isset ( $result -> import_id )) {
2024-05-23 20:12:05 +02:00
return ( array ( 'status' => false , 'message' => " Angebotsimport in Mirakl abgelehnt: " . print_r ( $response , true )));
2024-05-22 11:25:35 +02:00
}
2024-05-17 16:50:19 +02:00
2024-05-22 11:25:35 +02:00
$import_id = $result -> import_id ;
// Wait for import to finish
2024-05-17 16:50:19 +02:00
2024-05-22 11:25:35 +02:00
$status = null ;
/*
WAITING_SYNCHRONIZATION_PRODUCT , WAITING , RUNNING , COMPLETE , FAILED
*/
while ( $status != 'COMPLETE' && $status != 'FAILED' ) {
sleep ( 5 );
$response = $this -> miraklRequest ( 'offers/imports/' . $import_id , raw : true );
$result = json_decode ( $response );
$status = $result -> status ;
}
if ( $status == 'FAILED' ) {
2024-05-23 20:12:05 +02:00
return ( array ( 'status' => false , 'message' => " Angebotsimport in Mirakl fehlgeschlagen: " . print_r ( $response , true )));
2024-05-22 11:25:35 +02:00
}
if ( $result -> lines_in_error == 0 ) {
return ( $result -> lines_in_success );
}
// Check errors
$response = $this -> miraklRequest ( 'offers/imports/' . $import_id . '/error_report' , raw : true );
2024-05-23 20:12:05 +02:00
return ( array ( 'status' => false , 'message' => " Angebotsimport in Mirakl hat Fehler: " . print_r ( $response , true )));
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
private function getOrdersToProcess ( int $limit ) {
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
private function Log ( $message , $dump = '' ) {
if ( $this -> protocol ) {
$this -> app -> erp -> Logfile ( $message , print_r ( $dump , true ));
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
}
2024-05-13 14:43:33 +02:00
2024-05-22 11:25:35 +02:00
public function ImportDeleteAuftrag () {
}
2024-05-13 14:43:33 +02:00
2024-05-22 11:25:35 +02:00
public function ImportUpdateAuftrag () {
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00
public function ImportGetAuftraegeAnzahl () {
}
2024-05-13 14:43:33 +02:00
2024-05-22 11:25:35 +02:00
public function ImportGetAuftrag () {
}
2024-05-13 14:43:33 +02:00
}
2024-05-22 11:25:35 +02:00