app = $app; $this->intern = true; if ($intern) { return; } $this->app->ActionHandlerInit($this); $this->app->ActionHandler('list', 'Shopimporter_Shopware6List'); $this->app->ActionHandler('auth', 'ImportAuth'); $this->app->ActionHandler('sendlistlager', 'ImportSendListLager'); $this->app->ActionHandler('getauftraegeanzahl', 'ImportGetAuftraegeAnzahl'); $this->app->ActionHandler('getauftrag', 'ImportGetAuftrag'); $this->app->ActionHandler('deleteauftrag', 'ImportDeleteAuftrag'); $this->app->ActionHandler('updateauftrag', 'ImportUpdateAuftrag'); $this->app->ActionHandler('storniereauftrag','ImportStorniereAuftrag'); $this->app->ActionHandler('getarticle','ImportGetArticle'); $this->app->ActionHandler('getarticlelist','ImportGetArticleList'); $this->app->ActionHandler("updatezahlungsstatus","ImportUpdateZahlungsstatus"); $this->app->DefaultActionHandler('list'); $this->app->ActionHandlerListen($app); } /** * @param string $productId * * @return mixed */ public function addSyncCustomFieldToProduct(string $productId) { $customField = [ 'customFields' => [ 'wawision_shopimporter_syncstate' => 1 ] ]; return $this->shopwareRequest('PATCH', "product/{$productId}", $customField); } /** * @param string $orderId * * @return mixed */ public function addCustomFieldToOrder(string $orderId) { $customField = [ 'customFields' => [ 'wawision_shopimporter_syncstate' => 1 ] ]; return $this->shopwareRequest('PATCH', "order/{$orderId}", $customField); } public function ImportGetArticleList() { $page = 1; $limit = 500; do { $productIdsToAdd = []; $searchdata = [ 'limit' => $limit, 'page' => $page, 'filter' => [ [ 'field' => 'product.parentId', 'type' => 'equals', 'value' => null ] ] ]; $productsInShop = $this->shopwareRequest('POST', 'search/product', $searchdata); if (!empty($productsInShop['data'])) { foreach ($productsInShop['data'] as $productInShop) { $productIdsToAdd[] = $productInShop['id']; } } foreach ($productIdsToAdd as $productId) { $this->app->DB->Insert("INSERT INTO shopexport_getarticles (shop, nummer) VALUES ('$this->shopid', '" . $this->app->DB->real_escape_string($productId) . "')"); } $page++; } while (count($productsInShop['data']) === $limit); $anzahl = $this->app->DB->Select("SELECT COUNT(id) FROM shopexport_getarticles WHERE shop=$this->shopid"); $this->app->erp->SetKonfigurationValue('artikelimportanzahl_' . $this->shopid, $anzahl); } /** * @param string $method * @param string $endpoint * @param string $data * * @param array $headerInformation * @return mixed */ public function shopwareRequest($method, $endpoint, $data = '', $headerInformation = []) { $accessToken = $this->shopwareToken(); $url = $this->ShopUrl; $url .= $endpoint; $ch = curl_init(); $headerInformation[] = 'Content-Type:application/json'; $headerInformation[] = 'Authorization:Bearer ' . $accessToken['token']; curl_setopt($ch, CURLOPT_URL, $url); if (!empty($data)) { curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); } curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headerInformation); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); if (curl_error($ch)) { $this->error[] = curl_error($ch); } curl_close($ch); return json_decode($response, true); } /** * @return array */ protected function shopwareToken() { $result = []; $result['success'] = true; $result['token'] = $this->accessToken; $result['message'] = 'Keine Antwort von API erhalten.'; if (!empty($result['token'])) { return $result; } $result['success'] = false; $data = [ 'username' => $this->UserName, 'password' => $this->Password, 'grant_type' => 'password', 'scopes' => 'write', 'client_id' => 'administration', ]; $ch = curl_init($this->ShopUrl . 'oauth/token'); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/json', 'Content-Type: application/json', 'Cache-Control: no-cache', ] ); $response = json_decode(curl_exec($ch), true); if (!empty((string)$response['title'])) { $result['message'] = $response['title']; } if (!empty($response['access_token'])) { $result['success'] = true; $this->accessToken = $response['access_token']; $result['token'] = $response['access_token']; } return $result; } public function ImportGetArticle() { $tmp = $this->CatchRemoteCommand('data'); if (isset($tmp['nummerintern'])) { $nummer = $tmp['nummerintern']; $response = $this->shopwareRequest('GET', 'product/' . $nummer); if (empty($response['data'])) { $this->error[] = 'Artikel in der Shop Datenbank nicht gefunden!'; return; } $nummer = $response['data']['attributes']['productNumber']; } else { $nummer = $tmp['nummer']; } $articleInfo = $this->shopwareRequest('GET', 'product?filter[product.productNumber]=' . $nummer . '&associations[manufacturer][]&associations[properties][]'); if (empty($articleInfo['data'][0])) { $this->error[] = 'Artikel in der Shop Datenbank nicht gefunden!'; return; } $articleIdInShop = $articleInfo['data'][0]['id']; if(empty($articleInfo['data'][0]['customFields']) || empty($articleInfo['data'][0]['customFields']['wawision_shopimporter_syncstate'])){ $this->addSyncCustomFieldToProduct((string)$articleIdInShop); } $articleInfo = $this->shopwareRequest('GET', 'product?filter[product.productNumber]=' . $nummer . '&associations[manufacturer][]&associations[properties][]'); $associatedInformation = []; $properties = []; foreach ($articleInfo['included'] as $includedInformation) { if ($includedInformation['type'] === 'property_group_option') { $properties[$includedInformation['id']] = $includedInformation['attributes']; } else { $associatedInformation[$includedInformation['id']] = $includedInformation['attributes']; } } $groups = []; if (!empty($properties)) { $groupsInShop = $this->shopwareRequest('GET', 'property-group'); foreach ($groupsInShop['data'] as $groupInShop) { $groups[$groupInShop['id']] = $groupInShop['attributes']['name']; } } $media = $this->shopwareRequest('GET', 'product/' . $articleIdInShop . '/media'); $imagesToAdd = []; if (!empty($media['included'])) { foreach ($media['included'] as $mediaInfo) { if ($mediaInfo['type'] === 'media') { $imagesToAdd[] = [ 'content' => base64_encode(@file_get_contents($mediaInfo['attributes']['url'])), 'path' => $mediaInfo['attributes']['url'], 'id' => $mediaInfo['id'] ]; } } } $articleInfo = $articleInfo['data'][0]['attributes']; $data = []; $data['name'] = $articleInfo['name']; if (isset($tmp['nummerintern'])) { $data['nummer'] = $articleInfo['productNumber']; } $data['artikelnummerausshop'] = $articleInfo['productNumber']; $data['restmenge'] = $articleInfo['stock']; $data['uebersicht_de'] = $articleInfo['description']; $data['preis_netto'] = $articleInfo['price'][0]['net']; if (!empty($articleInfo['price'][0]['listPrice'])) { $data['pseudopreis'] = $articleInfo['price'][0]['listPrice']; } $data['aktiv'] = $articleInfo['active']; if (!empty($articleInfo['weight'])) { $data['gewicht'] = $articleInfo['weight']; } if (!empty($articleInfo['manufacturerNumber'])) { $data['herstellernummer'] = $articleInfo['manufacturerNumber']; } if (!empty($articleInfo['ean'])) { $data['ean'] = $articleInfo['ean']; } if (!empty($articleInfo['manufacturerId'])) { $data['hersteller'] = $associatedInformation[$articleInfo['manufacturerId']]['name']; } if (!empty($articleInfo['taxId'])) { $data['umsatzsteuer'] = $associatedInformation[$articleInfo['taxId']]['taxRate']; } if (!empty($properties)) { foreach ($properties as $property) { if ($this->propertyOption === 'toProperties') { $data['eigenschaften'][] = [ 'name' => $groups[$property['groupId']], 'values' => $property['name'], ]; } if ($this->propertyOption === 'toCustomFields') { $data['freifeld_' . $groups[$property['groupId']]] = $property['name']; } } } if (!empty($articleInfo['customFields'])) { foreach ($articleInfo['customFields'] as $customFieldName => $customFieldValue) { if ($this->freeFieldOption === 'toProperties') { $data['eigenschaften'][] = [ 'name' => $customFieldName, 'values' => $customFieldValue ]; } if ($this->freeFieldOption === 'toCustomFields') { $data['freifeld_' . $customFieldName] = $customFieldValue; } } } if (!empty($imagesToAdd)) { $data['bilder'] = $imagesToAdd; } if ($articleInfo['childCount'] > 0) { $data = [$data]; $limit = 50; $page = 1; $optionInfo = []; $optionGroupInfo = []; do { $searchdata = [ 'limit' => $limit, 'page' => $page, 'filter' => [ [ 'field' => 'product.parentId', 'type' => 'equals', 'value' => $articleIdInShop ] ], 'sort' => [ [ 'field' => 'product.options.groupId', 'naturalSorting' => false, 'order' => 'ASC' ], [ 'field' => 'product.options.id', 'naturalSorting' => false, 'order' => 'ASC' ] ], 'associations' => [ 'options' => [ 'sort' => [ [ 'field' => 'groupId', 'naturalSorting' => false, 'order' => 'ASC' ], [ 'field' => 'id', 'naturalSorting' => false, 'order' => 'ASC' ] ] ] ] ]; $variantsInShop = $this->shopwareRequest('POST', 'search/product', $searchdata); foreach ($variantsInShop['included'] as $includedInfo) { if ($includedInfo['type'] === 'property_group_option') { $optionInfo[$includedInfo['id']] = $includedInfo['attributes']; if (empty($optionGroupInfo[$includedInfo['attributes']['groupId']])) { $optionGroupInfo[$includedInfo['attributes']['groupId']] = (!empty($optionGroupInfo)?count($optionGroupInfo):0) + 1; } } } foreach ($variantsInShop['data'] as $variantInShop) { $variantData = []; $variantName = $data[0]['name']; foreach ($variantInShop['attributes']['optionIds'] as $optionId) { $variantData['matrixprodukt_wert' . $optionGroupInfo[$optionInfo[$optionId]['groupId']]] = $optionInfo[$optionId]['name']; $variantName .= ' - ' . $optionInfo[$optionId]['name']; } $variantData['name'] = $variantName; $variantData['nummer'] = $variantInShop['attributes']['productNumber']; $variantData['artikelnummerausshop'] = $variantInShop['attributes']['productNumber']; $variantData['restmenge'] = $variantInShop['attributes']['stock']; $variantData['uebersicht_de'] = $variantInShop['attributes']['description']; if (empty($variantInShop['attributes']['price'][0]['net'])) { $variantData['preis_netto'] = $data[0]['preis_netto']; } else { $variantData['preis_netto'] = $variantInShop['attributes']['price'][0]['net']; } if (!empty($variantInShop['attributes']['price'][0]['listPrice'])) { $variantData['pseudopreis'] = $variantInShop['attributes']['price'][0]['listPrice']; } $variantData['aktiv'] = $variantInShop['attributes']['active']; if (!empty($variantInShop['attributes']['weight'])) { $variantData['gewicht'] = $variantInShop['attributes']['weight']; } if (!empty($variantInShop['attributes']['manufacturerNumber'])) { $variantData['herstellernummer'] = $variantInShop['attributes']['manufacturerNumber']; } if (!empty($variantInShop['attributes']['ean'])) { $variantData['ean'] = $variantInShop['attributes']['ean']; } if (!empty($data[0]['umsatzsteuer'])) { $variantData['umsatzsteuer'] = $data[0]['umsatzsteuer']; } $data[] = $variantData; } $page++; } while (count($variantsInShop['data']) > $limit); foreach ($optionGroupInfo as $groupId => $sorting) { $data[0]['matrixprodukt_gruppe' . $sorting] = $groups[$groupId]; } foreach ($optionInfo as $optionData) { $data[0]['matrixprodukt_optionen' . $optionGroupInfo[$optionData['groupId']]][] = $optionData['name']; } } //TODO Staffelpreise //TODO Kategorien //TODO Freifelder //TODO Crossselling return $data; } /** * @param array $data * * @return array */ public function checkApiApp($data) { foreach (['shopwareUserName', 'shopwarePassword', 'shopwareUrl'] as $field) { if (empty($data['data'][$field])) { return ['success' => false, 'error' => sprintf('%s is empty', $field)]; } } $shops = $this->app->DB->SelectArr( sprintf( "SELECT `einstellungen_json`, `bezeichnung`,`id` FROM `shopexport` WHERE `modulename` = 'shopimporter_shopware6' AND `einstellungen_json` IS NOT NULL AND `einstellungen_json` <> ''" ) ); if (empty($shops)) { return [ 'info' => [ 'Shop' => 'Shopware', 'info' => 'Url ' . $data['data']['shopwareUrl'], ] ]; } foreach ($shops as $shop) { if (empty($shop['einstellungen_json'])) { continue; } $json = @json_decode($shop['einstellungen_json'], true); if (empty($json['felder']) || empty($json['felder']['shopwareUrl'])) { continue; } if ($json['felder']['shopwareUrl'] === $data['data']['shopwareUrl']) { return [ 'success' => false, 'error' => sprintf('Shop with url %s allready exists', $data['data']['shopwareUrl']) ]; } } return [ 'info' => [ 'Shop' => 'Shopware', 'info' => 'Url ' . $data['data']['shopwareUrl'], ] ]; } /** * */ public function Shopimporter_Shopware6List() { $msg = $this->app->erp->base64_url_encode('
Sie können hier die Shops einstellen
'); header('Location: index.php?module=onlineshops&action=list&msg=' . $msg); exit; } /** * @param $shopid * @param $data */ public function getKonfig($shopid, $data) { $this->shopid = $shopid; $this->data = $data; $importerSettings = $this->app->DB->SelectArr("SELECT `einstellungen_json`, `kategorienuebertragen` FROM `shopexport` WHERE `id` = '$shopid' LIMIT 1"); $importerSettings = reset($importerSettings); $this->exportCategories = (bool) $importerSettings['kategorienuebertragen']; $einstellungen = []; if (!empty($importerSettings['einstellungen_json'])) { $einstellungen = json_decode($importerSettings['einstellungen_json'], true); } $this->protocol = $einstellungen['felder']['protocol']; $this->UserName = $einstellungen['felder']['shopwareUserName']; $this->Password = $einstellungen['felder']['shopwarePassword']; $this->ShopUrl = rtrim($einstellungen['felder']['shopwareUrl'], '/') . '/'; $this->createManufacturerAllowed = false; if ($einstellungen['felder']['shopwareAllowCreateManufacturer'] === '1') { $this->createManufacturerAllowed = true; } $this->defaultManufacturer = $einstellungen['felder']['shopwareDefaultManufacturer']; $this->defaultRuleName = $einstellungen['felder']['shopwareDefaultRuleName']; $this->statesToFetch = $einstellungen['felder']['statesToFetch']; $this->deliveryStatesToFetch = $einstellungen['felder']['deliveryStatesToFetch']; $this->transactionStatesToFetch = $einstellungen['felder']['transactionStatesToFetch']; $this->salesChannelToFetch = $einstellungen['felder']['salesChannelToFetch']; $this->orderSearchLimit = $einstellungen['felder']['orderSearchLimit']; $this->freeFieldOption = $einstellungen['felder']['shopwareFreeFieldOption']; $this->propertyOption = $einstellungen['felder']['shopwarePropertyOption']; $this->shopwareDefaultSalesChannel = $einstellungen['felder']['shopwareDefaultSalesChannel']; $this->shopwareMediaFolder = $einstellungen['felder']['shopwareMediaFolder']; $this->normalTaxId = $einstellungen['felder']['normalTaxId']; $this->reducedTaxId = $einstellungen['felder']['reducedTaxId']; $query = sprintf('SELECT `steuerfreilieferlandexport` FROM `shopexport` WHERE `id` = %d', $this->shopid); $this->taxationByDestinationCountry = !empty($this->app->DB->Select($query)); $this->client = $this->app->Container->get('Shopware6Client'); $this->client->setCredentials( $this->UserName, $this->Password, $this->ShopUrl ); } /** * @return array */ public function EinstellungenStruktur() { return [ 'ausblenden' => ['abholmodus' => ['ab_nummer']], 'functions' => ['exportartikelbaum','getarticlelist','updatezahlungsstatus'], 'felder' => [ 'protocol' => [ 'typ' => 'checkbox', 'bezeichnung' => '{|Protokollierung im Logfile|}:', ], 'shopwareUserName' => [ 'typ' => 'text', 'bezeichnung' => '{|Benutzername|}:', 'size' => 40, ], 'shopwarePassword' => [ 'typ' => 'text', 'bezeichnung' => '{|Passwort|}:', 'size' => 40, ], 'shopwareUrl' => [ 'typ' => 'text', 'bezeichnung' => '{|Shop API URL|}:', 'size' => 40, ], 'shopwareDefaultManufacturer' => [ 'typ' => 'text', 'bezeichnung' => '{|Standard Hersteller|}:', 'size' => 40, 'default' => 'Keine Herstellerinformation', ], 'shopwareAllowCreateManufacturer' => [ 'typ' => 'checkbox', 'bezeichnung' => '{|Bei Artikelexport Hersteller anlegen|}:', ], 'shopwareDefaultRuleName' => [ 'typ' => 'text', 'bezeichnung' => '{|Name der Standardpreisgruppe|}:', 'size' => 40, 'default' => 'All customers', ], 'shopwarePropertyOption' => [ 'heading' => '{|Eigenschaften / Freifeld Zuordnung|}', 'typ' => 'select', 'bezeichnung' => '{|Xentral Artikel Eigenschaften|}:', 'size' => 40, 'default' => 'toProperties', 'optionen' => ['toProperties' => '{|Shopware Eigenschaften|}', 'toCustomFields' => '{|Shopware Zusatzfelder|}', 'doNotExport' => '{|Nicht übertragen|}'] ], 'shopwareFreeFieldOption' => [ 'typ' => 'select', 'bezeichnung' => '{|Xentral Artikel Freifelder|}:', 'size' => 40, 'default' => 'toCustomFields', 'optionen' => ['toProperties' => '{|Shopware Eigenschaften|}', 'toCustomFields' => '{|Shopware Zusatzfelder|}', 'doNotExport' => '{|Nicht übertragen|}'] ], 'shopwareDefaultSalesChannel' => [ 'heading' => '{|Artikelexport Standardeinstellungen|}', 'typ' => 'text', 'bezeichnung' => '{|Standard Sichtbarkeit|}:', 'size' => 40 ], 'shopwareMediaFolder' => [ 'typ' => 'text', 'bezeichnung' => '{|Media Folder für Artikelbilder|}:', 'size' => 40, 'default' => 'Product Media' ], 'normalTaxId' => [ 'typ' => 'text', 'bezeichnung' => '{|TaxId für Steuersatz "normal"|}', 'size' => 40, ], 'reducedTaxId' => [ 'typ' => 'text', 'bezeichnung' => '{|TaxId für Steuersatz "ermäßigt"|}', 'size' => 40, ], 'statesToFetch' => [ 'typ' => 'text', 'bezeichnung' => '{|Abzuholender Bestellstatus|}:', 'size' => 40, 'default' => 'open', 'col' => 2, 'info' => '
Erlaubte Werte: open;in_progress;completed;cancelled' ], 'deliveryStatesToFetch' => [ 'typ' => 'text', 'bezeichnung' => '{|Eingrenzen auf Lieferstatus|}:', 'size' => 40, 'default' => '', 'col' => 2, 'info' => '
Erlaubte Werte: open;shipped_partially;shipped;returned;returned_partially;cancelled' ], 'transactionStatesToFetch' => [ 'typ' => 'text', 'bezeichnung' => '{|Eingrenzen auf Bezahlstatus|}:', 'size' => 40, 'default' => '', 'col' => 2, 'info' => '
Erlaubte Werte: open;paid;authorized;paid_partially;refunded;refunded_partially;reminded;cancelled' ], 'salesChannelToFetch' => [ 'typ' => 'text', 'bezeichnung' => '{|Eingrenzen auf Sales Channel|}:', 'size' => 40, 'default' => '', 'col' => 2, 'info' => '
Klicke auf "Verbindung prüfen" um die verfügbaren Channels (bitte die Id verwenden) anzuzeigen.' ], 'orderSearchLimit' => [ 'typ' => 'select', 'bezeichnung' => '{|Anzahl Aufträge abholen|}:', 'optionen' => [ '25' => '25', '50' => '50', '75' => '75', '100' => '100', ], 'default' => '25', 'col' => 2 ], ], ]; } public function ImportUpdateZahlungsstatus() { $tmp = $this->CatchRemoteCommand('data'); $auftrag = $tmp['auftrag']; $transactions = $this->shopwareRequest('GET', 'order/'.$auftrag.'/transactions'); $transactionId = $transactions['data'][0]['id']; if(empty($transactionId)){ return; } $response = $this->shopwareRequest('POST', '_action/order_transaction/'.$transactionId.'/state/paid'); if (!empty($response['id'])) { return 'ok'; } } public function ImportSendArtikelbaum(){ $xentralCategoryTree = []; $this->app->erp->GetKategorienbaum($xentralCategoryTree, 0, 0, $this->shopid); $xentralCategoryIdToParentId = []; foreach ($xentralCategoryTree as $key => $value) { $xentralCategoryTree[$key]['erledigt'] = false; $xentralCategoryTree[$key]['shopid'] = ''; $xentralCategoryTree[$key]['aktiv'] = false; $xentralCategoryIdToParentId[$value['id']] = $key; } $parentCategoryId = null; foreach ($xentralCategoryTree as $index => $categoryData) { $this->createCategoryTree($index, $xentralCategoryTree, $xentralCategoryIdToParentId, $parentCategoryId); } } protected function createCategoryTree($id, &$xentralCategoryTree, $xentralCategoryIdToParentId, $parentCategoryId) { $parentId = $parentCategoryId; if ($xentralCategoryTree[$id]['parent']) { $parentId = $xentralCategoryTree[$xentralCategoryIdToParentId[$xentralCategoryTree[$id]['parent']]]['shopid']; } if ($xentralCategoryTree[$id]['parent'] && !$xentralCategoryTree[$xentralCategoryIdToParentId[$xentralCategoryTree[$id]['parent']]]['erledigt']) { $this->createCategoryTree($xentralCategoryIdToParentId[$xentralCategoryTree[$id]['parent']], $xentralCategoryTree, $xentralCategoryIdToParentId, $parentCategoryId); } $xentralCategoryTree[$id]['erledigt'] = true; $categoryName = $xentralCategoryTree[$id]['bezeichnung']; $searchdata = [ 'limit' => 25, 'filter' => [ [ 'field' => 'category.name', 'type' => 'equals', 'value' => $categoryName ], [ 'field' => 'category.parentId', 'type' => 'equals', 'value' => $parentId ] ] ]; $categoriesInShop = $this->shopwareRequest('POST', 'search/category', $searchdata); $categoryId = ''; if (!empty($categoriesInShop['data'])) { $categoryId = $categoriesInShop['data'][0]['id']; } if (!$categoryId) { $categoryData = [ 'parentId' => $parentId, 'name' => $categoryName ]; $result = $this->shopwareRequest('POST', 'category?_response=true', $categoryData); if ($result['data']['id']) { $categoryId = $result['data']['id']; } } if ($categoryId) { $xentralCategoryTree[$id]['shopid'] = $categoryId; } } /** * @return int */ public function ImportSendListLager() { $tmp = $this->CatchRemoteCommand('data'); $count = 0; foreach ($tmp as $article) { $artikel = $article['artikel']; if ($artikel === 'ignore') { continue; } $nummer = $article['nummer']; $fremdnummer = $article['fremdnummer']; if (!empty($fremdnummer)) { $nummer = $fremdnummer; } $articleInfo = $this->shopwareRequest('GET', 'product?filter[product.productNumber]=' . $nummer); if (empty($articleInfo['data'][0]['id'])) { $this->Shopware6Log('Artikel wurde nicht im Shop gefunden: ' . $nummer, $articleInfo); continue; } if(empty($articleInfo['data'][0]['customFields']) || empty($articleInfo['data'][0]['customFields']['wawision_shopimporter_syncstate'])){ $this->addSyncCustomFieldToProduct((string)$articleInfo['data'][0]['id']); } $active = true; if ($article['inaktiv']) { $active = false; } $stock = $article['anzahl_lager']; if (!empty($article['pseudolager'])) { $stock = $article['pseudolager']; } $stock = $this->getCorrectedStockFromAvailable($active, (int)$stock, $articleInfo); $data = [ 'stock' => $stock, 'active' => $active, ]; $response = $this->shopwareRequest('PATCH', 'product/' . $articleInfo['data'][0]['id'], $data); $this->Shopware6Log('Lagerbestand konnte nicht uebertragen werden fuer Artikel: ' . $nummer, $response); $count++; } return $count; } /** * @param bool $isStockActive * @param int $stock * @param array|null $articleInfo * * @return int */ public function getCorrectedStockFromAvailable(bool $isStockActive, int $stock, ?array $articleInfo): int { if(!$isStockActive) { return $stock; } if(empty($articleInfo)) { return $stock; } if(!isset($articleInfo['data'][0]['attributes']['availableStock'])) { return $stock; } if(!isset($articleInfo['data'][0]['attributes']['availableStock'])) { return $stock; } $reserved = (int)$articleInfo['data'][0]['attributes']['stock'] - (int)$articleInfo['data'][0]['attributes']['availableStock']; if($reserved <= 0) { return $stock; } return $stock + $reserved; } /** * @param string $message * @param mixed $dump */ public function Shopware6Log($message, $dump = '') { if ($this->protocol) { $this->app->erp->Logfile($message, print_r($dump, true)); } } /** * @return int */ public function ImportSendList() { $articleList = $this->CatchRemoteCommand('data'); $successCounter = 0; foreach ($articleList as $article) { $number = $article['nummer']; $articleInfo = $this->shopwareRequest( 'GET', sprintf('product?filter[product.productNumber]=%s', $number) ); $articleIdShopware = ''; if (!empty($articleInfo['data'][0]['id'])) { $articleIdShopware = $articleInfo['data'][0]['id']; } $quantity = $article['anzahl_lager']; if (!empty($article['pseudolager'])) { $quantity = $article['pseudolager']; } $inaktiv = $article['inaktiv']; $active = true; if (!empty($inaktiv)) { $active = false; } $quantity = $this->getCorrectedStockFromAvailable($active, (int)$quantity, $articleInfo); $taxRate = (float)$article['steuersatz']; if (!empty($this->normalTaxId) && $article['umsatzsteuer'] == 'normal') $taxId = $this->normalTaxId; else if (!empty($this->reducedTaxId) && $article['umsatzsteuer'] == 'ermaessigt') $taxId = $this->reducedTaxId; else $taxId = $this->getTaxIdByRate($taxRate); $mediaToAdd = $this->mediaToExport($article, $articleIdShopware); $categoriesToAdd = []; if($this->exportCategories){ $categoriesToAdd = $this->categoriesToExport($article, $articleIdShopware); } $propertiesToAdd = $this->propertiesToExport($article, $articleIdShopware); $crosselingToAdd = $this->crosssellingToExport($article, $articleIdShopware); $systemFieldsToAdd = $this->systemFieldsToExport($article, $articleIdShopware); $deliveryTimeId = null; if(!empty($article['lieferzeitmanuell'])){ $deliveryTimeId = $this->getDeliveryTimeId($article['lieferzeitmanuell']); } if (empty($systemFieldsToAdd['visibilities']) && !empty($this->shopwareDefaultSalesChannel)) { $systemFieldsToAdd['visibilities'] = $this->modifySalesChannel(explode(',', $this->shopwareDefaultSalesChannel), $articleIdShopware); } if(empty($systemFieldsToAdd['unitId']) && !empty($article['einheit']) ){ $systemFieldsToAdd['unitId'] = $this->unitToAdd($article['einheit']); } //Hersteller in Shopware suchen bzw. Anlegen $manufacturerName = $article['hersteller']; $manufacturerId = $this->getManufacturerIdByName($manufacturerName); if ($manufacturerId === null && $this->createManufacturerAllowed === true) { $manufacturerId = $this->createManufacturer($manufacturerName); } if (empty($manufacturerId)) { return 'error: Für den Artikelexport ist die Herstellerinformation zwingend erforderlich'; } $isCloseOut = false; if(!empty($article['restmenge'])){ $isCloseOut = true; } $description = $this->prepareDescription($article['uebersicht_de']); $ean = $article['ean']; $metaTitle = $article['metatitle_de']; $metaDescription = $article['metadescription_de']; $metaKeywords = $article['metakeywords_de']; $manufacturerNumber = $article['herstellernummer']; if (empty($manufacturerNumber)) { $manufacturerNumber = ''; } $weight = (float)$article['gewicht']; $length = (float)$article['laenge'] * 10; $height = (float)$article['hoehe'] * 10; $width = (float)$article['breite'] * 10; $purchasePrice = (float)$article['einkaufspreis']; $currencyId = $this->findCurrencyId($article['waehrung']); $price = [ 'net' => $article['preis'], 'gross' => $article['bruttopreis'], 'currencyId' => $currencyId, 'linked' => true]; if (!empty($article['pseudopreis'])) { $price['listPrice'] = [ 'currencyId' => $currencyId, 'gross' => $article['pseudopreis'], 'linked' => true, 'net' => $article['pseudopreis']/(1+$taxRate/100) ]; } $data = [ 'name' => $article['name_de'], 'isCloseout' => $isCloseOut, 'productNumber' => $number, 'manufacturerId' => $manufacturerId, 'stock' => (int)$quantity, 'taxId' => $taxId, 'active' => $active, 'description' => $description, 'ean' => $ean, 'metaTitle' => $metaTitle, 'metaDescription' => $metaDescription, 'keywords' => $metaKeywords, 'manufacturerNumber' => $manufacturerNumber, 'length' => $length, 'width' => $width, 'height' => $height, 'weight' => $weight, 'purchasePrice' => $purchasePrice, 'price' => [$price], 'categories' => $categoriesToAdd, 'properties' => $propertiesToAdd, 'crossSellings' => $crosselingToAdd, 'media' => $mediaToAdd, 'deliveryTimeId' => $deliveryTimeId ]; if (!$article['texteuebertragen']) { unset($data['description']); } $data = array_merge($data, $systemFieldsToAdd); if(empty($data['customFields']) || empty($data['customFields']['wawision_shopimporter_syncstate'])){ $data['customFields']['wawision_shopimporter_syncstate'] = 1; } if (empty($articleIdShopware)) { $result = $this->shopwareRequest('POST', 'product?_response=true', $data); if (!empty($result['data']['id'])) { $articleIdShopware = $result['data']['id']; $articleInfo['data'][0] = $result['data']; } } else { $headerInformation = []; $languageId = $this->getLanguageIdByCountryIso('DE'); if (!empty($languageId)) { $headerInformation[] = 'sw-language-id: ' . $languageId; } $result = $this->shopwareRequest('PATCH', sprintf('product/%s?_response=true', $articleIdShopware), $data, $headerInformation); } if(!empty($articleIdShopware) && $article['texteuebertragen']) { $this->exportTranslationsForArticle($article, $articleIdShopware); } $this->addCoverImage($article, $articleIdShopware); if (empty($result['data']) || is_array($result['errors'])) { $this->Shopware6Log('Artikelexport fehlgeschlagen', ['data:' => $data, 'response' => $result]); continue; } $this->exportSeoUrls($article, $articleIdShopware); $this->exportVariants($article, $articleIdShopware, $currencyId); if (empty($result['data']) || is_array($result['errors'])) { $this->Shopware6Log('Artikelexport bei Bildübertragung fehlgeschlagen', ['data:' => $data, 'response' => $result]); continue; } $defaultPrices = $this->getPricesFromArray($article['staffelpreise_standard'] ?? []); $groupPrices = $this->getPricesFromArray($article['staffelpreise_gruppen'] ?? []); if (!empty($defaultPrices) || !empty($groupPrices)) { $this->deleteOldBulkPrices($articleIdShopware); } if (!empty($defaultPrices)) { foreach ($defaultPrices as $priceData) { $this->exportBulkPriceForGroup($articleIdShopware, $this->defaultRuleName, $priceData); } } if (!empty($groupPrices)) { foreach ($groupPrices as $priceData) { $this->exportBulkPriceForGroup($articleIdShopware, $priceData->getGroupName(), $priceData); } } $successCounter++; } return $successCounter; } protected function exportBulkPriceForGroup(string $productId, string $groupName, PriceData $priceData): void { $currencyId = $this->findCurrencyId($priceData->getCurrency()); $groupRuleId = $this->client->getGroupRuleId($groupName); if (empty($groupRuleId)) { $this->Shopware6Log("Fehler: Gruppe {$groupName} konnte im Shop nicht gefunden werden"); return; } $result = $this->client->saveBulkPrice($productId, $groupRuleId, $currencyId, $priceData); if (empty($result['data'])) { $this->Shopware6Log("Fehler: Staffelpreis für Gruppe {$groupName} konnte nicht exportiert werden", $result); } } /** * @param string $deliveryTimeText * * @return string|null */ protected function getDeliveryTimeId(string $deliveryTimeText): ?string { $searchCommand = [ 'limit' => 5, 'filter' => [ [ 'field' => 'name', 'type' => 'equals', 'value' => $deliveryTimeText ] ] ]; $result = $this->shopwareRequest('POST', 'search/delivery-time', $searchCommand); if (empty($result['data'][0]['id'])) { return null; } return $result['data'][0]['id']; } /** * @param string $description * @return string */ protected function prepareDescription($description): string { $markupSubstitute = [ '/"/' => '"', '/<([^&]+)>/' => '<\1>', '/\\/' => '', '/\\<\/strong>/' => '', '/\\/' => '', '/\\<\/em>/' => '', '/&/' => '&', ]; return (string)preg_replace(array_keys($markupSubstitute), array_values($markupSubstitute), $description); } /** * @param array $article * @param string $articleIdShopware */ protected function exportTranslationsForArticle(array $article, string $articleIdShopware): void { $customFieldsToAdd = $this->customFieldsToExport($article, $articleIdShopware); $preparedTranslations = []; $preparedTranslations['DE'] = [ 'name' => $article['name_de'], 'description' => $this->prepareDescription($article['uebersicht_de']), 'metaTitle' => $article['metatitle_de'], 'metaDescription' => $article['metadescription_de'], 'keywords' => $article['metakeywords_de'], 'customFields' => [] ]; if(!empty($customFieldsToAdd['DE'])){ $preparedTranslations['DE']['customFields'] = $customFieldsToAdd['DE']; } $preparedTranslations['GB'] = [ 'name' => $article['name_en'], 'description' => $this->prepareDescription($article['uebersicht_en']), 'metaTitle' => $article['metatitle_en'], 'metaDescription' => $article['metadescription_en'], 'keywords' => $article['metakeywords_en'], 'customFields' => [], ]; if(!empty($customFieldsToAdd['GB'])){ $preparedTranslations['GB']['customFields'] = $customFieldsToAdd['GB']; } foreach ($article['texte'] as $translation) { if ($translation['sprache'] === 'EN') { $translation['sprache'] = 'GB'; } $preparedTranslations[$translation['sprache']] = [ 'name' => $translation['name'], 'description' => $this->prepareDescription($translation['beschreibung_online']), 'metaTitle' => $translation['meta_title'], 'metaDescription' => $translation['meta_description'], 'keywords' => $translation['meta_keywords'], ]; if(!empty($customFieldsToAdd[$translation['sprache']])){ $preparedTranslations[$translation['sprache']]['customFields'] = $customFieldsToAdd[$translation['sprache']]; } } foreach ($preparedTranslations as $countryIsoCode => $translation) { $languageId = $this->getLanguageIdByCountryIso($countryIsoCode); if (empty($languageId)) { $this->Shopware6Log('Language Id not found for country: ' . $countryIsoCode); continue; } $headerInformation = ['sw-language-id: ' . $languageId]; $this->shopwareRequest( 'PATCH', sprintf('product/%s', $articleIdShopware), $translation, $headerInformation ); } } /** * @param string $countryIso * * @return string|null */ protected function getLanguageIdByCountryIso(string $countryIso): ?string { if(array_key_exists($countryIso, $this->knownShopLanguageIds)){ return $this->knownShopLanguageIds[$countryIso]; } $searchCommand = [ 'limit' => 5, 'filter' => [ [ 'field' => 'country.iso', 'type' => 'equals', 'value' => $countryIso ] ] ]; $countryInformation = $this->shopwareRequest('POST', 'search/country', $searchCommand); foreach ($countryInformation['data'] as $country){ $searchCommand = [ 'limit' => 5, 'filter' => [ [ 'field' => 'locale.territory', 'type' => 'equals', 'value' => $country['attributes']['name'] ] ] ]; $localeInformation = $this->shopwareRequest('POST', 'search/locale', $searchCommand); foreach ($localeInformation['data'] as $locale) { $searchCommand = [ 'limit' => 5, 'filter' => [ [ 'field' => 'language.localeId', 'type' => 'equals', 'value' => $locale['id'] ] ] ]; $languageInformation = $this->shopwareRequest('POST', 'search/language', $searchCommand); if (!empty($languageInformation['data'][0]['id'])) { $this->knownShopLanguageIds[$countryIso] = $languageInformation['data'][0]['id']; return $languageInformation['data'][0]['id']; } } } $this->knownShopLanguageIds[$countryIso] = null; return null; } /** * @param string $manufacturerName * * @return null|string */ protected function createManufacturer(string $manufacturerName): ?string { $data = ['name' => $manufacturerName]; $response = $this->shopwareRequest('POST', 'product-manufacturer?_response=true', $data); $manufacturerId = null; if(!empty($response['data']['id'])){ $manufacturerId = $response['data']['id']; $this->knownManufacturerIds[$manufacturerName] = $manufacturerId; } return $manufacturerId; } /** * @param string $manufacturerName * * @return null|string */ protected function getManufacturerIdByName(string $manufacturerName): ?string { if (!empty($this->knownManufacturerIds[$manufacturerName])) { return $this->knownManufacturerIds[$manufacturerName]; } $manufacturerId = null; if (empty($manufacturerName)) { $manufacturerName = $this->defaultManufacturer; } $manufacturer = $this->shopwareRequest( 'GET', 'product-manufacturer?filter[product_manufacturer.name]=' . urlencode($manufacturerName) ); $manufacturerId = $manufacturer['data'][0]['id']; $this->knownManufacturerIds[$manufacturerName] = $manufacturerId; return $manufacturerId; } /** * @param float $taxRate * * @return string */ protected function getTaxIdByRate(float $taxRate): string{ if(empty($this->taxesInShop)){ $this->taxesInShop = $this->shopwareRequest('GET', 'tax'); } foreach ($this->taxesInShop['data'] as $taxData) { if (abs(($taxData['attributes']['taxRate']-$taxRate)) < 0.0001 ) { return $taxData['id']; } } return $this->taxesInShop['data'][0]['id']; } /** * @param array $internalArticleData * @param string $articleIdShopware * * @return array */ protected function mediaToExport($internalArticleData, $articleIdShopware) { $mediaToAdd = []; if (empty($internalArticleData['Dateien'])) { return $mediaToAdd; } $internalMediaIds = []; $searchdata = [ 'limit' => 1, 'filter' => [ [ 'field' => 'name', 'type' => 'equals', 'value' => $this->shopwareMediaFolder ] ] ]; $mediaFolderData = $this->shopwareRequest('POST', 'search/media-folder', $searchdata); if(empty($mediaFolderData['data'][0]['id'])){ $this->Shopware6ErrorLog('Kein Media Folder gefunden für: ', $this->shopwareMediaFolder); return []; } $mediaFolderId = $mediaFolderData['data'][0]['id']; foreach ($internalArticleData['Dateien'] as $internalFile) { $filename = explode('.', $internalFile['filename']); unset($filename[(!empty($filename)?count($filename):0) - 1]); $filename = $internalFile['id'].'_'.implode($filename); $extension = $internalFile['extension']; $imageTitle = (string)$internalFile['titel']; $imageAltText = (string)$internalFile['beschreibung']; $accessToken = $this->shopwareToken(); $searchdata = [ 'limit' => 5, 'filter' => [ [ 'field' => 'media.fileName', 'type' => 'equals', 'value' => $filename ] ] ]; $mediaData = $this->shopwareRequest('POST', 'search/media', $searchdata); if (!empty($mediaData['data'][0]['id'])) { $internalMediaIds[] = $mediaData['data'][0]['id']; if($mediaData['data'][0]['attributes']['title'] !== $imageTitle || $mediaData['data'][0]['attributes']['alt'] !== $imageAltText){ $this->setMediaTitleAndAltText($mediaData['data'][0]['id'], $imageTitle, $imageAltText); } continue; } $mediaData = $this->shopwareRequest('POST', 'media?_response=true', []); if(empty($mediaData['data']['id'])){ $this->Shopware6Log('Error when creating media for sku: ' . $internalArticleData['nummer'], ['mediaData' => $mediaData, 'title' => $imageTitle, 'text' => $imageAltText]); continue; } $mediaId = $mediaData['data']['id']; $this->setMediaTitleAndAltText($mediaId, $imageTitle, $imageAltText); $mediaAssociationData = [ [ 'action' => 'upsert', 'entity' => 'media', 'payload' => [ [ 'id' => $mediaId, 'mediaFolderId' => $mediaFolderId ] ] ] ]; $this->shopwareRequest('POST', '_action/sync?_response=true', $mediaAssociationData); $url = $this->ShopUrl . '_action/media/' . $mediaId . '/upload?extension=' . $extension . '&fileName=' . $filename; $ch = curl_init(); $setHeaders = [ 'Content-Type:image/' . $extension, 'Authorization:Bearer ' . $accessToken['token'] ]; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, base64_decode($internalFile['datei'])); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, $setHeaders); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_exec($ch); $internalMediaIds[] = $mediaId; } $existingMediaConnection = []; if (!empty($articleIdShopware)) { $existingMediaConnection = $this->shopwareRequest('GET', 'product/' . $articleIdShopware . '/media?limit=100'); foreach ($existingMediaConnection['data'] as $existingConnection) { if (!in_array($existingConnection['attributes']['mediaId'], $internalMediaIds, false)) { $this->shopwareRequest('DELETE', 'product/' . $articleIdShopware . '/media/' . $existingConnection['id']); } } } $alreadyAddedMediaIDs = []; if (!empty($existingMediaConnection)) { foreach ($existingMediaConnection['data'] as $existingConnection) { $alreadyAddedMediaIDs[$existingConnection['attributes']['mediaId']] = $existingConnection['id']; } } $position = 0; foreach ($internalMediaIds as $mediaId) { $mediaDataSet = [ 'mediaId' => $mediaId, 'position' => $position ]; if (array_key_exists($mediaId, $alreadyAddedMediaIDs)) { $mediaDataSet['id'] = $alreadyAddedMediaIDs[$mediaId]; } $mediaToAdd[] = $mediaDataSet; $position++; } return $mediaToAdd; } /** * @param string $mediaId * @param string $title * @param string $altText */ protected function setMediaTitleAndAltText(string $mediaId, string $title, string $altText): void { $this->shopwareRequest('PATCH', 'media/' . $mediaId, ['title' => $title, 'alt' => $altText ] ); } /** * @param array $articleInXentral * @param string $articleIdShopware */ protected function addCoverImage($articleInXentral, $articleIdShopware){ if(empty($articleIdShopware)){ return; } if(empty($articleInXentral['Dateien'])){ return; } $existingMediaConnection = $this->shopwareRequest('GET', 'product/' . $articleIdShopware . '/media?limit=100'); if(empty($existingMediaConnection['data'])){ return; } foreach ($articleInXentral['Dateien'] as $xentralFile) { $filename = explode('.', $xentralFile['filename']); unset($filename[(!empty($filename)?count($filename):0) - 1]); $filename = $xentralFile['id'].'_'.implode($filename); $searchdata = [ 'limit' => 5, 'filter' => [ [ 'field' => 'media.fileName', 'type' => 'equals', 'value' => $filename ] ] ]; $mediaData = $this->shopwareRequest('POST', 'search/media', $searchdata); $mediaId = $mediaData['data'][0]['id']; foreach ($existingMediaConnection['data'] as $mediaConnection){ if($mediaId === $mediaConnection['attributes']['mediaId']){ $this->shopwareRequest('PATCH', sprintf('product/%s?_response=true', $articleIdShopware),['coverId' => $mediaConnection['id']]); return; } } } } /** * @param array $articleInXentral * @param string $articleIdShopware * @return array */ protected function categoriesToExport($articleInXentral, $articleIdShopware) { $categoryName = $articleInXentral['kategoriename']; $categoryTree = $articleInXentral['kategorien']; $categoriesToAdd = []; if (empty($categoryName) && empty($categoryTree)) { return $categoriesToAdd; } $categoriesInXentral = []; if (!empty($categoryTree)) { $rootcategory = null; $categoryTreeid = []; foreach ($categoryTree as $categoryData) { $categoryData['shopwareparent'] = 0; if (!$categoryData['parent']) { $categoryData['shopwareid'] = $rootcategory; } $categoryTreeid[$categoryData['id']] = $categoryData; } foreach ($categoryTree as $categoryData) { $parentid = $rootcategory; if (!empty($categoryData['parent'])) { $parentid = $this->getCategoryParentId($categoryData, $categoryTreeid); } $searchdata = [ 'limit' => 25, 'filter' => [ [ 'field' => 'category.name', 'type' => 'equals', 'value' => $categoryData['name'] ] ] ]; if (!empty($parentid)) { $searchdata['filter'][] = [ 'field' => 'category.parentId', 'type' => 'equals', 'value' => $parentid ]; } $result = $this->shopwareRequest('POST', 'search/category', $searchdata); if (!empty($result['data'][0]['id'])) { $categoryTreeid[$categoryData['id']]['shopwareid'] = $result['data'][0]['id']; $categoriesInXentral[] = $result['data'][0]['id']; } } } else if (!empty($categoryName)) { $searchdata = [ 'limit' => 25, 'filter' => [ [ 'field' => 'category.name', 'type' => 'equals', 'value' => $categoryName ] ] ]; $result = $this->shopwareRequest('POST', 'search/category', $searchdata); if (!empty($result['data'][0]['id'])) { $categoriesInXentral[] = $result['data'][0]['id']; } } if (!empty($articleIdShopware)) { $existingCategories = $this->shopwareRequest('GET', 'product/' . $articleIdShopware . '/categories?limit=50'); foreach ($existingCategories['data'] as $existingCategory) { if (!in_array($existingCategory['id'], $categoriesInXentral, false)) { $this->shopwareRequest('DELETE', 'product/' . $articleIdShopware . '/categories/' . $existingCategory['id']); } } } foreach ($categoriesInXentral as $categoryId) { $categoriesToAdd[] = ['id' => $categoryId]; } return $categoriesToAdd; } /** * @param $categoryData * @param $categoryTreeId * @return string|null */ protected function getCategoryParentId($categoryData, &$categoryTreeId) { $parentId = $categoryTreeId[$categoryData['parent']]['shopwareid']; if (!empty($parentId)) { return $parentId; } $parentCategoryData = $this->app->DB->SelectRow("SELECT id,parent,bezeichnung AS name FROM artikelkategorien WHERE id<>'' AND id<>'0' AND id='" . $categoryData['parent'] . "' LIMIT 1"); if (empty($parentCategoryData)) { return null; } $searchData = [ 'limit' => 25, 'filter' => [ [ 'field' => 'category.name', 'type' => 'equals', 'value' => $parentCategoryData['name'] ] ] ]; $result = $this->shopwareRequest('POST', 'search/category', $searchData); if (count($result['data']) < 1) { return null; } if (count($result['data']) === 1) { $parentCategoryData['shopwareid'] = $result['data'][0]['id']; $categoryTreeId[$parentCategoryData['id']] = $parentCategoryData; return $result['data'][0]['id']; } $grandparentId = $this->getCategoryParentId($parentCategoryData, $categoryTreeId); $searchData = [ 'limit' => 25, 'filter' => [ [ 'field' => 'category.name', 'type' => 'equals', 'value' => $parentCategoryData['name'] ], [ 'field' => 'category.parentId', 'type' => 'equals', 'value' => $grandparentId ] ] ]; $result = $this->shopwareRequest('POST', 'search/category', $searchData); if (count($result['data']) === 1) { $parentCategoryData['shopwareid'] = $result['data'][0]['id']; $categoryTreeId[$parentCategoryData['id']] = $parentCategoryData; return $result['data'][0]['id']; } return null; } /** * @param string $propertyName * * @return string|null */ protected function getPropertyGroupId($propertyName): ?string { if(array_key_exists($propertyName, $this->knownPropertyGroupIds)){ return $this->knownPropertyGroupIds[$propertyName]; } $searchData = [ 'limit' => 25, 'filter' => [ [ 'field' => 'property_group.name', 'type' => 'equals', 'value' => $propertyName ] ] ]; $germanLanguageId = $this->getLanguageIdByCountryIso('DE'); $headerInformation = ['sw-language-id: ' . $germanLanguageId]; $propertyData = $this->shopwareRequest( 'POST', 'search/property-group', $searchData, $headerInformation); if (empty($propertyData['data'][0]['id'])) { return null; } $this->knownPropertyGroupIds[$propertyName] = $propertyData['data'][0]['id']; return $propertyData['data'][0]['id']; } /** * @param string $propertyName * @return null|string */ protected function createPropertyGroup($propertyName): ?string { $propertyGroupData = [ 'displayType' => 'text', 'name' => $propertyName, 'sortingType' => 'alphanumeric' ]; $propertyGroup = $this->shopwareRequest( 'POST', 'property-group?_response=true', $propertyGroupData); $this->knownPropertyGroupIds[$propertyName] = $propertyGroup['data']['id']; if (empty($propertyGroup['data']['id'])) { return null; } return $propertyGroup['data']['id']; } /** * @param string $propertyGroupId * @param string $propertyName * @param string $countryIsoCode */ protected function createTranslationForPropertyGroup($propertyGroupId, $propertyName, $countryIsoCode): void { $languageId = $this->getLanguageIdByCountryIso($countryIsoCode); if (empty($languageId)) { return; } $headerInformation = ['sw-language-id: ' . $languageId]; $translation = [ 'name' => $propertyName, ]; $this->shopwareRequest( 'PATCH', sprintf('property-group/%s', $propertyGroupId), $translation, $headerInformation); } /** * @param string $propertyGroupId * @param string $propertyOptionName * @param string $countryIsoCode * @return mixed|null */ protected function getPropertyOptionId($propertyGroupId, $propertyOptionName, $countryIsoCode = 'DE'): ?string { $searchData = [ 'limit' => 25, 'filter' => [ [ 'field' => 'property_group_option.name', 'type' => 'equals', 'value' => $propertyOptionName ] ] ]; $languageId = $this->getLanguageIdByCountryIso($countryIsoCode); $headerInformation = ['sw-language-id: ' . $languageId]; $optionData = $this->shopwareRequest( 'POST', 'search/property-group/' . $propertyGroupId . '/options', $searchData, $headerInformation); if (empty($optionData['data'][0]['id'])) { return null; } return $optionData['data'][0]['id']; } /** * @param string $propertyGroupId * @param string $propertyOptionName * @return null|string */ protected function createPropertyOption($propertyGroupId, $propertyOptionName): ?string { $propertyOptionData = [ 'name' => $propertyOptionName ]; $createdPropertyOption = $this->shopwareRequest( 'POST', 'property-group/' . $propertyGroupId . '/options?_response=true', $propertyOptionData); if (empty($createdPropertyOption['data']['id'])) { return null; } return $createdPropertyOption['data']['id']; } /** * @param string $optionId * @param string $optionName * @param string $countryIsoCode */ protected function createTranslationForPropertyOption($optionId, $optionName, $countryIsoCode): void { $languageId = $this->getLanguageIdByCountryIso($countryIsoCode); if (empty($languageId)) { return; } $headerInformation = ['sw-language-id: ' . $languageId]; $translation = [ 'name' => $optionName, ]; $this->shopwareRequest( 'PATCH', sprintf('property-group-option/%s', $optionId), $translation, $headerInformation); } /** * @param array $internalArticle * @param string $articleIdShopware * @return array */ protected function propertiesToExport($internalArticle, $articleIdShopware): array { $propertiesToAdd = $this->getPropertiesFromArticle($internalArticle); if (empty($propertiesToAdd)) { return []; } $assignedProperties = []; foreach ($propertiesToAdd as $propertyDefaultName => $countryIsoToPropertyTranslation) { if (empty($countryIsoToPropertyTranslation['DE'])) { continue; } $propertyGroupId = $this->getPropertyGroupId($propertyDefaultName); if (empty($propertyGroupId)) { $propertyGroupId = $this->createPropertyGroup($propertyDefaultName); } if (empty($propertyGroupId)) { $this->Shopware6Log('PropertyGroup kann nicht erstellt werden: ' . $propertyDefaultName); continue; } foreach ($countryIsoToPropertyTranslation as $countryIsoCode => $translation) { $this->createTranslationForPropertyGroup($propertyGroupId, $translation['name'], $countryIsoCode); } $optionId = $this->getPropertyOptionId($propertyGroupId, $countryIsoToPropertyTranslation['DE']['value'], 'DE'); if (empty($optionId)) { $optionId = $this->createPropertyOption($propertyGroupId, $countryIsoToPropertyTranslation['DE']['value']); } if (empty($optionId)) { $this->Shopware6Log('Option kann nicht erstellt werden: ' . $countryIsoToPropertyTranslation['DE']['value']); continue; } $assignedProperties[] = $optionId; foreach ($countryIsoToPropertyTranslation as $countryIsoCode => $translation) { $this->createTranslationForPropertyOption($optionId, $translation['value'], $countryIsoCode); } } if (!empty($articleIdShopware)) { $existingProperties = $this->shopwareRequest('GET', 'product/' . $articleIdShopware . '/properties?limit=100'); foreach ($existingProperties['data'] as $existingProperty) { if (!in_array($existingProperty['id'], $assignedProperties, false)) { $this->shopwareRequest('DELETE', 'product/' . $articleIdShopware . '/properties/' . $existingProperty['id']); } } } $propertiesToAdd = []; foreach ($assignedProperties as $propertyOptionId) { $propertiesToAdd[] = ['id' => $propertyOptionId]; } return $propertiesToAdd; } /** * @param string $name * @param string $value * @return bool */ protected function propertyMustBeIgnored(string $name, string $value): bool { return empty($value) || strpos($name, 'customField_') === 0 || stripos($name, 'shopware6_') !== false; } /** * @param array $internalArticleData * @return array */ protected function getPropertiesFromArticle($internalArticleData): array { //'Farbe' => [['DE' => ['name' => 'Farbe, 'value' => 'Gelb']], // ['EN' => ['name' => 'Colour, 'value' => 'Yellow']]] $propertiesToAdd = []; if (!empty($internalArticleData['eigenschaften'])) { foreach ($internalArticleData['eigenschaften'] as $property) { if ($this->propertyMustBeIgnored($property['name'], $property['values'])) { continue; } if (strpos($property['name'], 'property_') === 0) { $propertyName = substr($property['name'], 9); $propertiesToAdd[$propertyName]['DE'] = [ 'name' => $propertyName, 'value' => $property['values']]; continue; } if ($this->propertyOption === 'toProperties') { $propertiesToAdd[$property['name']]['DE'] = [ 'name' => $property['name'], 'value' => $property['values']]; } } } if (!empty($internalArticleData['eigenschaftenuebersetzungen'])) { foreach ($internalArticleData['eigenschaftenuebersetzungen'] as $translatedProperty) { if ($translatedProperty['language_to'] === 'EN') { $translatedProperty['language_to'] = 'GB'; } if ($this->propertyMustBeIgnored($translatedProperty['property_to'], $translatedProperty['property_value_to'])) { continue; } if (strpos($translatedProperty['property_to'], 'property_') === 0) { $propertiesToAdd[$translatedProperty['property_from']][$translatedProperty['language_to']] = [ 'name' => substr($translatedProperty['property_to'], 9), 'value' => $translatedProperty['property_value_to']]; continue; } if ($this->propertyOption === 'toProperties') { $propertiesToAdd[$translatedProperty['property_from']][$translatedProperty['language_to']] = [ 'name' => $translatedProperty['property_to'], 'value' => $translatedProperty['property_value_to']]; } } } if (!empty($internalArticleData['freifelder'])) { foreach ($internalArticleData['freifelder']['DE'] as $freeFieldKey => $freeFieldValue) { if ($this->propertyMustBeIgnored($freeFieldKey, $freeFieldValue)) { continue; } if (strpos($freeFieldKey, 'property_') === 0) { $propertyName = substr($freeFieldKey, 9); $propertiesToAdd[$propertyName]['DE'] = [ 'name' => $propertyName, 'value' => $freeFieldValue ]; continue; } if ($this->freeFieldOption === 'toProperties') { $propertiesToAdd[$freeFieldKey]['DE'] = [ 'name' => $freeFieldKey, 'value' => $freeFieldValue ]; } } foreach ($internalArticleData['freifelder'] as $languageIso => $freeFields) { if ($languageIso === 'DE') { continue; } if ($languageIso === 'EN') { $languageIso = 'GB'; } foreach ($freeFields as $freeFieldData) { if ($this->propertyMustBeIgnored($freeFieldData['mapping'], $freeFieldData['wert'])) { continue; } if (strpos($freeFieldData['mapping'], 'property_') === 0) { $propertyName = substr($freeFieldData['mapping'], 9); $propertiesToAdd[$propertyName][$languageIso] = [ 'name' => $propertyName, 'value' => $freeFieldData['wert'] ]; continue; } if ($this->freeFieldOption === 'toProperties') { $propertiesToAdd[$freeFieldData['mapping']][$languageIso] = [ 'name' => $freeFieldData['mapping'], 'value' => $freeFieldData['wert'] ]; } } } } return $propertiesToAdd; } /** * @param array $articleInXentral * @param string $articleIdShopware * * @return array */ protected function customFieldsToExport($articleInXentral, $articleIdShopware): array { $customFieldsToAdd = $this->getCustomFieldsFromArticle($articleInXentral); if (empty($customFieldsToAdd)) { return []; } $languageId = $this->getLanguageIdByCountryIso('DE'); $headerInformation = ['sw-language-id: ' . $languageId]; $customFields = []; if (!empty($articleIdShopware)) { $articleInfo = $this->shopwareRequest( 'GET', 'product/' . $articleIdShopware, [], $headerInformation); $customFields['DE'] = $articleInfo['data'][0]['attributes']['customFields']; if ($customFields === null) { $customFields = []; } } foreach ($customFieldsToAdd as $defaultFieldName => $countryIsoCodeToCustomFieldData) { $customFieldDefinition = $this->shopwareRequest( 'GET', sprintf('custom-field?filter[custom_field.name]=%s', $defaultFieldName), [], $headerInformation ); if (empty($customFieldDefinition)) { $this->Shopware6Log('Freifeld entspricht keinem shopware Freifeld', $defaultFieldName); continue; } foreach ($countryIsoCodeToCustomFieldData as $countryIsoCode => $customFieldData) { $name = $customFieldData['name']; $value = $customFieldData['value']; if ($value === '') { continue; } if($countryIsoCode === 'EN'){ $countryIsoCode = 'GB'; } $fieldType = $customFieldDefinition['data'][0]['attributes']['type']; $controlType = $customFieldDefinition['data'][0]['attributes']['config']['componentName']; switch ($fieldType) { case 'text': case 'html': if ($controlType === 'sw-media-field') { $this->Shopware6Log( 'Warnung: Freifelder vom Type "medium" werden nicht unterstützt.' ); } else { $customFields[$countryIsoCode][$name] = (string)$value; } break; case 'bool': $customFields[$countryIsoCode][$name] = filter_var($value, FILTER_VALIDATE_BOOLEAN); break; case 'int': $customFields[$countryIsoCode][$name] = (int)$value; break; case 'float': $customFields[$countryIsoCode][$name] = (float)$value; break; case 'select': $options = $customFieldDefinition['data'][0]['attributes']['config']['options']; $allowedValues = []; foreach ($options as $option) { $allowedValues[] = $option['value']; } if ($controlType === 'sw-single-select') { if (in_array($value, $allowedValues, true)) { $customFields[$countryIsoCode][$name] = $value; } else { $this->Shopware6Log( sprintf('Warnung: Freifeld "%s"="%s"; ungültiger Wert', $name, $value), ['allowed values' => $allowedValues] ); } } if ($controlType === 'sw-multi-select') { $value = explode(',', $value); foreach ($value as &$item) { $item = trim($item); } unset($item); if (array_intersect($value, $allowedValues) === $value) { $customFields[$countryIsoCode][$name] = $value; } else { $this->Shopware6Log( sprintf('Warnung: Freifeld "%s"; ungültiger Wert', $name), ['values' => $value, 'allowed values' => $allowedValues] ); } } break; default: $this->Shopware6Log( 'Warnung: Freifeld enthält falschen Typ.', ['freifeld' => $name, 'wert' => $value] ); continue 2; } } } return $customFields; } /** * @param string $name * @param string $value * @return bool */ protected function customFieldMustBeIgnored(string $name, string $value): bool { return empty($value) || strpos($name, 'property_') === 0 || stripos($name, 'shopware6_') !== false; } /** * @param array $articleInXentral * @return array */ protected function getCustomFieldsFromArticle($articleInXentral): array { $customFieldsToAdd = []; if (!empty($articleInXentral['eigenschaften'])) { foreach ($articleInXentral['eigenschaften'] as $propertyInXentral) { if ($this->customFieldMustBeIgnored($propertyInXentral['name'], $propertyInXentral['values'])) { continue; } if (strpos($propertyInXentral['name'], 'customField_') === 0) { $customFieldName = substr($propertyInXentral['name'], 12); $customFieldsToAdd[$customFieldName]['DE'] = [ 'name' => $customFieldName, 'value' => $propertyInXentral['values'] ]; continue; } if ($this->propertyOption === 'toCustomFields') { $customFieldsToAdd[$propertyInXentral['name']]['DE'] = [ 'name' => $propertyInXentral['name'], 'value' => $propertyInXentral['values'] ]; } } } if (!empty($articleInXentral['eigenschaftenuebersetzungen'])) { foreach ($articleInXentral['eigenschaftenuebersetzungen'] as $translatedProperty) { if ($this->customFieldMustBeIgnored($translatedProperty['property_to'], $translatedProperty['property_value_to'])) { continue; } if (strpos($translatedProperty['property_to'], 'customField_') === 0) { $customFieldName = substr($translatedProperty['property_to'], 12); $customFieldsToAdd[$customFieldName][$translatedProperty['language_to']] = [ 'name' => $customFieldName, 'value' => $translatedProperty['property_value_to'] ]; continue; } if ($this->propertyOption === 'toCustomFields') { $customFieldsToAdd[$translatedProperty['property_to']][$translatedProperty['language_to']] = [ 'name' => $translatedProperty['property_to'], 'value' => $translatedProperty['property_value_to'] ]; } } } if (!empty($articleInXentral['freifelder'])) { foreach ($articleInXentral['freifelder']['DE'] as $freeFieldKey => $freeFieldValue) { if ($this->customFieldMustBeIgnored($freeFieldKey, $freeFieldValue)) { continue; } if (strpos($freeFieldKey, 'customField_') === 0) { $customFieldName = substr($freeFieldKey, 12); $customFieldsToAdd[$customFieldName]['DE'] = [ 'name' => $customFieldName, 'value' => $freeFieldValue ]; continue; } if ($this->freeFieldOption === 'toCustomFields') { $customFieldsToAdd[$freeFieldKey]['DE'] = [ 'name' => $freeFieldKey, 'value' => $freeFieldValue ]; } } foreach ($articleInXentral['freifelder'] as $countryIsoCode => $freeFieldTranslations) { if ($countryIsoCode === 'DE') { continue; } foreach ($freeFieldTranslations as $freeFieldTranslation){ if ($this->customFieldMustBeIgnored($freeFieldTranslation['mapping'], $freeFieldTranslation['wert'])) { continue; } if ($countryIsoCode === 'EN') { $countryIsoCode = 'GB'; } if (strpos($freeFieldTranslation['mapping'], 'customField_') === 0) { $customFieldName = substr($freeFieldTranslation['mapping'], 12); $customFieldsToAdd[$customFieldName][$countryIsoCode] = [ 'name' => $customFieldName, 'value' => $freeFieldTranslation['wert'] ]; continue; } if ($this->freeFieldOption === 'toCustomFields') { $customFieldsToAdd[$freeFieldTranslation['mapping']][$countryIsoCode] = [ 'name' => $freeFieldTranslation['mapping'], 'value' => $freeFieldTranslation['wert'] ]; } } } } return $customFieldsToAdd; } /** * @param array $articleInXentral * @param int $articleIdShopware * * @return array */ protected function crosssellingToExport($articleInXentral, $articleIdShopware){ if (empty($articleInXentral['crosssellingartikel'])) { return []; } $crosssellingArticles = []; foreach ($articleInXentral['crosssellingartikel'] as $crosssellingArticle){ $type = 'Ähnlich'; if($crosssellingArticle['art'] == 2){ $type = 'Zubehör'; } $crosssellingArticles[$type][] = $crosssellingArticle['nummer']; } $crossselingInformation = []; foreach ($crosssellingArticles as $type => $articles){ if(!empty($articleIdShopware)){ $existingCrossSellings = $this->shopwareRequest('GET', sprintf('product/%s/cross-sellings/', $articleIdShopware)); if(!empty($existingCrossSellings['data'])){ foreach ($existingCrossSellings['data'] as $existingCrossSelling){ if($existingCrossSelling['attributes']['name'] === $type){ $this->shopwareRequest('DELETE', sprintf('product/%s/cross-sellings/%s/', $articleIdShopware, $existingCrossSelling['id'])); } } } } $crosselingToAdd = []; foreach ($articles as $articleNumber) { $articleInfo = $this->shopwareRequest( 'GET', sprintf('product?filter[product.productNumber]=%s', $articleNumber) ); if(empty($articleInfo['data'][0]['id'])){ continue; } $crosselingToAdd[] = $articleInfo['data'][0]['id']; } if(empty($crosselingToAdd)){ continue; } $crossselingInformationForType = [ 'active' => true, 'name' => $type, 'assignedProducts' => [], 'type' => 'productList', 'sortBy' => 'name', 'limit' => 24, 'position' => 1 ]; $position = 1; foreach ($crosselingToAdd as $articleId){ $crossselingInformationForType['assignedProducts'][] = [ 'productId' => $articleId, 'position' => $position, ]; $position++; } $crossselingInformation[] = $crossselingInformationForType; } return $crossselingInformation; } /** * @param string $unitShortCode * * @return string */ protected function unitToAdd(string $unitShortCode): string{ $searchData = [ 'limit' => 25, 'source' => [ 'id' ], 'filter' => [ [ 'field' => 'unit.shortCode', 'type' => 'equals', 'value' => $unitShortCode ] ] ]; $unitInShopware = $this->shopwareRequest( 'POST', 'search/unit', $searchData); if(!empty($unitInShopware['data'][0]['id'])){ return $unitInShopware['data'][0]['id']; } $query = sprintf("SELECT `internebemerkung` FROM `artikeleinheit` WHERE `einheit_de` = '%s' LIMIT 1", $unitShortCode); $unitName = $this->app->DB->Select($query); if(empty($unitName)){ $unitName = $unitShortCode; } $unitInformation = [ 'name' => $unitName, 'shortCode' => $unitShortCode ]; $result = $this->shopwareRequest('POST', 'unit?_response=true', $unitInformation); if(empty($result['data']['id'])){ return ''; } return $result['data']['id']; } /** * @param array $internArticle * @param int $articleIdShopware * * @return array */ protected function systemFieldsToExport($internArticle, $articleIdShopware): array { $internalSpecialFields = []; foreach ($internArticle['freifelder']['DE'] as $freeFieldName => $freeFieldValue) { if (stripos($freeFieldName, 'shopware6_') !== false) { $internalSpecialFields[$freeFieldName] = $freeFieldValue; } } foreach ($internArticle['eigenschaften'] as $property) { if (stripos($property['name'], 'shopware6_') !== false) { $internalSpecialFields[$property['name']] = $property['values']; } } $systemFields = []; foreach ($internalSpecialFields as $fieldName => $fieldValue) { switch (strtolower($fieldName)) { case 'shopware6_sales_channel': $systemFields['visibilities'] = $this->modifySalesChannel(explode(',', $fieldValue), $articleIdShopware); break; case 'shopware6_purchase_unit': $systemFields['purchaseUnit'] = (float)str_replace(',', '.', $fieldValue); break; case 'shopware6_reference_unit': $systemFields['referenceUnit'] = (float)str_replace(',', '.', $fieldValue); break; case 'shopware6_unit': $systemFields['unitId'] = $this->unitToAdd($fieldValue); break; case 'shopware6_pack_unit': $systemFields['packUnit'] = (string)$fieldValue; break; case 'shopware6_restock_time': $systemFields['restockTime'] = (int)$fieldValue; break; case 'shopware6_pack_unit_plural': $systemFields['packUnitPlural'] = (string)$fieldValue; break; } } return $systemFields; } /** * @param array $salesChannelNames * @param string $articleIdInShopware * * @return array */ protected function modifySalesChannel($salesChannelNames, $articleIdInShopware) { $salesChannelInXentralIds = []; foreach ($salesChannelNames as $salesChannelName) { $salesChannelInfo = $this->shopwareRequest('GET', sprintf('sales-channel?filter[sales_channel.name]=%s', urlencode(trim($salesChannelName))) ); if (!empty($salesChannelInfo['data'][0]['id'])) { $salesChannelInXentralIds[] = $salesChannelInfo['data'][0]['id']; } } $existingVisibilities = $this->shopwareRequest( 'GET', sprintf('product/%s/visibilities', $articleIdInShopware) ); $existingSalesChannelIds = []; if (!empty($existingVisibilities['data'])) { foreach ($existingVisibilities['data'] as $visibility) { $existingSalesChannelIds[$visibility['id']] = $visibility['attributes']['salesChannelId']; } } foreach ($existingSalesChannelIds as $associationId => $existingSalesChannelId){ if (!in_array($existingSalesChannelId, $salesChannelInXentralIds,true)) { $this->shopwareRequest('DELETE', sprintf('product/%s/visibilities/%s/', $articleIdInShopware, $associationId)); } } $salesChannelsToAdd = []; foreach ($salesChannelInXentralIds as $salesChannelInXentralId){ if (!in_array($salesChannelInXentralId, $existingSalesChannelIds,true)) { $salesChannelsToAdd[] = $salesChannelInXentralId; } } $visibilities = []; foreach ($salesChannelsToAdd as $salesChannelIdToAdd) { $visibilities[] = [ 'salesChannelId' => $salesChannelIdToAdd, 'visibility' => 30 ]; } return $visibilities; } /** * @param string $isoCode * * @return string */ protected function findCurrencyId($isoCode) { $this->requestCurrencyMappingLazy(); if (isset($this->currencyMapping[strtoupper($isoCode)])) { return $this->currencyMapping[strtoupper($isoCode)]; } $this->Shopware6Log( sprintf('Warnung: Kein Mapping für Waehrung "%s" gefunden.', $isoCode), $this->currencyMapping ); return null; } /** * request currency mapping only once */ protected function requestCurrencyMappingLazy() { if ($this->currencyMapping !== null) { return; } $currencies = $this->shopwareRequest('GET', 'currency'); if (!isset($currencies['data'])) { $this->Shopware6Log('Kann Währungsmapping nicht abrufen', $currencies); } foreach ($currencies['data'] as $currency) { $isoCode = strtoupper($currency['attributes']['isoCode']); $this->currencyMapping[$isoCode] = $currency['id']; } } /** * @param array $internalArticleData * @param string $articleIdInShopware * @return bool */ public function exportSeoUrls(array $internalArticleData, string $articleIdInShopware): bool { if (empty($articleIdInShopware)) { return false; } $preparedSeoInformation = []; foreach ($internalArticleData['freifelder'] as $countryIsoCode => $freeFieldInformation) { if($countryIsoCode === 'EN'){ $countryIsoCode = 'GB'; } if($countryIsoCode === 'DE'){ foreach ($freeFieldInformation as $freeFieldName => $freeFieldValue) { if (stripos($freeFieldName, 'shopware6_seo_url') !== false) { $preparedSeoInformation[$countryIsoCode][$freeFieldName] = $freeFieldValue; } } }else{ foreach ($freeFieldInformation as $freeFieldData) { if (stripos($freeFieldData['mapping'], 'shopware6_seo_url') !== false) { $preparedSeoInformation[$countryIsoCode][$freeFieldData['mapping']] = $freeFieldData['wert']; } } } } foreach ($internalArticleData['eigenschaften'] as $property) { if (stripos($property['name'], 'shopware6_seo_url') !== false) { $preparedSeoInformation['DE'][$property['name']] = $property['values']; } } foreach ($internalArticleData['eigenschaftenuebersetzungen'] as $propertyTranslation) { if($propertyTranslation['language_to'] === 'EN'){ $propertyTranslation['language_to'] = 'GB'; } if (stripos($propertyTranslation['property_to'], 'shopware6_seo_url') !== false) { $preparedSeoInformation[$propertyTranslation['language_to']][$propertyTranslation['property_to']] = $propertyTranslation['property_value_to']; } } $specificSalesChannelSeoUrls = []; $defaultSeoUrls = []; foreach ($preparedSeoInformation as $countryIsoCode => $channelAssociations) { foreach ($channelAssociations as $fieldName => $fieldValue){ if(strtolower($fieldName) === 'shopware6_seo_url'){ $defaultSeoUrls[$countryIsoCode] = $fieldValue; }else{ $seoInformation = explode('|', $fieldName); $specificSalesChannelSeoUrls[$countryIsoCode][array_pop($seoInformation)] = $fieldValue; } } } if (empty($specificSalesChannelSeoUrls) && empty($defaultSeoUrls)) { return false; } $salesChannelsIdToName = []; $salesChannels = $this->shopwareRequest('GET','sales-channel'); foreach ($salesChannels['data'] as $salesChannel) { $salesChannelsIdToName[$salesChannel['id']] = $salesChannel['attributes']['name']; } foreach ($preparedSeoInformation as $countryIsoCode => $x){ $languageId = $this->getLanguageIdByCountryIso($countryIsoCode); if (empty($languageId)) { $this->Shopware6Log('Language Id not found for country: ' . $countryIsoCode); continue; } $headerInformation = ['sw-language-id: ' . $languageId]; foreach ($salesChannelsIdToName as $salesChannelId => $salesChannelName) { $seoUrlToUse = $defaultSeoUrls[$countryIsoCode]; if (!empty($specificSalesChannelSeoUrls[$countryIsoCode][$salesChannelName])) { $seoUrlToUse = $specificSalesChannelSeoUrls[$countryIsoCode][$salesChannelsIdToName[$salesChannelName]]; } if (empty($seoUrlToUse)) { continue; } $seoDataToSend = [ 'seoPathInfo' => $seoUrlToUse, '_isNew' => true, 'isModified' => true, 'isCanonical' => true, 'isDeleted' => false, 'routeName' => 'frontend.detail.page', 'foreignKey' => $articleIdInShopware, 'pathInfo' => '/detail/'.$articleIdInShopware, 'languageId' => $languageId, 'salesChannelId' => $salesChannelId]; $this->shopwareRequest('PATCH', '_action/seo-url/canonical', $seoDataToSend, $headerInformation); } } return true; } /** * @param array $article * @param string $articleIdShopware * @param string $currencyId * * @return bool */ protected function exportVariants($article, $articleIdShopware, $currencyId): bool { $languageId = $this->getLanguageIdByCountryIso('DE'); if (empty($languageId)) { return false; } if (empty($article['matrix_varianten']) || empty($articleIdShopware)) { return false; } $headerInformation = ['sw-language-id: ' . $languageId]; $internalGroupPropertiesToShopwareId = []; foreach ($article['matrix_varianten']['gruppen'] as $propertyGroupName => $internalPropertyGroupValues) { $propertyGroupId = $this->getPropertyGroupId($propertyGroupName); if (empty($propertyGroupId)) { $propertyGroupId = $this->createPropertyGroup($propertyGroupName); } if (empty($propertyGroupId)) { $this->Shopware6Log('PropertyGroup kann nicht erstellt werden: ' . $propertyGroupName); return false; } if (!empty($article['matrix_varianten']['texte'])) { $this->createTranslationForPropertyGroup($propertyGroupId, $propertyGroupName, 'DE'); foreach ($article['matrix_varianten']['texte']['gruppen'] as $countryIsoCode => $matrixGroupTranslation) { if ($countryIsoCode === 'EN') { $countryIsoCode = 'GB'; } $this->createTranslationForPropertyGroup($propertyGroupId, $matrixGroupTranslation[$propertyGroupName], $countryIsoCode); } } $shopwarePropertyGroupOptions = $this->shopwareRequest( 'GET', 'property-group/' . $propertyGroupId . '/options?limit=100', $headerInformation); foreach ($shopwarePropertyGroupOptions['data'] as $shopwarePropertyGroupOption) { $propertyValue = $shopwarePropertyGroupOption['attributes']['name']; $internalGroupPropertiesToShopwareId[$propertyGroupName][$propertyValue] = $shopwarePropertyGroupOption['id']; } foreach ($internalPropertyGroupValues as $internalPropertyGroupValue => $valueNotNeeded) { if (!array_key_exists($internalPropertyGroupValue, $internalGroupPropertiesToShopwareId[$propertyGroupName] ?? [])) { $newOptionData = [ 'name' => (string)$internalPropertyGroupValue ]; $optionData = $this->shopwareRequest( 'POST', 'property-group/' . $propertyGroupId . '/options?_response=true', $newOptionData); $internalGroupPropertiesToShopwareId[$propertyGroupName][$internalPropertyGroupValue] = $optionData['data']['id']; } } if (!empty($article['matrix_varianten']['texte'])) { foreach ($internalPropertyGroupValues as $optionValue => $valueNotNeeded) { $optionId = $internalGroupPropertiesToShopwareId[$propertyGroupName][$optionValue]; $this->createTranslationForPropertyOption( $optionId, $optionValue, 'DE'); foreach ($article['matrix_varianten']['texte']['werte'] as $countryIsoCode => $matrixOptionTranslations) { if ($countryIsoCode === 'EN') { $countryIsoCode = 'GB'; } if (array_key_exists($optionValue, $matrixOptionTranslations)) { $this->createTranslationForPropertyOption( $optionId, $matrixOptionTranslations[$optionValue], $countryIsoCode); } } } } } $existingCombinations = $this->shopwareRequest( 'GET', '_action/product/' . $articleIdShopware . '/combinations'); $existingCombinationsByNumber = []; foreach ($existingCombinations as $combinationId => $combinationInfo) { $existingCombinationsByNumber[$combinationInfo['productNumber']] = [ 'id' => $combinationId, 'options' => [], ]; foreach ($combinationInfo['options'] as $combinationOption) { $existingCombinationsByNumber[$combinationInfo['productNumber']]['options'][$combinationOption] = $combinationOption; } } foreach ($article['artikel_varianten'] as $variant) { $internalVariantMatrixData = $article['matrix_varianten']['artikel'][$variant['artikel']]; $productNumber = $internalVariantMatrixData[0]['nummer']; $name = $variant['name_de']; $stock = $variant['lag']; $ean = $variant['ean']; $weight = (float)$variant['gewicht']; $pseudoPrice = $variant['pseudopreis']; if (empty($pseudoPrice)) { $pseudoPrice = 0; } if (!empty($variant['pseudolager'])) { $stock = $variant['pseudolager']; } $active = true; if (!empty($variant['inaktiv'])) { $active = false; } $isCloseOut = false; if (!empty($variant['restmenge'])) { $isCloseOut = true; } if ($variant['umsatzsteuer'] == 'normal' && !empty($this->normalTaxId)) $taxId = $this->normalTaxId; else if ($variant['umsatzsteuer'] == 'ermaessigt' && !empty($this->reducedTaxId)) $taxId = $this->reducedTaxId; else $taxId = $this->getTaxIdByRate($variant['steuersatz']); $variantProductData = [ 'active' => $active, 'isCloseout' => $isCloseOut, 'name' => $name, 'description' => null, 'weight' => null, 'price' => [ [ 'currencyId' => $currencyId, 'gross' => $variant['bruttopreis'], 'net' => $variant['preis'], 'linked' => true, 'listPrice' => [ 'currencyId' => $currencyId, 'gross' => $pseudoPrice, 'linked' => true, 'net' => $pseudoPrice / (1 + $variant['steuersatz'] / 100) ] ] ], 'stock' => (int)$stock, 'ean' => null, 'taxId' => $taxId, ]; if(!empty($weight)){ $variantProductData['weight'] = $weight; } if(!empty($ean)){ $variantProductData['ean'] = $ean; } if (!empty($variant['uebersicht_de'])) { $variantProductData['description'] = $variant['uebersicht_de']; } $renewVariant = false; $options = []; foreach ($internalVariantMatrixData as $expression) { if (!in_array( $internalGroupPropertiesToShopwareId[$expression['name']][$expression['values']], $existingCombinationsByNumber[$productNumber]['options'] ?? [], false)) { $renewVariant = true; } else { unset($existingCombinationsByNumber[$productNumber]['options'][$internalGroupPropertiesToShopwareId[$expression['name']][$expression['values']]]); } $options[] = ['id' => $internalGroupPropertiesToShopwareId[$expression['name']][$expression['values']]]; } if (!empty($existingCombinationsByNumber[$productNumber]['options'])) { $renewVariant = true; } $variantImageData = [ 'Dateien' => [] ]; $variantProductId = ''; if (!empty($existingCombinationsByNumber[$productNumber]['id']) && !$renewVariant) { $variantProductId = $existingCombinationsByNumber[$productNumber]['id']; } if (!empty($variant['Dateien']['id'])) { foreach ($variant['Dateien']['id'] as $index => $fileId) { $variantImageData['Dateien'][] = [ 'filename' => $variant['Dateien']['filename'][$index], 'extension' => $variant['Dateien']['extension'][$index], 'datei' => $variant['Dateien']['datei'][$index], 'beschreibung' => $variant['Dateien']['beschreibung'][$index], 'titel' => $variant['Dateien']['titel'][$index], 'id' => $fileId, ]; } } $mediaToAdd = $this->mediaToExport($variantImageData, $variantProductId); $variantProductData['media'] = $mediaToAdd; if ($renewVariant) { if (!empty($existingCombinationsByNumber[$productNumber]['id'])) { $this->shopwareRequest('DELETE', 'product/' . $existingCombinationsByNumber[$productNumber]['id']); } $variantProductData['productNumber'] = $productNumber; $variantProductData['parentId'] = $articleIdShopware; $variantProductData['options'] = $options; $result = $this->shopwareRequest('POST', 'product?_response=true', $variantProductData); $variantProductId = $result['data']['id']; } else { $variantProductId = $existingCombinationsByNumber[$productNumber]['id']; $this->shopwareRequest('PATCH', 'product/' . $variantProductId, $variantProductData); } $defaultPrices = $this->getPricesFromArray($variant['staffelpreise_standard'] ?? []); $groupPrices = $this->getPricesFromArray($variant['staffelpreise_gruppen'] ?? []); $this->deleteOldBulkPrices($variantProductId); if (!empty($defaultPrices)) { foreach ($defaultPrices as $priceData) { $this->exportBulkPriceForGroup($variantProductId, $this->defaultRuleName, $priceData); } } if (!empty($groupPrices)) { foreach ($groupPrices as $priceData) { $this->exportBulkPriceForGroup($variantProductId, $priceData->getGroupName(), $priceData); } } $this->addCoverImage($variantImageData, $variantProductId); } $existingConfigurations = $this->shopwareRequest( 'GET', 'product/' . $articleIdShopware . '/configuratorSettings'); $optionIdsToAdd = []; foreach ($article['artikel_varianten'] as $variant) { foreach ($article['matrix_varianten']['artikel'][$variant['artikel']] as $matrixInfo) { $configurationExists = false; foreach ($existingConfigurations['data'] as $configuration) { if ($configuration['attributes']['optionId'] === $internalGroupPropertiesToShopwareId[$matrixInfo['name']][$matrixInfo['values']]) { $configurationExists = true; break; } } if (!$configurationExists) { $optionIdsToAdd[] = $internalGroupPropertiesToShopwareId[$matrixInfo['name']][$matrixInfo['values']]; } } } if (!empty($optionIdsToAdd)) { $optionIdsToAdd = array_flip(array_flip($optionIdsToAdd)); $configurationData = [ 'configuratorSettings' => [] ]; foreach ($optionIdsToAdd as $id) { $configurationData['configuratorSettings'][] = ['optionId' => $id]; } $this->shopwareRequest( 'PATCH', sprintf('product/%s', $articleIdShopware), $configurationData ); $existingConfigurations = $this->shopwareRequest( 'GET', 'product/' . $articleIdShopware . '/configuratorSettings'); $optionsToSort = []; foreach ($article['artikel_varianten'] as $variant) { foreach ($article['matrix_varianten']['artikel'][$variant['artikel']] as $matrixInfo) { foreach ($existingConfigurations['data'] as $configuration) { if ($configuration['attributes']['optionId'] === $internalGroupPropertiesToShopwareId[$matrixInfo['name']][$matrixInfo['values']]) { $optionsToSort[] = $configuration['id']; break; } } } } if (!empty($optionsToSort)) { $optionsToSort = array_flip(array_flip($optionsToSort)); $configurationData = [ 'configuratorSettings' => [] ]; $position = 1; foreach ($optionsToSort as $id) { $configurationData['configuratorSettings'][] = [ 'id' => $id, 'position' => $position]; $position++; } $this->shopwareRequest( 'PATCH', sprintf('product/%s', $articleIdShopware), $configurationData ); } } return true; } /** * @param $priceArray * @return PriceData[] */ protected function getPricesFromArray($priceArray): array{ $c = count($priceArray); $result = []; for ($i = 0; $i < $c; $i++) { $end = null; if ($i+1 < $c && ($priceArray[$i+1]['gruppeextern'] ?? '') == ($priceArray[$i]['gruppeextern'] ?? '')) $end = (int)$priceArray[$i+1]['ab_menge'] - 1; $result[] = new PriceData( (int)$priceArray[$i]['ab_menge'], (float)$priceArray[$i]['preis'], (float)$priceArray[$i]['bruttopreis'], $priceArray[$i]['waehrung'], $priceArray[$i]['gruppeextern'] ?? '', $end); } return $result; } /** * delete all old price entries for a product * * @param string $productId */ protected function deleteOldBulkPrices($productId) { //TODO Instead of deleting all old prices we should rather check first whether they are still in order $oldPrices = $this->shopwareRequest( 'GET', sprintf('product-price?filter[product_price.productId]=%s', $productId) ); if (is_array($oldPrices)) { foreach ($oldPrices['data'] as $deletePrice) { $this->shopwareRequest('DELETE', 'product-price/' . $deletePrice['id']); } } else { $this->Shopware6Log('Fehler: Alte Preise wurden nicht gelöscht', $productId); } } /** * @return int */ public function getOrderSearchLimit(): int { if(in_array($this->orderSearchLimit, ['50', '75', '100'])) { return (int)$this->orderSearchLimit; } return 25; } /** * @return int */ public function ImportGetAuftraegeAnzahl() { $order = null; $dataToGet = $this->CatchRemoteCommand('data'); if (empty($this->statesToFetch)) { return false; } $ordersToProcess = $this->getOrdersToProcess($this->getOrderSearchLimit()); return (!empty(count($ordersToProcess['data'])?count($ordersToProcess['data']):0)); } /** * @param string $parameter1 * @param string $parameter2 */ public function Shopware6ErrorLog($parameter1, $parameter2 = '') { $this->app->DB->Insert( sprintf( "INSERT INTO `shopexport_log` (shopid, typ, parameter1, parameter2, bearbeiter, zeitstempel) VALUES (%d, 'fehler', '%s','%s','%s',NOW())", $this->shopid, $this->app->DB->real_escape_string($parameter1), $this->app->DB->real_escape_string($parameter2), $this->app->DB->real_escape_string($this->app->User->GetName()) ) ); } /** * @param array $stateMachinesIds * @return array */ protected function getTransactionStateIdsToFetch($stateMachinesIds): array { $transactionStateIdsToFetch = []; if (!empty($this->transactionStatesToFetch)) { $transactionStatesToFetch = explode(';', $this->transactionStatesToFetch); foreach ($transactionStatesToFetch as $transactionStateToFetch) { $stateInformation = $this->shopwareRequest('GET', 'state-machine-state?filter[technicalName]=' . trim($transactionStateToFetch) . '&filter[stateMachineId]=' . $stateMachinesIds['order_transaction.state']); if (empty($stateInformation['data'])) { $this->Shopware6ErrorLog('Zahlungsstatus für Abholung nicht gefunden', $transactionStateToFetch); return false; } foreach ($stateInformation['data'] as $state) { $transactionStateIdsToFetch[] = $state['id']; } } } return $transactionStateIdsToFetch; } /** * @param int $limit * * @return mixed */ protected function getOrdersToProcess(int $limit) { $searchData = [ 'limit' => $limit, 'includes' => [ 'order' => ['id'] ], 'sort' => [ [ 'field' => 'order.createdAt', 'direction' => 'DESC' ] ], 'filter' => [] ]; $searchData['filter'][] = [ 'field' => 'stateMachineState.technicalName', 'type' => 'equalsAny', 'value' => explode(';', $this->statesToFetch) ]; if (!empty($this->deliveryStatesToFetch)) { $searchData['filter'][] = [ 'field' => 'deliveries.stateMachineState.technicalName', 'type' => 'equalsAny', 'value' => explode(';', $this->deliveryStatesToFetch) ]; } if (!empty($this->transactionStatesToFetch)) { $searchData['filter'][] = [ 'field' => 'transactions.stateMachineState.technicalName', 'type' => 'equalsAny', 'value' => explode(';', $this->transactionStatesToFetch) ]; } if (!empty($this->salesChannelToFetch)) { $searchData['filter'][] = [ 'field' => 'order.salesChannelId', 'type' => 'equals', 'value' => $this->salesChannelToFetch ]; } return $this->shopwareRequest('POST', 'search/order', $searchData); } /** * @return int|mixed */ public function ImportGetAuftrag() { $voucherArticleId = $this->app->DB->Select("SELECT s.artikelrabatt FROM `shopexport` AS `s` WHERE s.id='$this->shopid' LIMIT 1"); $voucherArticleNumber = $this->app->DB->Select("SELECT a.nummer FROM `artikel` AS `a` WHERE a.id='$voucherArticleId' LIMIT 1"); $dataToGet = $this->CatchRemoteCommand('data'); if (empty($this->statesToFetch)) { return false; } $expectOrderArray = !empty($dataToGet['anzgleichzeitig']) && (int)$dataToGet['anzgleichzeitig'] > 1; $expectNumber = !empty($dataToGet['nummer']); $order = null; if($expectNumber) { $order = $this->shopwareRequest('GET', 'order/' . $dataToGet['nummer'] . '?associations[currency][]'); if(empty($order['data'])) { return false; } $ordersToProcess = ['data' => [ ['id' => $dataToGet['nummer']] ]]; $orderIncludedData = $order['included']; $order = $order['data']; } elseif(!$expectOrderArray) { $ordersToProcess = $this->getOrdersToProcess(1); } elseif(!$expectNumber) { $ordersToProcess = $this->getOrdersToProcess($this->getOrderSearchLimit()); } if (empty($ordersToProcess['data'])) { return false; } $fetchedOrders = []; if (isset($ordersToFetch['data']['id']) && !isset($ordersToFetch['data'][0])) { $ordersToFetch['data'] = [$ordersToFetch['data']]; } foreach ($ordersToProcess['data'] as $currentlyOpenOrder) { $orderIdToFetch = $currentlyOpenOrder['id']; if (empty($dataToGet['nummer']) || empty($order)) { $order = $this->shopwareRequest('GET', 'order/' . $orderIdToFetch.'?associations[currency][]'); $orderIncludedData = $order['included']; $order = $order['data']; } $cart = []; try { $timestamp = date_create_from_format('Y-m-d\TH:i:s+', $order['attributes']['createdAt']); $cart['zeitstempel'] = $timestamp->format('Y-m-d H:i:s'); } catch (Exception $ex) { } $cart['auftrag'] = $order['id']; $cart['subshop'] = $order['attributes']['salesChannelId']; $cart['order'] = $order; $cart['onlinebestellnummer'] = $order['attributes']['orderNumber']; $cart['gesamtsumme'] = $order['attributes']['amountTotal']; $cart['versandkostenbrutto'] = $order['attributes']['shippingTotal']; $cart['bestelldatum'] = substr($order['attributes']['orderDate'], 0, 10); if (!empty($order['attributes']['customerComment'])) { $cart['freitext'] = $order['attributes']['customerComment']; } foreach ($orderIncludedData as $includedDataSet){ if($includedDataSet['type'] === 'currency'){ $cart['waehrung'] = $includedDataSet['attributes']['isoCode']; } } $deliveryInfo = $this->shopwareRequest('GET', 'order/' . $order['id'] . '/deliveries'); $shippingMethod = $this->shopwareRequest('GET', 'order-delivery/' . $deliveryInfo['data'][0]['id'] . '/shipping-method'); $order['shippingMethod'] = $shippingMethod; $cart['lieferung'] = $shippingMethod['data'][0]['attributes']['name']; $customer = $this->shopwareRequest('GET', 'order/' . $order['id'] . '/order-customer'); $order['customer'] = $customer; $cart['email'] = $customer['data']['0']['attributes']['email']; $addresses = $this->shopwareRequest('GET', 'order/' . $order['id'] . '/addresses?associations[salutation][]&associations[country][]'); $order['addresses'] = $addresses; $deliveryCountryId = ''; $billingCountryId = ''; $billingSalutationId = ''; foreach ($addresses['data'] as $address) { if ($address['id'] === $order['attributes']['billingAddressId']) { if (!empty($address['attributes']['vatId'])) { $cart['ustid'] = $address['attributes']['vatId']; } $cart['name'] = $address['attributes']['firstName'] . ' ' . $address['attributes']['lastName']; if (!empty($address['attributes']['company'])) { $cart['ansprechpartner'] = $cart['name']; $cart['name'] = $address['attributes']['company']; } $cart['strasse'] = $address['attributes']['street']; $cart['abteilung'] = $address['attributes']['department']; $cart['adresszusatz'] = trim($address['attributes']['additionalAddressLine1'].' '. $address['attributes']['additionalAddressLine2']); $cart['telefon'] = $address['attributes']['phoneNumber']; $cart['plz'] = $address['attributes']['zipcode']; $cart['ort'] = $address['attributes']['city']; $billingCountryId = $address['attributes']['countryId']; $billingSalutationId = $address['attributes']['salutationId']; } if ($address['id'] !== $order['attributes']['billingAddressId']) { $cart['abweichendelieferadresse'] = 1; if (!empty($address['attributes']['vatId'])) { $cart['lieferadresse_ustid'] = $address['attributes']['vatId']; } $cart['lieferadresse_name'] = $address['attributes']['firstName'] . ' ' . $address['attributes']['lastName']; if (!empty($address['attributes']['company'])) { $cart['lieferadresse_ansprechpartner'] = $cart['lieferadresse_name']; $cart['lieferadresse_name'] = $address['attributes']['company']; } $cart['lieferadresse_strasse'] = $address['attributes']['street']; $cart['lieferadresse_abteilung'] = $address['attributes']['department']; $cart['lieferadresse_adresszusatz'] = trim($address['attributes']['additionalAddressLine1'].' '. $address['attributes']['additionalAddressLine2']); $cart['lieferadresse_plz'] = $address['attributes']['zipcode']; $cart['lieferadresse_ort'] = $address['attributes']['city']; $deliveryCountryId = $address['attributes']['countryId']; } } $anrede = 'herr'; $land = 'DE'; $lieferadresseLand = 'DE'; foreach ($addresses['included'] as $includedInfo) { if ($includedInfo['id'] === $billingCountryId) { $land = $includedInfo['attributes']['iso']; } if ($includedInfo['id'] === $deliveryCountryId) { $lieferadresseLand = $includedInfo['attributes']['iso']; } if ($includedInfo['id'] === $billingSalutationId) { $salutation = $includedInfo['attributes']['salutationKey']; if ($salutation === 'ms' || $salutation === 'mrs') { $anrede = 'frau'; } } } $cart['anrede'] = $anrede; $cart['land'] = $land; if (!empty($cart['abweichendelieferadresse'])) { $cart['lieferadresse_land'] = $lieferadresseLand; } $transactionData = $this->shopwareRequest('GET', 'order/' . $order['id'] . '/transactions'); $cart['transacion_data'] = $transactionData; if (!empty($transactionData['data'][0]['attributes']['customFields']['swag_paypal_pui_payment_instruction']['reference_number'])) { $cart['transaktionsnummer'] = $transactionData['data'][0]['attributes']['customFields']['swag_paypal_pui_payment_instruction']['reference_number']; } if (empty($cart['transaktionsnummer'] && !empty($transactionData['data'][0]['attributes']['customFields']['swag_paypal_order_id']))) { $cart['transaktionsnummer'] = (string)$transactionData['data'][0]['attributes']['customFields']['swag_paypal_order_id']; } if (empty($cart['transaktionsnummer'] && !empty($transactionData['data'][0]['attributes']['customFields']['swag_paypal_transaction_id']))) { $livePayPalData = $this->shopwareRequest('GET', 'paypal/payment-details/' . $order['id'] . '/' . $transactionData['data'][0]['attributes']['customFields']['swag_paypal_transaction_id']); if (!empty($livePayPalData['transactions'])) { foreach ($livePayPalData['transactions'] as $payPalData) { foreach ($payPalData['related_resources'] as $ressources) { if ($ressources['sale']['state'] === 'completed') { $cart['transaktionsnummer'] = $ressources['sale']['id']; break 2; } } } } } if( empty($cart['transaktionsnummer']) && isset($transactionData['data'][0]['attributes']['customFields']['stripe_payment_context']['payment']['payment_intent_id']) ){ $cart['transaktionsnummer'] = $transactionData['data'][0]['attributes']['customFields']['stripe_payment_context']['payment']['payment_intent_id']; } $paymentMethodId = $transactionData['data'][0]['attributes']['paymentMethodId']; $paymentMethod = $this->shopwareRequest('GET', 'payment-method/' . $paymentMethodId); $cart['zahlungsweise'] = $paymentMethod['data']['attributes']['name']; $taxedCountry = $land; if($this->taxationByDestinationCountry){ $taxedCountry = $lieferadresseLand; } if($order['attributes']['amountTotal'] === $order['attributes']['amountNet']){ if($this->app->erp->IstEU($taxedCountry)){ $cart['ust_befreit'] = 1; }elseif($this->app->erp->Export($taxedCountry)){ $cart['ust_befreit'] = 2; }else{ $cart['ust_befreit'] = 3; } } $lineItems = $this->shopwareRequest('GET', 'order/' . $order['id'] . '/line-items'); $order['lineItems'] = $lineItems; $cart['articlelist'] = []; $taxRate = 0; foreach ($lineItems['data'] as $lineItem) { if ($lineItem['attributes']['price']['calculatedTaxes'][0]['taxRate'] > $taxRate) { $taxRate = $lineItem['attributes']['price']['calculatedTaxes'][0]['taxRate']; } } $orderPriceType = 'price'; if(in_array($order['attributes']['taxStatus'], ['net', 'tax-free'])) { $orderPriceType = 'price_netto'; $cart['versandkostennetto'] = $cart['versandkostenbrutto']; unset($cart['versandkostenbrutto']); } foreach ($lineItems['data'] as $lineItem) { $productPriceType = $orderPriceType; if(empty($lineItem['attributes']['price']['calculatedTaxes'][0]['taxRate'])){ $productPriceType = 'price_netto'; } $articleId = null; if($lineItem['attributes']['price']['unitPrice'] < 0) { $articleId = $voucherArticleNumber; } elseif(isset($lineItem['attributes']['payload']['productNumber'])){ $articleId = $lineItem['attributes']['payload']['productNumber']; } $product = [ 'articleid' => $articleId, 'name' => $lineItem['attributes']['label'], 'quantity' => $lineItem['attributes']['quantity'], $productPriceType => $lineItem['attributes']['price']['unitPrice'], 'steuersatz' => $lineItem['attributes']['price']['calculatedTaxes'][0]['taxRate'], ]; $this->parseBogxData($lineItem, $product); $cart['articlelist'][] = $product; } $cart['order'] = $order; $fetchedOrders[] = [ 'id' => $cart['auftrag'], 'sessionid' => '', 'logdatei' => '', 'warenkorb' => base64_encode(serialize($cart)), 'warenkorbjson' => base64_encode(json_encode($cart)), ]; $this->Shopware6Log('Ergebnis: Auftrag', $order); $this->Shopware6Log('Ergebnis: Adresse', $addresses); $this->Shopware6Log('Ergebnis: Positionen', $lineItems); } return $fetchedOrders; } /** * @return void */ public function ImportDeleteAuftrag() { $tmp = $this->CatchRemoteCommand('data'); $auftrag = $tmp['auftrag']; $this->shopwareRequest('POST', '_action/order/'.$auftrag.'/state/process'); $this->addCustomFieldToOrder((string)$auftrag); } /** * @return void */ public function ImportUpdateAuftrag() { /** @var OrderStatusUpdateRequest $data */ $data = $this->CatchRemoteCommand('data'); if ($data->orderStatus !== OrderStatus::Completed) return; $auftrag = $data->shopOrderId; $this->shopwareRequest('POST', '_action/order/'.$auftrag.'/state/complete'); $deliveries = $this->shopwareRequest('GET', 'order/'.$auftrag.'/deliveries'); $deliveryId = $deliveries['data'][0]['id']; if(!empty($deliveryId)){ $this->shopwareRequest('POST', '_action/order_delivery/'.$deliveryId.'/state/ship'); $deliveryData = [ 'trackingCodes' => [$data->getTrackingNumberList()] ]; $this->shopwareRequest('PATCH', 'order-delivery/'.$deliveryId,$deliveryData); } $this->sendInvoce($auftrag); $this->addCustomFieldToOrder($auftrag); if(empty($data->orderId)) { return; } $this->updateStorageForOrderIntId($data->orderId); } public function ImportStorniereAuftrag() { $tmp = $this->CatchRemoteCommand('data'); $auftrag = $tmp['auftrag']; $this->shopwareRequest('POST', '_action/order/'.$auftrag.'/state/cancel'); $this->addCustomFieldToOrder((string)$auftrag); } /** * @param string $extOrderId */ protected function sendInvoce($extOrderId) { $order = $this->app->DB->SelectRow( sprintf( "SELECT `rechnungid`, `id` FROM `auftrag` WHERE shopextid='%s'", $extOrderId ) ); $invoiceId = 0; if (!empty($order['rechnungid'])) { $invoiceId = $order['rechnungid']; $sql = sprintf("SELECT projekt, belegnr FROM rechnung WHERE id='%s'", $invoiceId); $invoiceData = $this->app->DB->SelectRow($sql); } if (empty($invoiceId) && !empty($order['id'])) { $invoiceData = $this->app->DB->SelectRow( sprintf( "SELECT `id`, `projekt`, `belegnr` FROM `rechnung` WHERE `auftragid` = %d AND `status` <> 'storniert' AND `status` <> 'angelegt' LIMIT 1", $order['id'] ) ); if (!empty($invoiceData)) { $invoiceId = $invoiceData['id']; } } if (!empty($invoiceData['belegnr'])) { $projekt = $invoiceData['projekt']; if (class_exists('RechnungPDFCustom')) { $Brief = new RechnungPDFCustom($this->app, $projekt); } else { $Brief = new RechnungPDF($this->app, $projekt); } $Brief->GetRechnung($invoiceId); $filePath = $Brief->displayTMP(true); $documentNumber = $invoiceData['belegnr']; $invoiceDocumentData = [ 'config' => [ 'custom' => [ 'invoiceNumber' => $documentNumber, ], 'documentComment' => 'Aus Xentral heraus erstellte Rechnung', 'documentNumber' => $documentNumber, ], 'referenced_document_id' => null, 'static' => true ]; $documentData = $this->shopwareRequest('POST', '_action/order/' . $extOrderId . '/document/invoice', $invoiceDocumentData); $documentId = $documentData['documentId']; $accessToken = $this->shopwareToken(); $url = $this->ShopUrl . '_action/document/' . $documentId . '/upload?_response=true&extension=pdf&fileName=' . $documentNumber; $ch = curl_init(); $setHeaders = [ 'Content-Type:application/pdf', 'Authorization:Bearer ' . $accessToken['token'] ]; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($filePath)); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, $setHeaders); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = json_decode(curl_exec($ch), true); curl_close($ch); if (!empty($response['errors'])) { $this->Shopware6Log( 'Fehler bei Rechnugnsübertragung für ' . $documentNumber, $response['errors'] ); } } } /** * @return string */ public function ImportAuth() { $tokeninfo = $this->shopwareToken(); if (!$tokeninfo['success']) { return 'failed: ' . $tokeninfo['message']; } if($this->data === 'info'){ $salesChannelsInShopware = $this->client->getAllSalesChannels(); $salesChannelsToShow = ['subshops' => []]; foreach ($salesChannelsInShopware['data'] as $salesChannelInShopware){ $salesChannelsToShow['subshops'][] = [ 'id'=>$salesChannelInShopware['id'], 'name'=>$salesChannelInShopware['name'], 'aktiv'=>$salesChannelInShopware['active'] ]; } return $salesChannelsToShow; } return 'success'; } /** * Build category tree as displayed in article info * May be useful for setting category in the future * but probably obsolete * * @param string $categoryName * @param array $categoryTree * * @return array */ protected function appendCategoryTree($categoryName, $categoryTree = []) { $shopwareCategory = $this->shopwareRequest( 'GET', 'category?filter[category.name]=' . urlencode($categoryName) ); if (!isset($shopwareCategory['data'][0]['id'])) { return $categoryTree; } $categoryInfo = $shopwareCategory['data'][0]['attributes']; $categories[] = [(int)$categoryInfo['level'], $shopwareCategory['data'][0]['id']]; $path = $categoryInfo['path']; if (!empty($path)) { $pathArray = explode('|', $path); foreach ($pathArray as $nodeId) { if ($nodeId === '') { continue; } $nodeCategory = $this->shopwareRequest('GET', 'category/' . $nodeId); if (isset($nodeCategory['data']['id'])) { $categories[] = [(int)$nodeCategory['data']['attributes']['level'], $nodeId]; unset($nodeCategory); } } } foreach ($categories as $category) { $level = $category[0]; if (!isset($categoryTree[$level])) { $categoryTree[$level] = []; } if (!in_array($category, $categoryTree[$level], true)) { $categoryTree[$level][] = $category[1]; } } ksort($categoryTree); return $categoryTree; } /** * @param array $postData * * @return array */ public function updatePostDataForAssistent($postData) { if(!empty($this->ShopUrl)) { $postData['shopwareUrl'] = $this->ShopUrl; } return $postData; } /** * @param array $shopArr * @param array $postData * * @return array */ public function updateShopexportArr($shopArr, $postData) { $shopArr['stornoabgleich'] = 1; $shopArr['demomodus'] = 0; return $shopArr; } /** * @return JsonResponse|null */ public function AuthByAssistent() { $shopwareUrl = $this->app->Secure->GetPOST('shopwareUrl'); $shopwareUserName = $this->app->Secure->GetPOST('shopwareUserName'); $shopwarePassword = $this->app->Secure->GetPOST('shopwarePassword'); $step = (int)$this->app->Secure->GetPOST('step'); if($step <= 1){ if(empty($shopwareUrl)){ return new JsonResponse(['error' => 'Bitte die URL des Shops angeben.'], JsonResponse::HTTP_BAD_REQUEST); } if(empty($shopwareUserName)){ return new JsonResponse(['error' => 'Bitte den Benutzernamen angeben'], JsonResponse::HTTP_BAD_REQUEST); } if(empty($shopwarePassword)){ return new JsonResponse(['error' => 'Bitte das Passwort angeben'], JsonResponse::HTTP_BAD_REQUEST); } $this->UserName = $shopwareUserName; $this->Password = $shopwarePassword; $shopwareUrl = rtrim($shopwareUrl, '/') . '/'; $testUrls = []; $hasNoHttp = strpos($shopwareUrl,'http') !== 0; if(substr($shopwareUrl, -5) !== '/api/') { if($hasNoHttp) { $testUrls[] = 'https://'.$shopwareUrl.'api/'; $testUrls[] = 'http://'.$shopwareUrl.'api/'; } $testUrls[] = $shopwareUrl.'api/'; } elseif($hasNoHttp) { $testUrls[] = 'https://'.$shopwareUrl; $testUrls[] = 'http://'.$shopwareUrl; } else { $testUrls[] = $shopwareUrl; } foreach($testUrls as $testUrl) { $this->ShopUrl = $testUrl; $tokeninfo = $this->shopwareToken(); if(!empty($tokeninfo['success'])) { break; } } if(!$tokeninfo['success']){ return new JsonResponse(['error' => $tokeninfo['message']], JsonResponse::HTTP_BAD_REQUEST); } } return null; } /** * @return string */ public function getClickByClickHeadline() { return 'Bitte im Shopware Backend einen eigenen Benutzer für Xentral anlegen und diese Zugangsdaten hier eintragen.'; } /** * @return array */ public function getStructureDataForClickByClickSave() { return [ 'shopwareAllowCreateManufacturer' => 1, ]; } /** * @return array[] */ public function getCreateForm() { return [ [ 'id' => 0, 'name' => 'urls', 'inputs' => [ [ 'label' => 'URL des Shops', 'type' => 'text', 'name' => 'shopwareUrl', 'validation' => true, ], ], ], [ 'id' => 1, 'name' => 'username', 'inputs' => [ [ 'label' => 'Benutzername aus Shopware', 'type' => 'text', 'name' => 'shopwareUserName', 'validation' => true, ], ], ], [ 'id' => 2, 'name' => 'password', 'inputs' => [ [ 'label' => 'Passwort aus Shopware', 'type' => 'password', 'name' => 'shopwarePassword', 'validation' => true, ], ], ], ]; } public function getBoosterHeadline(): string { return 'Shopware 6 Business Booster App'; } public function getBoosterSubHeadline(): string { return 'Bitte gehe auf Shopware 6 und installiere dort das Plugin Xentral Business Booster App. Dort kann man sich dann mit ein paar Klicks mit Xentral verbinden.'; } /** * @param int $intOrderId * * @return array */ protected function getArticleShopLinks(int $intOrderId): array { return $this->app->DB->SelectPairs( "SELECT DISTINCT ao.artikel, a.nummer FROM `auftrag_position` AS `ap` INNER JOIN `auftrag` AS `ab` ON ap.auftrag = ab.id INNER JOIN `artikel` AS `a` ON ap.artikel = a.id INNER JOIN `artikel_onlineshops` AS `ao` ON ab.shop = ao.shop AND a.id = ao.artikel WHERE ab.id = {$intOrderId} AND ao.aktiv = 1" ); } /** * @param array $articleIds */ protected function updateArticleCacheToSync(array $articleIds): void { if(empty($articleIds)) { return; } $articleIdsString = implode(', ', $articleIds); $this->app->DB->Update( "UPDATE `artikel` SET `laststorage_changed` = DATE_ADD(NOW(), INTERVAL 1 SECOND) WHERE `id` IN ({$articleIdsString})" ); } /** * @param array $articleIds */ protected function updateArticleOnlineShopCache(array $articleIds): void { if(empty($articleIds)) { return; } $articleIdsString = implode(', ', $articleIds); $this->app->DB->Update( "UPDATE `artikel_onlineshops` SET `storage_cache` = -999, `pseudostorage_cache` = -999 WHERE `artikel` IN ({$articleIdsString}) AND `aktiv` = 1 AND `shop` = {$this->shopid}" ); } /** * @param int $intOrderId */ protected function updateStorageForOrderIntId(int $intOrderId): void { $articles = $this->getArticleShopLinks($intOrderId); if(empty($articles)) { return; } $articleIds = array_keys($articles); $this->updateArticleCacheToSync($articleIds); $this->updateArticleOnlineShopCache($articleIds); $isStorageSyncCronjobActive = (int)$this->app->DB->Select( "SELECT COUNT(`id`) FROM `prozessstarter` WHERE `aktiv` = 1 AND `parameter` = 'lagerzahlen'" ) > 0; if(!$isStorageSyncCronjobActive) { return; } foreach($articleIds as $articleId) { try { $this->app->erp->LagerSync($articleId, false, [$this->shopid]); } catch (Exception $e) { $articleNumber = $articles[$articleId]; $this->Shopware6ErrorLog('LagerSync konnte nicht ausgeführt werden', $articleNumber); } } $this->updateArticleCacheToSync($articleIds); } protected function parseBogxData(array $lineItem, array &$product) : void { if (!isset($lineItem['attributes']['payload']['bogxProductConfigurator'])) return; $bogxdata = $lineItem['attributes']['payload']['bogxProductConfigurator']; $textlines = []; if (isset($bogxdata['ordercode'])) $textlines[] = "Order-Code: ${bogxdata['ordercode']}"; foreach ($bogxdata['optionsGroups'] as $bogxposition) { $dt = $bogxposition['datatype']; if ($dt == 'quantity_total') continue; if (is_array($bogxposition['valueID']) && is_array($bogxposition['title'])) { foreach ($bogxposition['valueID'] as $valueID) { $bogxTitle = $bogxposition['title'][$valueID]; if ($dt == 'checkbox_quantity') $bogxTitle = $bogxposition['label'][$valueID]." ".$bogxTitle; $textlines[] = sprintf("%s: %s", $bogxposition['groupname'], $bogxTitle); } } else { if (is_array($bogxposition['title'])) $bogxTitle = join(' ', $bogxposition['title']); else $bogxTitle = $bogxposition['title']; $textlines[] = sprintf("%s: %s", $bogxposition['groupname'], $bogxTitle); } } if (!empty($bogxdata['shippingtime'])) { $textlines[] = $bogxdata['shippingtime']; } $product['options'] .= join("\n", $textlines); $product['price'] = $bogxdata['unitySurcharge']; $product['price_netto'] = $bogxdata['unitySurchargeNetto']; $product['quantity'] = $bogxdata['totalQuantity']; } }