diff --git a/README.md b/README.md
index 4558e28..cc00116 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,23 @@
# Shopware Ergonode Integration
+## Our modification devEcommerce
+- Change php-graphql-client (not compatible with psr/http-message^2.0)
+- **Variants**:
+ - Fix adding new variants (wrong grouping)
+ - Full inheritance for variants
+- **Attributes**:
+ - Hide attributes with hide metadata value
+ - Disable default filterable attribute for new attributes
+- Add filterProductsNotMatchingSkuPattern (filter not products and parts)
+- Languages - updates only active languages
+- Category: inject the main category as tree root
+- Images: leave the original name as a prefix
+- **Change default**
+ - DEFAULT_STOCK_VALUE to "0"
+ - DEFAULT_GROSS_PRICE to "99999"
+ - minPurchaseMapping to "1"
+- Use only selected brands (temporary)
+
## Description
This plugin synchronizes data from Ergonode to Shopware. It takes advantage of Ergonode's GraphQL API and utilizes
@@ -119,4 +137,4 @@ Available cache pools:
|------------------|-------------|
| 6.6 from 6.6.0.0 | Version 3.x |
| 6.5 from 6.5.0.0 | Version 2.x |
-| 6.4 from 6.4.0.0 | Version 1.x |
\ No newline at end of file
+| 6.4 from 6.4.0.0 | Version 1.x |
diff --git a/composer.json b/composer.json
index 942d6c0..143b8d1 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "ergonode/integration-shopware",
"description": "Shopware Ergonode Integration",
- "version": "3.0.1",
+ "version": "3.0.10-patch",
"type": "shopware-platform-plugin",
"license": "proprietary",
"authors": [
@@ -24,9 +24,9 @@
"require": {
"php": ">= 8.2",
"ext-json": "*",
- "shopware/administration": "6.6.*",
- "shopware/core": "6.6.*",
- "gmostafa/php-graphql-client": "^1.13"
+ "shopware/administration": ">=6.6.0 <6.7.0",
+ "shopware/core": ">=6.6.0 <6.7.0",
+ "carnage/php-graphql-client": "^1.14"
},
"require-dev": {
"phpunit/phpunit": "~9.5.17",
diff --git a/src/DTO/ProductShopwareData.php b/src/DTO/ProductShopwareData.php
index 77a5946..0f5630a 100644
--- a/src/DTO/ProductShopwareData.php
+++ b/src/DTO/ProductShopwareData.php
@@ -103,7 +103,7 @@ public function setMedia(array $payloads): void
$this->data['media'] = $payloads;
}
- public function setTax(string $taxId): void
+ public function setTax(?string $taxId): void // devEcommerce change
{
$this->data['taxId'] = $taxId;
}
@@ -148,7 +148,7 @@ public function setProductNumber(string $sku): void
$this->data['productNumber'] = $sku;
}
- public function setPrice(array $pricePayload): void
+ public function setPrice(?array $pricePayload): void // devEcommerce change
{
$this->data['price'] = $pricePayload;
}
@@ -170,7 +170,12 @@ public function addChild(ProductShopwareData $productShopwareData): void
public function setDisplayParent(bool $display = true): void
{
- $this->data['displayParent'] = $display;
+ $this->data['variantListingConfig'] = [ // devEcommerce change
+ 'extensions' => [],
+ 'displayParent' => $display,
+ 'mainVariantId' => null,
+ 'configuratorGroupConfig' => null
+ ];
}
public function addConfigrationSettings(array $configurationSettings): void
diff --git a/src/Manager/FileManager.php b/src/Manager/FileManager.php
index 0f59de1..618fcd1 100644
--- a/src/Manager/FileManager.php
+++ b/src/Manager/FileManager.php
@@ -86,6 +86,7 @@ private function buildFileName(ProductMultimediaTranslation $image): string
'url' => $image->getUrl(),
];
- return md5(json_encode($imageData));
+ //return md5(json_encode($imageData));
+ return pathinfo($imageData['name'], PATHINFO_FILENAME) . '_' . pathinfo($imageData['url'], PATHINFO_FILENAME); // devEcommerce change
}
}
diff --git a/src/Persistor/ProductPersistor.php b/src/Persistor/ProductPersistor.php
index aee79d9..78eed92 100644
--- a/src/Persistor/ProductPersistor.php
+++ b/src/Persistor/ProductPersistor.php
@@ -97,6 +97,8 @@ public function persist(array $productListData, Context $context): array
$productListData = $this->filterMainProducts($productListData);
+ $productListData = $this->filterProductsNotMatchingSkuPattern($productListData); // devEcommerce change
+
foreach ($productListData as $productData) {
$mainProductPayload = $this->getProductPayload($productData['node'] ?? [], $context);
if (
@@ -306,6 +308,20 @@ private function filterMainProducts(array $productListData): array
);
}
+ // start devEcommerce change
+ private function filterProductsNotMatchingSkuPattern(array $productListData): array
+ {
+ return array_values(
+ array_filter(
+ $productListData,
+ static function (array $productData): bool {
+ return preg_match('/^V[0-9]+$/', $productData['node']['sku']) === 1; // todo move to configuration
+ }
+ )
+ );
+ }
+ // stop devEcommerce change
+
private function extractSkus(array $productListData, bool $onlyVariants = false): array
{
foreach ($productListData as $productData) {
diff --git a/src/Persistor/PropertyGroupPersistor.php b/src/Persistor/PropertyGroupPersistor.php
index df84c87..4c4462c 100644
--- a/src/Persistor/PropertyGroupPersistor.php
+++ b/src/Persistor/PropertyGroupPersistor.php
@@ -9,6 +9,8 @@
use Ergonode\IntegrationShopware\Processor\Attribute\AttributeCustomProcessorResolver;
use Ergonode\IntegrationShopware\Provider\PropertyGroupProvider;
use Ergonode\IntegrationShopware\Transformer\PropertyGroupTransformer;
+use Ergonode\IntegrationShopware\Util\YesNo;
+use Ergonode\IntegrationShopware\Service\ConfigService;
use Shopware\Core\Content\Property\PropertyGroupDefinition;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
@@ -33,7 +35,8 @@ public function __construct(
EntityRepository $propertyGroupOptionRepository,
PropertyGroupTransformer $propertyGroupTransformer,
PropertyGroupProvider $propertyGroupProvider,
- AttributeCustomProcessorResolver $attributeCustomProcessorResolver
+ AttributeCustomProcessorResolver $attributeCustomProcessorResolver,
+ private ConfigService $configService
) {
$this->propertyGroupRepository = $propertyGroupRepository;
$this->propertyGroupOptionRepository = $propertyGroupOptionRepository;
@@ -57,6 +60,19 @@ public function persistStream(AttributeStreamResultsProxy $attributes, Context $
continue;
}
+ // start devEcommerce change
+ $skipMetaHideAttribute = false;
+ foreach ($node['metadata'] as $metadata) {
+ if (($metadata['key'] === $this->configService->getHideMetaDataKey() && YesNo::cast($metadata['value']))) {
+ $skipMetaHideAttribute = true;
+ break;
+ }
+ }
+ if ($skipMetaHideAttribute) {
+ continue;
+ }
+ // end devEcommerce change
+
$propertyGroup = $this->propertyGroupProvider->getPropertyGroupByMapping($code, $context);
$dto = new PropertyGroupTransformationDTO($node);
diff --git a/src/Processor/Attribute/ManufacturerAttributeProcessor.php b/src/Processor/Attribute/ManufacturerAttributeProcessor.php
index 0edb59f..4af75ad 100644
--- a/src/Processor/Attribute/ManufacturerAttributeProcessor.php
+++ b/src/Processor/Attribute/ManufacturerAttributeProcessor.php
@@ -59,6 +59,10 @@ public function process(array $node, Context $context): void
$code = $option['code'];
$manufacturerEntity = $this->getExistingManufacturerEntity($code, $context);
+ if(!in_array($code, ['gymtek', 'outtec', 'kedica', 'queenfit', 'xride', 'luverno', 'stars' ])) { // devEcommerce change
+ continue;
+ }
+
$translations = [];
foreach ($option['name'] as $nameRow) {
$translations[IsoCodeConverter::ergonodeToShopwareIso($nameRow['language'])] = [
diff --git a/src/Processor/CategoryTreeSyncProcessor.php b/src/Processor/CategoryTreeSyncProcessor.php
index f75be94..046cfa2 100644
--- a/src/Processor/CategoryTreeSyncProcessor.php
+++ b/src/Processor/CategoryTreeSyncProcessor.php
@@ -113,7 +113,7 @@ public function processStream(
$stopwatch->start('process');
try {
- $leafEdges = $edge['node']['categoryTreeLeafList']['edges'] ?? [];
+ $leafEdges = $this->normalizeCategoryTreeEdges($edge);
$primaryKeys = $this->categoryTreePersistor->persistLeaves($leafEdges, $currentTreeCode, $context);
$this->categoryTreePersistor->markCategoriesAsActive($primaryKeys);
@@ -178,6 +178,42 @@ public function processStream(
return $counter;
}
+ /**
+ * devEcommerce change
+ */
+ private function normalizeCategoryTreeEdges(mixed $edge): mixed
+ {
+ $leafEdges = $edge['node']['categoryTreeLeafList']['edges'] ?? [];
+ $categoryTreeCode = $edge['node']['code'];
+ $categoryTreeNames = $edge['node']['name'];
+ $mainEdgeKeys = array_keys(array_filter(
+ $leafEdges,
+ fn($row) => empty($row['node']['parentCategory'])
+ ));
+
+ if(!empty($mainEdgeKeys)) {
+ $firstCategory = [
+ 'node' => [
+ 'category' => [
+ 'code' => $categoryTreeCode,
+ 'name' => $categoryTreeNames,
+ ],
+ 'parentCategory' => null,
+ ]
+ ];
+ array_unshift($leafEdges,$firstCategory);
+
+ foreach ($mainEdgeKeys as $key) {
+ $leafEdges[$key+1]['node']['parentCategory'] = ['code' => $categoryTreeCode];
+ }
+ }
+
+ file_put_contents('../custom/plugins/ergo_graph.log', print_r($mainEdgeKeys, true) . "\n-----\n" ,FILE_APPEND);
+ file_put_contents('../custom/plugins/ergo_graph.log', print_r($leafEdges, true) . "\n-----\n" ,FILE_APPEND);
+
+ return $leafEdges;
+ }
+
/**
* Gets ID of last existing top level category
*
diff --git a/src/Provider/LanguageProvider.php b/src/Provider/LanguageProvider.php
index e89c717..78ab97b 100644
--- a/src/Provider/LanguageProvider.php
+++ b/src/Provider/LanguageProvider.php
@@ -9,6 +9,7 @@
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
+use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\Language\LanguageEntity;
class LanguageProvider
@@ -58,6 +59,8 @@ public function getActiveLocaleCodes(Context $context): array
{
$criteria = new Criteria();
$criteria->addAssociation('locale');
+ $criteria->addAssociation('swagLanguagePackLanguage'); // devEcommerce change
+ $criteria->addFilter(new EqualsFilter('swagLanguagePackLanguage.salesChannelActive', true)); // devEcommerce change
/** @var LanguageEntity[] $languages */
$languages = $this->languageRepository->search($criteria, $context);
diff --git a/src/QueryBuilder/AttributeQueryBuilder.php b/src/QueryBuilder/AttributeQueryBuilder.php
index 3378e2f..0c048d1 100644
--- a/src/QueryBuilder/AttributeQueryBuilder.php
+++ b/src/QueryBuilder/AttributeQueryBuilder.php
@@ -44,6 +44,11 @@ protected function getAttributeSelectionSet(): array
->setSelectionSet([
'code',
'scope',
+ (new Query('metadata')) // devEcommerce change
+ ->setSelectionSet([
+ 'key',
+ 'value',
+ ]),
(new Query('name'))
->setSelectionSet([
'language',
diff --git a/src/QueryBuilder/CategoryQueryBuilder.php b/src/QueryBuilder/CategoryQueryBuilder.php
index 50df7a2..bb59d10 100644
--- a/src/QueryBuilder/CategoryQueryBuilder.php
+++ b/src/QueryBuilder/CategoryQueryBuilder.php
@@ -154,6 +154,11 @@ public function buildTreeStream(
(new Query('node'))
->setSelectionSet([
'code',
+ (new Query('name')) // devEcommerce change
+ ->setSelectionSet([
+ 'value',
+ 'language',
+ ]),
(new Query('categoryTreeLeafList'))
->setArguments($categoryLeafArguments)
->setSelectionSet([
diff --git a/src/Resources/config/config.xml b/src/Resources/config/config.xml
index 15c18b5..50c0d40 100644
--- a/src/Resources/config/config.xml
+++ b/src/Resources/config/config.xml
@@ -26,6 +26,17 @@
+
+ Fields configuration*
+
+
+ hideMetaDataKey
+ ukryty_w_sklepie
+
+ Enter the exact name of the metadata key used in Ergonode. Example: "ukryty_w_sklepie". When this key is set on an attribute, that attribute will be hidden in the storefront. This value is inherited by all sales channels.
+
+
+
Fields mapping
Fields mapping
@@ -80,8 +91,8 @@
- Product sync
- Product sync
+ Template sync
+ Template sync
templateLayoutMapping
diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml
index f2827b3..1a72217 100644
--- a/src/Resources/config/services.yml
+++ b/src/Resources/config/services.yml
@@ -215,6 +215,7 @@ services:
- '@Ergonode\IntegrationShopware\Transformer\ProductScaleUnitTransformer'
- '@Ergonode\IntegrationShopware\Transformer\ProductMinMaxQuantityTransformer'
- '@Ergonode\IntegrationShopware\Transformer\ProductLayoutTransformer'
+ - '@Ergonode\IntegrationShopware\Transformer\ProductCustomBusinessTransformer'
Ergonode\IntegrationShopware\Transformer\CategoryAttributesTransformerChain:
arguments:
diff --git a/src/Service/ConfigService.php b/src/Service/ConfigService.php
index 2ed5804..0908977 100644
--- a/src/Service/ConfigService.php
+++ b/src/Service/ConfigService.php
@@ -186,4 +186,11 @@ public function getTemplateLayoutMapping(): array
}, $value)
);
}
+
+ // start devEcommerce change
+ public function getHideMetaDataKey(): string
+ {
+ return $this->configService->getString(self::CONFIG_NAMESPACE . 'hideMetaDataKey');
+ }
+ // end devEcommerce change
}
diff --git a/src/Transformer/ProductCustomBusinessTransformer.php b/src/Transformer/ProductCustomBusinessTransformer.php
new file mode 100644
index 0000000..d97df7d
--- /dev/null
+++ b/src/Transformer/ProductCustomBusinessTransformer.php
@@ -0,0 +1,43 @@
+configService = $configService;
+ }
+
+ public function transform(ProductTransformationDTO $productData, Context $context): ProductTransformationDTO {
+ $swData = $productData->getShopwareData();
+ $ergonodeData = $productData->getErgonodeData();
+ $sku = $ergonodeData->getSku();
+
+ if (preg_match('/^V[0-9]+_.+$/', $sku)) { // handle for good parts
+ // inherit from a parent
+ $swData->setName('');
+ $swData->setTax(null);
+ $swData->setPrice(null);
+ $swData->setData('isCloseout', null);
+ $swData->setData('shippingFree', null);
+ $swData->setData('markAsTopseller', null);
+ $swData->setData('minPurchase', null);
+ $swData->setData('purchaseSteps', null);
+ $swData->setData('active', null);
+ $swData->setProperties([]);
+ }
+
+ $productData->setShopwareData($swData);
+
+ return $productData;
+ }
+
+}
diff --git a/src/Transformer/ProductDefaultValuesTransformer.php b/src/Transformer/ProductDefaultValuesTransformer.php
index 3d02e53..9ceb3dc 100644
--- a/src/Transformer/ProductDefaultValuesTransformer.php
+++ b/src/Transformer/ProductDefaultValuesTransformer.php
@@ -12,7 +12,7 @@
class ProductDefaultValuesTransformer implements ProductDataTransformerInterface
{
- private const DEFAULT_STOCK_VALUE = 999;
+ private const DEFAULT_STOCK_VALUE = 0;
private ConfigService $configService;
diff --git a/src/Transformer/ProductMinMaxQuantityTransformer.php b/src/Transformer/ProductMinMaxQuantityTransformer.php
index 4599564..48b75a4 100644
--- a/src/Transformer/ProductMinMaxQuantityTransformer.php
+++ b/src/Transformer/ProductMinMaxQuantityTransformer.php
@@ -83,7 +83,7 @@ private function getMinPurchase(
$existingMinPurchase = $productData->getSwProduct()?->getMinPurchase();
// if unmapped, return current value
if ($minPurchaseMapping === false) {
- return $existingMinPurchase;
+ return $existingMinPurchase ?? 1; // devEcommerce change
}
$minPurchase = $minPurchaseMapping?->getTranslation($defaultLanguage)?->getValue();
diff --git a/src/Transformer/ProductPriceTransformer.php b/src/Transformer/ProductPriceTransformer.php
index c40248f..7816438 100644
--- a/src/Transformer/ProductPriceTransformer.php
+++ b/src/Transformer/ProductPriceTransformer.php
@@ -11,6 +11,8 @@
class ProductPriceTransformer implements ProductDataTransformerInterface
{
+ const DEFAULT_GROSS_PRICE = 99999; // devEcommerce change
+
public function transform(ProductTransformationDTO $productData, Context $context): ProductTransformationDTO
{
$swData = $productData->getShopwareData();
@@ -18,20 +20,20 @@ public function transform(ProductTransformationDTO $productData, Context $contex
$defaultLanguage = $productData->getDefaultLanguage();
$pricePayload = [
- 'linked' => false,
+ 'linked' => true, // devEcommerce change
'currencyId' => Defaults::CURRENCY,
];
if (!$productData->getSwProduct()?->getPrice()) {
- $pricePayload['gross'] = 0;
+ $pricePayload['gross'] = self::DEFAULT_GROSS_PRICE; // devEcommerce change
if ($ergonodeData->getPriceGross() instanceof ProductAttribute) {
- $pricePayload['gross'] = (float)$ergonodeData->getPriceGross()->getTranslation($defaultLanguage)?->getValue() ?? 0;
+ $pricePayload['gross'] = (float)$ergonodeData->getPriceGross()->getTranslation($defaultLanguage)?->getValue() ?? self::DEFAULT_GROSS_PRICE; // devEcommerce change
}
- $pricePayload['net'] = 0;
+ $pricePayload['net'] = self::DEFAULT_GROSS_PRICE; // devEcommerce change
if ($ergonodeData->getPriceNet() instanceof ProductAttribute) {
$pricePayload['net'] = (float)$ergonodeData->getPriceNet()->getTranslation($defaultLanguage)?->getValue(
- ) ?? 0;
+ ) ?? self::DEFAULT_GROSS_PRICE; // devEcommerce change
}
} else {
$pricePayload['gross'] = $ergonodeData->getPriceGross()
diff --git a/src/Transformer/PropertyGroupTransformer.php b/src/Transformer/PropertyGroupTransformer.php
index 59125c1..f7626fd 100644
--- a/src/Transformer/PropertyGroupTransformer.php
+++ b/src/Transformer/PropertyGroupTransformer.php
@@ -87,6 +87,7 @@ public function transformAttributeNode(PropertyGroupTransformationDTO $dto): Pro
$dto->setPropertyGroupPayload([
'id' => $propertyGroup?->getId(),
'name' => $code,
+ 'filterable' => $propertyGroup?->getFilterable() ?? false, // devEcommerce change
'options' => $options,
'translations' => $translations,
'extensions' => [
diff --git a/src/Transformer/VariantsTransformer.php b/src/Transformer/VariantsTransformer.php
index 85126a0..42595ca 100644
--- a/src/Transformer/VariantsTransformer.php
+++ b/src/Transformer/VariantsTransformer.php
@@ -15,6 +15,7 @@
use Shopware\Core\Content\Product\Aggregate\ProductConfiguratorSetting\ProductConfiguratorSettingDefinition;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Framework\Context;
+use Shopware\Core\Framework\Uuid\Uuid;
class VariantsTransformer
{
@@ -78,14 +79,35 @@ public function transform(ProductTransformationDTO $productData, Context $contex
foreach ($transformedVariants as $variant) {
$shopwareData = $variant->getShopwareData();
+ // start devEcommerce change - fix configuratorSettings when add a new option
if (null !== $parentProduct) {
- $shopwareData->setParentId($parentProduct->getId());
+ $parentProductId = $parentProduct->getId();
+ } else{
+ if(!$swData->getData('id')) {
+ $swData->setId(Uuid::randomHex());
+ }
+
+ $parentProductId = $swData->getData('id');
}
+ $shopwareData->setParentId($parentProductId);
+
$swData->addChild($shopwareData);
- if (property_exists(ProductEntity::class, 'displayParent')) {
+ if (property_exists(ProductEntity::class, 'variantListingConfig')) { // devEcommerce change
$swData->setDisplayParent();
}
+
+ if(!$variant->getSwProduct()) {
+ foreach ($variant->getShopwareData()->getData('options') as $option) {
+ $swData->addConfigrationSettings([
+ 'id' => null,
+ 'productId' => $parentProductId,
+ 'optionId' => $option['id'],
+ ]);
+ }
+ }
+ // end start devEcommerce change
+
foreach ($variant->getSwProduct()?->getOptionIds() ?? [] as $optionId) {
if (
diff --git a/src/Util/YesNo.php b/src/Util/YesNo.php
index c4d92ad..b89c16b 100644
--- a/src/Util/YesNo.php
+++ b/src/Util/YesNo.php
@@ -13,11 +13,13 @@ class YesNo
'1',
'YES',
'yes',
+ 'Yes',
'Y',
'y',
'A',
'a',
- 'tak'
+ 'tak',
+ 'Tak'
];
private const FALSE_LIKE = [
@@ -27,11 +29,13 @@ class YesNo
'0',
'NO',
'no',
+ 'No',
'N',
'n',
'B',
'b',
- 'nie'
+ 'nie',
+ 'Nie'
];
public static function cast(string|bool|int $value): bool