Completed
Pull Request — master (#397)
by Jonas
03:29
created

ProductToShop::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 23
nc 1
nop 11
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * (c) shopware AG <[email protected]>
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
namespace ShopwarePlugins\Connect\Components;
9
10
use Shopware\Bundle\SearchBundle\Sorting\ReleaseDateSorting;
11
use Shopware\Connect\Gateway;
12
use Shopware\Components\Model\CategoryDenormalization;
13
use Shopware\Connect\ProductToShop as ProductToShopBase;
14
use Shopware\Connect\Struct\OrderStatus;
15
use Shopware\Connect\Struct\Product;
16
use Shopware\Models\Article\Article as ProductModel;
17
use Shopware\Models\Order\Order;
18
use Shopware\Models\Order\Status;
19
use Shopware\Models\Article\Detail as DetailModel;
20
use Shopware\Models\Attribute\Article as AttributeModel;
21
use Shopware\Components\Model\ModelManager;
22
use Shopware\Connect\Struct\PriceRange;
23
use Shopware\Connect\Struct\ProductUpdate;
24
use Shopware\CustomModels\Connect\ProductStreamAttribute;
25
use Shopware\Models\Customer\Group;
26
use Shopware\Connect\Struct\Property;
27
use Shopware\Models\ProductStream\ProductStream;
28
use Shopware\Models\Property\Group as PropertyGroup;
29
use Shopware\Models\Property\Option as PropertyOption;
30
use Shopware\Models\Property\Value as PropertyValue;
31
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamRepository;
32
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
33
use ShopwarePlugins\Connect\Components\Translations\LocaleMapper;
34
use ShopwarePlugins\Connect\Components\Gateway\ProductTranslationsGateway;
35
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway;
36
use ShopwarePlugins\Connect\Components\Utils\UnitMapper;
37
use Shopware\CustomModels\Connect\Attribute as ConnectAttribute;
38
use Shopware\Models\Article\Image;
39
use Shopware\Models\Article\Supplier;
40
41
/**
42
 * The interface for products imported *from* connect *to* the local shop
43
 *
44
 * @category  Shopware
45
 * @package   Shopware\Plugins\SwagConnect
46
 */
47
class ProductToShop implements ProductToShopBase
48
{
49
    /**
50
     * @var Helper
51
     */
52
    private $helper;
53
54
    /**
55
     * @var ModelManager
56
     */
57
    private $manager;
58
59
    /**
60
     * @var \ShopwarePlugins\Connect\Components\Config
61
     */
62
    private $config;
63
64
    /**
65
     * @var ImageImport
66
     */
67
    private $imageImport;
68
69
    /**
70
     * @var \ShopwarePlugins\Connect\Components\VariantConfigurator
71
     */
72
    private $variantConfigurator;
73
74
    /**
75
     * @var MarketplaceGateway
76
     */
77
    private $marketplaceGateway;
78
79
    /**
80
     * @var ProductTranslationsGateway
81
     */
82
    private $productTranslationsGateway;
83
84
    /**
85
     * @var \Shopware\Models\Shop\Repository
86
     */
87
    private $shopRepository;
88
89
    private $localeRepository;
90
91
    /**
92
     * @var CategoryResolver
93
     */
94
    private $categoryResolver;
95
96
    /**
97
     * @var \Shopware\Connect\Gateway
98
     */
99
    private $connectGateway;
100
101
    /**
102
     * @var \Enlight_Event_EventManager
103
     */
104
    private $eventManager;
105
106
    /**
107
     * @var CategoryDenormalization
108
     */
109
    private $categoryDenormalization;
110
111
    /**
112
     * @param Helper $helper
113
     * @param ModelManager $manager
114
     * @param ImageImport $imageImport
115
     * @param \ShopwarePlugins\Connect\Components\Config $config
116
     * @param VariantConfigurator $variantConfigurator
117
     * @param \ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway $marketplaceGateway
118
     * @param ProductTranslationsGateway $productTranslationsGateway
119
     * @param CategoryResolver $categoryResolver
120
     * @param Gateway $connectGateway
121
     * @param \Enlight_Event_EventManager $eventManager
122
     * @param CategoryDenormalization $categoryDenormalization
123
     */
124
    public function __construct(
125
        Helper $helper,
126
        ModelManager $manager,
127
        ImageImport $imageImport,
128
        Config $config,
129
        VariantConfigurator $variantConfigurator,
130
        MarketplaceGateway $marketplaceGateway,
131
        ProductTranslationsGateway $productTranslationsGateway,
132
        CategoryResolver $categoryResolver,
133
        Gateway $connectGateway,
134
        \Enlight_Event_EventManager $eventManager,
135
        CategoryDenormalization $categoryDenormalization
136
    ) {
137
        $this->helper = $helper;
138
        $this->manager = $manager;
139
        $this->config = $config;
140
        $this->imageImport = $imageImport;
141
        $this->variantConfigurator = $variantConfigurator;
142
        $this->marketplaceGateway = $marketplaceGateway;
143
        $this->productTranslationsGateway = $productTranslationsGateway;
144
        $this->categoryResolver = $categoryResolver;
145
        $this->connectGateway = $connectGateway;
146
        $this->eventManager = $eventManager;
147
        $this->categoryDenormalization = $categoryDenormalization;
148
    }
149
150
    /**
151
     * Start transaction
152
     *
153
     * Starts a transaction, which includes all insertOrUpdate and delete
154
     * operations, as well as the revision updates.
155
     *
156
     * @return void
157
     */
158
    public function startTransaction()
159
    {
160
        $this->manager->getConnection()->beginTransaction();
161
    }
162
163
    /**
164
     * Commit transaction
165
     *
166
     * Commits the transactions, once all operations are queued.
167
     *
168
     * @return void
169
     */
170
    public function commit()
171
    {
172
        $this->manager->getConnection()->commit();
173
    }
174
175
    /**
176
     * Import or update given product
177
     *
178
     * Store product in your shop database as an external product. The
179
     * associated sourceId
180
     *
181
     * @param Product $product
182
     */
183
    public function insertOrUpdate(Product $product)
184
    {
185
        /** @var Product $product */
186
        $product = $this->eventManager->filter(
187
            'Connect_ProductToShop_InsertOrUpdate_Before',
188
            $product
189
        );
190
191
        // todo@dn: Set dummy values and make product inactive
192
        if (empty($product->title) || empty($product->vendor)) {
193
            return;
194
        }
195
196
        if (!empty($product->sku)) {
197
            $number = 'SC-' . $product->shopId . '-' . $product->sku;
198
            $duplicatedDetail = $this->helper->getDetailByNumber($number);
199
            if ($duplicatedDetail
200
                && $this->helper->getConnectAttributeByModel($duplicatedDetail)->getSourceId() != $product->sourceId
201
            ) {
202
                $this->deleteDetail($duplicatedDetail);
203
            }
204
        } else {
205
            $number = 'SC-' . $product->shopId . '-' . $product->sourceId;
206
        }
207
208
        $detail = $this->helper->getArticleDetailModelByProduct($product);
209
        $detail = $this->eventManager->filter(
210
            'Connect_Merchant_Get_Article_Detail_After',
211
            $detail,
212
            [
213
                'product' => $product,
214
                'subject' => $this
215
            ]
216
        );
217
218
        $isMainVariant = false;
219
        if ($detail === null) {
220
            $active = $this->config->getConfig('activateProductsAutomatically', false) ? true : false;
221
            if ($product->groupId !== null) {
222
                $model = $this->helper->getArticleByRemoteProduct($product);
223
                if (!$model instanceof \Shopware\Models\Article\Article) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
224
                    $model = $this->helper->createProductModel($product);
225
                    $model->setActive($active);
226
                    $isMainVariant = true;
227
                }
228
            } else {
229
                $model = $this->helper->getConnectArticleModel($product->sourceId, $product->shopId);
230
                if (!$model instanceof \Shopware\Models\Article\Article) {
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Article\Article does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
231
                    $model = $this->helper->createProductModel($product);
232
                    $model->setActive($active);
233
                }
234
            }
235
236
            $detail = new DetailModel();
237
            $detail->setActive($model->getActive());
238
            $this->manager->persist($detail);
239
            $detail->setArticle($model);
240
            $model->getDetails()->add($detail);
241
            if (!empty($product->variant)) {
242
                $this->variantConfigurator->configureVariantAttributes($product, $detail);
243
            }
244
        } else {
245
            $model = $detail->getArticle();
246
            // fix for isMainVariant flag
247
            // in connect attribute table
248
            $mainDetail = $model->getMainDetail();
249
            if ($detail->getId() === $mainDetail->getId()) {
250
                $isMainVariant = true;
251
            }
252
253
            if (empty($product->variant) && $model->getConfiguratorSet()) {
254
                $this->manager->getConnection()->executeQuery(
255
                    'UPDATE s_articles SET configurator_set_id = NULL WHERE id = ?',
256
                    [$model->getId()]
257
                );
258
            }
259
        }
260
261
        $detail->setNumber($number);
262
263
        /** @var \Shopware\Models\Category\Category $category */
264
        foreach ($model->getCategories() as $category) {
265
            $attribute = $category->getAttribute();
266
            if (!$attribute) {
267
                continue;
268
            }
269
270
            if ($attribute->getConnectImported()) {
271
                $model->removeCategory($category);
272
            }
273
        }
274
275
        $detailAttribute = $detail->getAttribute();
276
        if (!$detailAttribute) {
277
            $detailAttribute = new AttributeModel();
278
            $detail->setAttribute($detailAttribute);
279
            $model->setAttribute($detailAttribute);
280
            $detailAttribute->setArticle($model);
281
            $detailAttribute->setArticleDetail($detail);
282
        }
283
284
        $connectAttribute = $this->helper->getConnectAttributeByModel($detail) ?: new ConnectAttribute;
285
        // configure main variant and groupId
286
        if ($isMainVariant === true) {
287
            $connectAttribute->setIsMainVariant(true);
288
        }
289
        $connectAttribute->setGroupId($product->groupId);
290
291
        list($updateFields, $flag) = $this->getUpdateFields($model, $detail, $connectAttribute, $product);
292
        /*
293
         * Make sure, that the following properties are set for
294
         * - new products
295
         * - products that have been configured to receive these updates
296
         */
297
        if ($updateFields['name']) {
298
            $model->setName($product->title);
299
        }
300
        if ($updateFields['shortDescription']) {
301
            $model->setDescription($product->shortDescription);
302
        }
303
        if ($updateFields['longDescription']) {
304
            $model->setDescriptionLong($product->longDescription);
305
        }
306
307
        if ($updateFields['additionalDescription']) {
308
            $detailAttribute->setConnectProductDescription($product->additionalDescription);
309
        }
310
311
        if ($product->vat !== null) {
312
            $repo = $this->manager->getRepository('Shopware\Models\Tax\Tax');
313
            $tax = round($product->vat * 100, 2);
314
            /** @var \Shopware\Models\Tax\Tax $tax */
315
            $tax = $repo->findOneBy(['tax' => $tax]);
316
            $model->setTax($tax);
317
        }
318
319
        if ($product->vendor !== null) {
320
            $repo = $this->manager->getRepository('Shopware\Models\Article\Supplier');
321
            $supplier = $repo->findOneBy(['name' => $product->vendor]);
322
            if ($supplier === null) {
323
                $supplier = $this->createSupplier($product->vendor);
324
            }
325
            $model->setSupplier($supplier);
326
        }
327
328
        //set product properties
329
        $this->applyProductProperties($model, $product);
330
331
        // apply marketplace attributes
332
        $detailAttribute = $this->applyMarketplaceAttributes($detailAttribute, $product);
333
334
        $connectAttribute->setShopId($product->shopId);
335
        $connectAttribute->setSourceId($product->sourceId);
336
        $connectAttribute->setExportStatus(null);
337
        $connectAttribute->setPurchasePrice($product->purchasePrice);
338
        $connectAttribute->setFixedPrice($product->fixedPrice);
339
        $connectAttribute->setStream($product->stream);
340
341
        // store product categories to connect attribute
342
        $connectAttribute->setCategory($product->categories);
343
344
        $connectAttribute->setLastUpdateFlag($flag);
345
        // store purchasePriceHash and offerValidUntil
346
        $connectAttribute->setPurchasePriceHash($product->purchasePriceHash);
347
        $connectAttribute->setOfferValidUntil($product->offerValidUntil);
348
349
        $detail->setInStock($product->availability);
350
        $detail->setEan($product->ean);
351
        $detail->setShippingTime($product->deliveryWorkDays);
352
        $releaseDate = new \DateTime();
353
        $releaseDate->setTimestamp($product->deliveryDate);
354
        $detail->setReleaseDate($releaseDate);
355
        $detail->setMinPurchase($product->minPurchaseQuantity);
356
357
        // some shops have feature "sell not in stock",
358
        // then end customer should be able to by the product with stock = 0
359
        $shopConfiguration = $this->connectGateway->getShopConfiguration($product->shopId);
360
        if ($shopConfiguration && $shopConfiguration->sellNotInStock) {
361
            $model->setLastStock(false);
362
        } else {
363
            $model->setLastStock(true);
364
        }
365
366
        // if connect product has unit
367
        // find local unit with units mapping
368
        // and add to detail model
369
        if (array_key_exists('unit', $product->attributes) && $product->attributes['unit']) {
370
            $detailAttribute->setConnectRemoteUnit($product->attributes['unit']);
371
            if ($this->config->getConfig($product->attributes['unit']) == null) {
372
                $this->config->setConfig($product->attributes['unit'], '', null, 'units');
373
            }
374
375
            /** @var \ShopwarePlugins\Connect\Components\Utils\UnitMapper $unitMapper */
376
            $unitMapper = new UnitMapper($this->config, $this->manager);
377
378
            $shopwareUnit = $unitMapper->getShopwareUnit($product->attributes['unit']);
379
380
            /** @var \Shopware\Models\Article\Unit $unit */
381
            $unit = $this->helper->getUnit($shopwareUnit);
382
            $detail->setUnit($unit);
383
            $detail->setPurchaseUnit($product->attributes['quantity']);
384
            $detail->setReferenceUnit($product->attributes['ref_quantity']);
385
        } else {
386
            $detail->setUnit(null);
387
            $detail->setPurchaseUnit(null);
388
            $detail->setReferenceUnit(null);
389
        }
390
391
        // set dimension
392
        if (array_key_exists('dimension', $product->attributes) && $product->attributes['dimension']) {
393
            $dimension = explode('x', $product->attributes['dimension']);
394
            $detail->setLen($dimension[0]);
395
            $detail->setWidth($dimension[1]);
396
            $detail->setHeight($dimension[2]);
397
        } else {
398
            $detail->setLen(null);
399
            $detail->setWidth(null);
400
            $detail->setHeight(null);
401
        }
402
403
        // set weight
404
        if (array_key_exists('weight', $product->attributes) && $product->attributes['weight']) {
405
            $detail->setWeight($product->attributes['weight']);
406
        }
407
408
        //set package unit
409
        if (array_key_exists(Product::ATTRIBUTE_PACKAGEUNIT, $product->attributes)) {
410
            $detail->setPackUnit($product->attributes[Product::ATTRIBUTE_PACKAGEUNIT]);
411
        }
412
413
        //set basic unit
414
        if (array_key_exists(Product::ATTRIBUTE_BASICUNIT, $product->attributes)) {
415
            $detail->setMinPurchase($product->attributes[Product::ATTRIBUTE_BASICUNIT]);
416
        }
417
418
        //set manufacturer no.
419
        if (array_key_exists(Product::ATTRIBUTE_MANUFACTURERNUMBER, $product->attributes)) {
420
            $detail->setSupplierNumber($product->attributes[Product::ATTRIBUTE_MANUFACTURERNUMBER]);
421
        }
422
423
        // Whenever a product is updated, store a json encoded list of all fields that are updated optionally
424
        // This way a customer will be able to apply the most recent changes any time later
425
        $connectAttribute->setLastUpdate(json_encode([
426
            'shortDescription' => $product->shortDescription,
427
            'longDescription' => $product->longDescription,
428
            'additionalDescription' => $product->additionalDescription,
429
            'purchasePrice' => $product->purchasePrice,
430
            'image' => $product->images,
431
            'variantImages' => $product->variantImages,
432
            'price' => $product->price * ($product->vat + 1),
433
            'name' => $product->title,
434
            'vat' => $product->vat
435
        ]));
436
437
        if ($model->getMainDetail() === null) {
438
            $model->setMainDetail($detail);
439
        }
440
441
        if ($detail->getAttribute() === null) {
442
            $detail->setAttribute($detailAttribute);
443
            $detailAttribute->setArticle($model);
444
        }
445
446
        $connectAttribute->setArticle($model);
447
        $connectAttribute->setArticleDetail($detail);
448
449
        $this->eventManager->notify(
450
            'Connect_Merchant_Saving_ArticleAttribute_Before',
451
            [
452
                'subject' => $this,
453
                'connectAttribute' => $connectAttribute
454
            ]
455
        );
456
457
        $this->manager->persist($model);
458
        $this->manager->flush();
459
460
        $this->categoryResolver->storeRemoteCategories($product->categories, $model->getId());
461
        $categories = $this->categoryResolver->resolve($product->categories);
462
        if (count($categories) > 0) {
463
            $detailAttribute->setConnectMappedCategory(true);
464
        }
465
466
        $this->manager->persist($connectAttribute);
467
        $this->manager->persist($detail);
468
        //article has to be flushed
469
        $this->manager->persist($detailAttribute);
470
        $this->manager->flush();
471
472
        $this->categoryDenormalization->disableTransactions();
473
        foreach ($categories as $category) {
474
            $this->categoryDenormalization->addAssignment($model->getId(), $category);
475
            $this->manager->getConnection()->executeQuery(
476
                'INSERT IGNORE INTO `s_articles_categories` (`articleID`, `categoryID`) VALUES (?,?)',
477
                [$model->getId(),  $category]
478
            );
479
        }
480
        $this->categoryDenormalization->enableTransactions();
481
482
        $defaultCustomerGroup = $this->helper->getDefaultCustomerGroup();
483
        // Only set prices, if fixedPrice is active or price updates are configured
484
        if (count($detail->getPrices()) == 0 || $connectAttribute->getFixedPrice() || $updateFields['price']) {
485
            $this->setPrice($model, $detail, $product);
486
        }
487
        // If the price is not being update, update the purchasePrice anyway
488
        $this->setPurchasePrice($detail, $product->purchasePrice, $defaultCustomerGroup);
489
490
        $this->manager->clear();
491
492
        $this->addArticleTranslations($model, $product);
493
494
        //clear cache for that article
495
        $this->helper->clearArticleCache($model->getId());
496
497
        if ($updateFields['image']) {
498
            // Reload the model in order to not to work an the already flushed model
499
            $model = $this->helper->getArticleModelByProduct($product);
500
            // import only global images for article
501
            $this->imageImport->importImagesForArticle(array_diff($product->images, $product->variantImages), $model);
0 ignored issues
show
Bug introduced by
It seems like $model defined by $this->helper->getArticleModelByProduct($product) on line 499 can be null; however, ShopwarePlugins\Connect\...mportImagesForArticle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
502
            // Reload the article detail model in order to not to work an the already flushed model
503
            $detail = $this->helper->getArticleDetailModelByProduct($product);
504
            // import only specific images for variant
505
            $this->imageImport->importImagesForDetail($product->variantImages, $detail);
0 ignored issues
show
Bug introduced by
It seems like $detail defined by $this->helper->getArticl...odelByProduct($product) on line 503 can be null; however, ShopwarePlugins\Connect\...importImagesForDetail() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
506
        }
507
508
        $this->eventManager->notify(
509
            'Connect_ProductToShop_InsertOrUpdate_After',
510
            [
511
                'connectProduct' => $product,
512
                'shopArticleDetail' => $detail
513
            ]
514
        );
515
516
        $stream = $this->getOrCreateStream($product);
517
        $this->addProductToStream($stream, $model);
518
    }
519
520
    /**
521
     * @param ProductModel $article
522
     * @param Product $product
523
     */
524
    private function applyProductProperties(ProductModel $article, Product $product)
525
    {
526
        if (empty($product->properties)) {
527
            return;
528
        }
529
530
        /** @var Property $firstProperty */
531
        $firstProperty = reset($product->properties);
532
        $groupRepo = $this->manager->getRepository(PropertyGroup::class);
533
        $group = $groupRepo->findOneBy(['name' => $firstProperty->groupName]);
534
535
        if (!$group) {
536
            $group = new PropertyGroup();
537
            $group->setName($firstProperty->groupName);
538
            $group->setComparable($firstProperty->comparable);
539
            $group->setSortMode($firstProperty->sortMode);
540
            $group->setPosition($firstProperty->groupPosition);
541
542
            $attribute = new \Shopware\Models\Attribute\PropertyGroup();
543
            $attribute->setPropertyGroup($group);
544
            $attribute->setConnectIsRemote(true);
545
            $group->setAttribute($attribute);
546
547
            $this->manager->persist($attribute);
548
            $this->manager->persist($group);
549
            $this->manager->flush();
550
        }
551
552
        $propertyValues = $article->getPropertyValues();
553
        $propertyValues->clear();
554
        $this->manager->persist($article);
555
        $this->manager->flush();
556
557
        $article->setPropertyGroup($group);
558
559
        $optionRepo = $this->manager->getRepository(PropertyOption::class);
560
        $valueRepo = $this->manager->getRepository(PropertyValue::class);
561
562
        foreach ($product->properties as $property) {
563
            $option = $optionRepo->findOneBy(['name' => $property->option]);
564
            $optionExists = $option instanceof PropertyOption;
0 ignored issues
show
Bug introduced by
The class Shopware\Models\Property\Option does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
565
            if (!$option) {
566
                $option = new PropertyOption();
567
                $option->setName($property->option);
568
                $option->setFilterable($property->filterable);
569
570
                $attribute = new \Shopware\Models\Attribute\PropertyOption();
571
                $attribute->setPropertyOption($option);
572
                $attribute->setConnectIsRemote(true);
573
                $option->setAttribute($attribute);
574
575
                $this->manager->persist($option);
576
                $this->manager->flush($option);
577
            }
578
579
            if (!$optionExists || !$value = $valueRepo->findOneBy(['option' => $option, 'value' => $property->value])) {
580
                $value = new PropertyValue($option, $property->value);
581
                $value->setPosition($property->valuePosition);
582
583
                $attribute = new \Shopware\Models\Attribute\PropertyValue();
584
                $attribute->setPropertyValue($value);
585
                $attribute->setConnectIsRemote(true);
586
                $value->setAttribute($attribute);
587
588
                $this->manager->persist($value);
589
            }
590
591
            if (!$propertyValues->contains($value)) {
592
                //add only new values
593
                $propertyValues->add($value);
594
            }
595
596
            $filters = [
597
                ['property' => 'options.name', 'expression' => '=', 'value' => $property->option],
598
                ['property' => 'groups.name', 'expression' => '=', 'value' => $property->groupName],
599
            ];
600
601
            $query = $groupRepo->getPropertyRelationQuery($filters, null, 1, 0);
602
            $relation = $query->getOneOrNullResult();
603
604
            if (!$relation) {
605
                $group->addOption($option);
606
                $this->manager->persist($group);
607
                $this->manager->flush($group);
608
            }
609
        }
610
611
        $article->setPropertyValues($propertyValues);
612
613
        $this->manager->persist($article);
614
        $this->manager->flush();
615
    }
616
617
    /**
618
     * @param Product $product
619
     * @return ProductStream
620
     */
621
    private function getOrCreateStream(Product $product)
622
    {
623
        /** @var ProductStreamRepository $repo */
624
        $repo = $this->manager->getRepository(ProductStreamAttribute::class);
625
        $stream = $repo->findConnectByName($product->stream);
626
627
        if (!$stream) {
628
            $stream = new ProductStream();
629
            $stream->setName($product->stream);
630
            $stream->setType(ProductStreamService::STATIC_STREAM);
631
            $stream->setSorting(json_encode(
632
                [ReleaseDateSorting::class => ['direction' => 'desc']]
633
            ));
634
635
            //add attributes
636
            $attribute = new \Shopware\Models\Attribute\ProductStream();
637
            $attribute->setProductStream($stream);
638
            $attribute->setConnectIsRemote(true);
639
            $stream->setAttribute($attribute);
640
641
            $this->manager->persist($attribute);
642
            $this->manager->persist($stream);
643
            $this->manager->flush();
644
        }
645
646
        return $stream;
647
    }
648
649
    /**
650
     * @param ProductStream $stream
651
     * @param ProductModel $article
652
     * @throws \Doctrine\DBAL\DBALException
653
     */
654
    private function addProductToStream(ProductStream $stream, ProductModel $article)
655
    {
656
        $conn = $this->manager->getConnection();
657
        $sql = 'INSERT INTO `s_product_streams_selection` (`stream_id`, `article_id`)
658
                VALUES (:streamId, :articleId)
659
                ON DUPLICATE KEY UPDATE stream_id = :streamId, article_id = :articleId';
660
        $stmt = $conn->prepare($sql);
661
        $stmt->execute([':streamId' => $stream->getId(), ':articleId' => $article->getId()]);
662
    }
663
664
    /**
665
     * Set detail purchase price with plain SQL
666
     * Entity usage throws exception when error handlers are disabled
667
     *
668
     * @param ProductModel $article
669
     * @param DetailModel $detail
670
     * @param Product $product
671
     * @throws \Doctrine\DBAL\DBALException
672
     */
673
    private function setPrice(ProductModel $article, DetailModel $detail, Product $product)
674
    {
675
        // set price via plain SQL because shopware throws exception
676
        // undefined index: key when error handler is disabled
677
        $customerGroup = $this->helper->getDefaultCustomerGroup();
678
679
        if (!empty($product->priceRanges)) {
680
            $this->setPriceRange($article, $detail, $product->priceRanges, $customerGroup);
681
682
            return;
683
        }
684
685
        $id = $this->manager->getConnection()->fetchColumn(
686
            'SELECT id FROM `s_articles_prices`
687
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
688
            [$customerGroup->getKey(), 1, 'beliebig', $article->getId(), $detail->getId()]
689
        );
690
691
        // todo@sb: test update prices
692
        if ($id > 0) {
693
            $this->manager->getConnection()->executeQuery(
694
                'UPDATE `s_articles_prices` SET `price` = ?, `baseprice` = ? WHERE `id` = ?',
695
                [$product->price, $product->purchasePrice, $id]
696
            );
697
        } else {
698
            $this->manager->getConnection()->executeQuery(
699
                'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`, `baseprice`)
700
              VALUES (?, 1, "beliebig", ?, ?, ?, ?);',
701
                [$customerGroup->getKey(), $article->getId(), $detail->getId(), $product->price, $product->purchasePrice]
702
            );
703
        }
704
    }
705
706
    /**
707
     * @param ProductModel $article
708
     * @param DetailModel $detail
709
     * @param array $priceRanges
710
     * @param Group $group
711
     * @throws \Doctrine\DBAL\ConnectionException
712
     * @throws \Exception
713
     */
714
    private function setPriceRange(ProductModel $article, DetailModel $detail, array $priceRanges, Group $group)
715
    {
716
        $this->manager->getConnection()->beginTransaction();
717
718
        try {
719
            // We always delete the prices,
720
            // because we can not know which record is update
721
            $this->manager->getConnection()->executeQuery(
722
                'DELETE FROM `s_articles_prices` WHERE `articleID` = ? AND `articledetailsID` = ?',
723
                [$article->getId(), $detail->getId()]
724
            );
725
726
            /** @var PriceRange $priceRange */
727
            foreach ($priceRanges as $priceRange) {
728
                $priceTo = $priceRange->to == PriceRange::ANY ? 'beliebig' : $priceRange->to;
729
730
                //todo: maybe batch insert if possible?
731
                $this->manager->getConnection()->executeQuery(
732
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`)
733
                      VALUES (?, ?, ?, ?, ?, ?);',
734
                    [
735
                        $group->getKey(),
736
                        $priceRange->from,
737
                        $priceTo,
738
                        $article->getId(),
739
                        $detail->getId(),
740
                        $priceRange->price
741
                    ]
742
                );
743
            }
744
            $this->manager->getConnection()->commit();
745
        } catch (\Exception $e) {
746
            $this->manager->getConnection()->rollBack();
747
            throw new \Exception($e->getMessage());
748
        }
749
    }
750
751
    /**
752
     * Adds translation record for given article
753
     *
754
     * @param ProductModel $article
755
     * @param Product $sdkProduct
756
     */
757
    private function addArticleTranslations(ProductModel $article, Product $sdkProduct)
758
    {
759
        /** @var \Shopware\Connect\Struct\Translation $translation */
760
        foreach ($sdkProduct->translations as $key => $translation) {
761
            /** @var \Shopware\Models\Shop\Locale $locale */
762
            $locale = $this->getLocaleRepository()->findOneBy(['locale' => LocaleMapper::getShopwareLocale($key)]);
763
            /** @var \Shopware\Models\Shop\Shop $shop */
764
            $shop = $this->getShopRepository()->findOneBy(['locale' => $locale]);
765
            if (!$shop) {
766
                continue;
767
            }
768
769
            $this->productTranslationsGateway->addArticleTranslation($translation, $article->getId(), $shop->getId());
770
        }
771
    }
772
773
    /**
774
     * dsadsa
775
     * @return \Shopware\Components\Model\ModelRepository
776
     */
777
    private function getLocaleRepository()
778
    {
779
        if (!$this->localeRepository) {
780
            $this->localeRepository = $this->manager->getRepository('Shopware\Models\Shop\Locale');
781
        }
782
783
        return $this->localeRepository;
784
    }
785
786
    private function getShopRepository()
787
    {
788
        if (!$this->shopRepository) {
789
            $this->shopRepository = $this->manager->getRepository('Shopware\Models\Shop\Shop');
790
        }
791
792
        return $this->shopRepository;
793
    }
794
795
    /**
796
     * Delete product or product variant with given shopId and sourceId.
797
     *
798
     * Only the combination of both identifies a product uniquely. Do NOT
799
     * delete products just by their sourceId.
800
     *
801
     * You might receive delete requests for products, which are not available
802
     * in your shop. Just ignore them.
803
     *
804
     * @param string $shopId
805
     * @param string $sourceId
806
     * @return void
807
     */
808
    public function delete($shopId, $sourceId)
809
    {
810
        $detail = $this->helper->getArticleDetailModelByProduct(new Product([
811
            'shopId' => $shopId,
812
            'sourceId' => $sourceId,
813
        ]));
814
        if ($detail === null) {
815
            return;
816
        }
817
818
        $this->deleteDetail($detail);
819
    }
820
821
    /**
822
     * @param DetailModel $detailModel
823
     */
824
    private function deleteDetail(DetailModel $detailModel)
825
    {
826
        $this->eventManager->notify(
827
            'Connect_Merchant_Delete_Product_Before',
828
            [
829
                'subject' => $this,
830
                'articleDetail' => $detailModel
831
            ]
832
        );
833
834
        $article = $detailModel->getArticle();
835
        // Not sure why, but the Attribute can be NULL
836
        $attribute = $this->helper->getConnectAttributeByModel($detailModel);
837
        $this->manager->remove($detailModel);
838
839
        if ($attribute) {
840
            $this->manager->remove($attribute);
841
        }
842
843
        // if removed variant is main variant
844
        // find first variant which is not main and mark it
845
        if ($detailModel->getKind() === 1) {
846
            /** @var \Shopware\Models\Article\Detail $variant */
847
            foreach ($article->getDetails() as $variant) {
848
                if ($variant->getId() != $detailModel->getId()) {
849
                    $variant->setKind(1);
850
                    $article->setMainDetail($variant);
851
                    $connectAttribute = $this->helper->getConnectAttributeByModel($variant);
852
                    if (!$connectAttribute) {
853
                        continue;
854
                    }
855
                    $connectAttribute->setIsMainVariant(true);
856
                    $this->manager->persist($connectAttribute);
857
                    $this->manager->persist($article);
858
                    $this->manager->persist($variant);
859
                    break;
860
                }
861
            }
862
        }
863
864
        if (count($details = $article->getDetails()) === 1) {
865
            $details->clear();
866
            $this->manager->remove($article);
867
        }
868
869
        // Do not remove flush. It's needed when remove article,
870
        // because duplication of ordernumber. Even with remove before
871
        // persist calls mysql throws exception "Duplicate entry"
872
        $this->manager->flush();
873
        // always clear entity manager, because $article->getDetails() returns
874
        // more than 1 detail, but all of them were removed except main one.
875
        $this->manager->clear();
876
    }
877
878
    /**
879
     * Get array of update info for the known fields
880
     *
881
     * @param $model
882
     * @param $detail
883
     * @param $attribute
884
     * @param $product
885
     * @return array
886
     */
887
    public function getUpdateFields($model, $detail, $attribute, $product)
888
    {
889
        // This also defines the flags of these fields
890
        $fields = $this->helper->getUpdateFlags();
891
        $flagsByName = array_flip($fields);
892
893
        $flag = 0;
894
        $output = [];
895
        foreach ($fields as $key => $field) {
896
            // Don't handle the imageInitialImport flag
897
            if ($field == 'imageInitialImport') {
898
                continue;
899
            }
900
901
            // If this is a new product
902
            if (!$model->getId() && $field == 'image' && !$this->config->getConfig('importImagesOnFirstImport', false)) {
903
                $output[$field] = false;
904
                $flag |= $flagsByName['imageInitialImport'];
905
                continue;
906
            }
907
908
            $updateAllowed = $this->isFieldUpdateAllowed($field, $model, $attribute);
909
            $output[$field] = $updateAllowed;
910
            if (!$updateAllowed && $this->hasFieldChanged($field, $model, $detail, $product)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $updateAllowed of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
911
                $flag |= $key;
912
            }
913
        }
914
915
        return [$output, $flag];
916
    }
917
918
    /**
919
     * Determine if a given field has changed
920
     *
921
     * @param $field
922
     * @param ProductModel $model
923
     * @param DetailModel $detail
924
     * @param Product $product
925
     * @return bool
926
     */
927
    public function hasFieldChanged($field, ProductModel $model, DetailModel $detail, Product $product)
928
    {
929
        switch ($field) {
930
            case 'shortDescription':
931
                return $model->getDescription() != $product->shortDescription;
932
            case 'longDescription':
933
                return $model->getDescriptionLong() != $product->longDescription;
934
            case 'additionalDescription':
935
                return $detail->getAttribute()->getConnectProductDescription() != $product->additionalDescription;
936
            case 'name':
937
                return $model->getName() != $product->title;
938
            case 'image':
939
                return count($model->getImages()) != count($product->images);
940
            case 'price':
941
                $prices = $detail->getPrices();
942
                if (empty($prices)) {
943
                    return true;
944
                }
945
                $price = $prices->first();
946
                if (!$price) {
947
                    return true;
948
                }
949
950
                return $prices->first()->getPrice() != $product->price;
951
        }
952
953
        throw new \InvalidArgumentException('Unrecognized field');
954
    }
955
956
    /**
957
     * Helper method to determine if a given $fields may/must be updated.
958
     * This method will check for the model->id in order to determine, if it is a new entity. Therefore
959
     * this method cannot be used after the model in question was already flushed.
960
     *
961
     * @param $field
962
     * @param $model ProductModel
963
     * @param $attribute ConnectAttribute
964
     * @throws \RuntimeException
965
     * @return bool|null
966
     */
967
    public function isFieldUpdateAllowed($field, ProductModel $model, ConnectAttribute $attribute)
968
    {
969
        $allowed = [
970
            'ShortDescription',
971
            'LongDescription',
972
            'AdditionalDescription',
973
            'Image',
974
            'Price',
975
            'Name',
976
        ];
977
978
        // Always allow updates for new models
979
        if (!$model->getId()) {
980
            return true;
981
        }
982
983
        $field = ucfirst($field);
984
        $attributeGetter = 'getUpdate' . $field;
985
        $configName = 'overwriteProduct' . $field;
986
987
        if (!in_array($field, $allowed)) {
988
            throw new \RuntimeException("Unknown update field {$field}");
989
        }
990
991
        $attributeValue = $attribute->$attributeGetter();
992
993
994
995
        // If the value is 'null' or 'inherit', the behaviour will be inherited from the global configuration
996
        // Once we have a supplier based configuration, we need to take it into account here
997
        if ($attributeValue == null || $attributeValue == 'inherit') {
998
            return $this->config->getConfig($configName, true);
999
        }
1000
1001
        return $attributeValue == 'overwrite';
1002
    }
1003
1004
    /**
1005
     * Read product attributes mapping and set to shopware attribute model
1006
     *
1007
     * @param AttributeModel $detailAttribute
1008
     * @param Product $product
1009
     * @return AttributeModel
1010
     */
1011
    private function applyMarketplaceAttributes(AttributeModel $detailAttribute, Product $product)
1012
    {
1013
        $detailAttribute->setConnectReference($product->sourceId);
1014
        $detailAttribute->setConnectArticleShipping($product->shipping);
1015
        //todo@sb: check if connectAttribute matches position of the marketplace attribute
1016
        array_walk($product->attributes, function ($value, $key) use ($detailAttribute) {
1017
            $shopwareAttribute = $this->marketplaceGateway->findShopwareMappingFor($key);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $shopwareAttribute is correct as $this->marketplaceGatewa...hopwareMappingFor($key) (which targets ShopwarePlugins\Connect\...indShopwareMappingFor()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1018
            if (strlen($shopwareAttribute) > 0) {
1019
                $setter = 'set' . ucfirst($shopwareAttribute);
1020
                $detailAttribute->$setter($value);
1021
            }
1022
        });
1023
1024
        return $detailAttribute;
1025
    }
1026
1027
    /**
1028
     * @param $vendor
1029
     * @return Supplier
1030
     */
1031
    private function createSupplier($vendor)
1032
    {
1033
        $supplier = new Supplier();
1034
1035
        if (is_array($vendor)) {
1036
            $supplier->setName($vendor['name']);
1037
            $supplier->setDescription($vendor['description']);
1038
            if (array_key_exists('url', $vendor) && $vendor['url']) {
1039
                $supplier->setLink($vendor['url']);
1040
            }
1041
1042
            $supplier->setMetaTitle($vendor['page_title']);
1043
1044
            if (array_key_exists('logo_url', $vendor) && $vendor['logo_url']) {
1045
                $this->imageImport->importImageForSupplier($vendor['logo_url'], $supplier);
1046
            }
1047
        } else {
1048
            $supplier->setName($vendor);
1049
        }
1050
1051
        //sets supplier attributes
1052
        $attr = new \Shopware\Models\Attribute\ArticleSupplier();
1053
        $attr->setConnectIsRemote(true);
1054
1055
        $supplier->setAttribute($attr);
1056
1057
        return $supplier;
1058
    }
1059
1060
    /**
1061
     * Set detail purchase price with plain SQL
1062
     * Entity usage throws exception when error handlers are disabled
1063
     *
1064
     * @param DetailModel $detail
1065
     * @param float $purchasePrice
1066
     * @param Group $defaultGroup
1067
     * @throws \Doctrine\DBAL\DBALException
1068
     */
1069
    private function setPurchasePrice(DetailModel $detail, $purchasePrice, Group $defaultGroup)
1070
    {
1071
        if (method_exists($detail, 'setPurchasePrice')) {
1072
            $this->manager->getConnection()->executeQuery(
1073
                    'UPDATE `s_articles_details` SET `purchaseprice` = ? WHERE `id` = ?',
1074
                    [$purchasePrice, $detail->getId()]
1075
                );
1076
        } else {
1077
            $id = $this->manager->getConnection()->fetchColumn(
1078
                'SELECT id FROM `s_articles_prices`
1079
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
1080
                [$defaultGroup->getKey(), 1, 'beliebig', $detail->getArticleId(), $detail->getId()]
1081
            );
1082
1083
            if ($id > 0) {
1084
                $this->manager->getConnection()->executeQuery(
1085
                    'UPDATE `s_articles_prices` SET `baseprice` = ? WHERE `id` = ?',
1086
                    [$purchasePrice, $id]
1087
                );
1088
            } else {
1089
                $this->manager->getConnection()->executeQuery(
1090
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `baseprice`)
1091
              VALUES (?, 1, "beliebig", ?, ?, ?);',
1092
                    [$defaultGroup->getKey(), $detail->getArticleId(), $detail->getId(), $purchasePrice]
1093
                );
1094
            }
1095
        }
1096
    }
1097
1098
    public function update($shopId, $sourceId, ProductUpdate $product)
1099
    {
1100
        // find article detail id
1101
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1102
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1103
            [$sourceId, $shopId]
1104
        );
1105
1106
        $this->eventManager->notify(
1107
            'Connect_Merchant_Update_GeneralProductInformation',
1108
            [
1109
                'subject' => $this,
1110
                'shopId' => $shopId,
1111
                'sourceId' => $sourceId,
1112
                'articleDetailId' => $articleDetailId
1113
            ]
1114
        );
1115
1116
        // update purchasePriceHash, offerValidUntil and purchasePrice in connect attribute
1117
        $this->manager->getConnection()->executeUpdate(
1118
            'UPDATE s_plugin_connect_items SET purchase_price_hash = ?, offer_valid_until = ?, purchase_price = ?
1119
            WHERE source_id = ? AND shop_id = ?',
1120
            [
1121
                $product->purchasePriceHash,
1122
                $product->offerValidUntil,
1123
                $product->purchasePrice,
1124
                $sourceId,
1125
                $shopId,
1126
            ]
1127
        );
1128
1129
        // update stock in article detail
1130
        // update prices
1131
        // if purchase price is stored in article detail
1132
        // update it together with stock
1133
        // since shopware 5.2
1134
        if (method_exists('Shopware\Models\Article\Detail', 'getPurchasePrice')) {
1135
            $this->manager->getConnection()->executeUpdate(
1136
                'UPDATE s_articles_details SET instock = ?, purchaseprice = ? WHERE id = ?',
1137
                [$product->availability, $product->purchasePrice, $articleDetailId]
1138
            );
1139
        } else {
1140
            $this->manager->getConnection()->executeUpdate(
1141
                'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1142
                [$product->availability, $articleDetailId]
1143
            );
1144
        }
1145
        $this->manager->getConnection()->executeUpdate(
1146
            "UPDATE s_articles_prices SET price = ?, baseprice = ? WHERE articledetailsID = ? AND pricegroup = 'EK'",
1147
            [$product->price, $product->purchasePrice, $articleDetailId]
1148
        );
1149
    }
1150
1151
    public function changeAvailability($shopId, $sourceId, $availability)
1152
    {
1153
        // find article detail id
1154
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1155
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1156
            [$sourceId, $shopId]
1157
        );
1158
1159
        $this->eventManager->notify(
1160
            'Connect_Merchant_Update_GeneralProductInformation',
1161
            [
1162
                'subject' => $this,
1163
                'shopId' => $shopId,
1164
                'sourceId' => $sourceId,
1165
                'articleDetailId' => $articleDetailId
1166
            ]
1167
        );
1168
1169
        // update stock in article detail
1170
        $this->manager->getConnection()->executeUpdate(
1171
            'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1172
            [$availability, $articleDetailId]
1173
        );
1174
    }
1175
1176
    /**
1177
     * @inheritDoc
1178
     */
1179
    public function makeMainVariant($shopId, $sourceId, $groupId)
1180
    {
1181
        //find article detail which should be selected as main one
1182
        $newMainDetail = $this->helper->getConnectArticleDetailModel($sourceId, $shopId);
1183
        if (!$newMainDetail) {
1184
            return;
1185
        }
1186
1187
        /** @var \Shopware\Models\Article\Article $article */
1188
        $article = $newMainDetail->getArticle();
1189
1190
        $this->eventManager->notify(
1191
            'Connect_Merchant_Update_ProductMainVariant_Before',
1192
            [
1193
                'subject' => $this,
1194
                'shopId' => $shopId,
1195
                'sourceId' => $sourceId,
1196
                'articleId' => $article->getId(),
1197
                'articleDetailId' => $newMainDetail->getId()
1198
            ]
1199
        );
1200
1201
        // replace current main detail with new one
1202
        $currentMainDetail = $article->getMainDetail();
1203
        $currentMainDetail->setKind(2);
1204
        $newMainDetail->setKind(1);
1205
        $article->setMainDetail($newMainDetail);
1206
1207
        $this->manager->persist($newMainDetail);
1208
        $this->manager->persist($currentMainDetail);
1209
        $this->manager->persist($article);
1210
        $this->manager->flush();
1211
    }
1212
1213
    /**
1214
     * Updates the status of an Order
1215
     *
1216
     * @param string $localOrderId
1217
     * @param string $orderStatus
1218
     * @param string $trackingNumber
1219
     * @return void
1220
     */
1221
    public function updateOrderStatus($localOrderId, $orderStatus, $trackingNumber)
1222
    {
1223
        if ($this->config->getConfig('updateOrderStatus') == 1) {
1224
            $this->updateDeliveryStatus($localOrderId, $orderStatus);
1225
        }
1226
1227
        if ($trackingNumber) {
1228
            $this->updateTrackingNumber($localOrderId, $trackingNumber);
1229
        }
1230
    }
1231
1232
    /**
1233
     * @param $localOrderId
1234
     * @param $orderStatus
1235
     */
1236
    private function updateDeliveryStatus($localOrderId, $orderStatus)
1237
    {
1238
        $status = false;
1239
        if ($orderStatus === OrderStatus::STATE_IN_PROCESS) {
1240
            $status = Status::ORDER_STATE_PARTIALLY_DELIVERED;
1241
        } elseif ($orderStatus === OrderStatus::STATE_DELIVERED) {
1242
            $status = Status::ORDER_STATE_COMPLETELY_DELIVERED;
1243
        }
1244
1245
        if ($status) {
1246
            $this->manager->getConnection()->executeQuery(
1247
                'UPDATE s_order 
1248
                SET status = :orderStatus
1249
                WHERE ordernumber = :orderNumber',
1250
                [
1251
                    ':orderStatus' => $status,
1252
                    ':orderNumber' => $localOrderId
1253
                ]
1254
            );
1255
        }
1256
    }
1257
1258
    /**
1259
     * @param string $localOrderId
1260
     * @param string $trackingNumber
1261
     */
1262
    private function updateTrackingNumber($localOrderId, $trackingNumber)
1263
    {
1264
        $currentTrackingCode = $this->manager->getConnection()->fetchColumn(
1265
            'SELECT trackingcode
1266
            FROM s_order
1267
            WHERE ordernumber = :orderNumber',
1268
            [
1269
                ':orderNumber' => $localOrderId
1270
            ]
1271
        );
1272
1273
        if (!$currentTrackingCode) {
1274
            $newTracking = $trackingNumber;
1275
        } else {
1276
            $newTracking = $this->combineTrackingNumbers($trackingNumber, $currentTrackingCode);
1277
        }
1278
1279
        $this->manager->getConnection()->executeQuery(
1280
            'UPDATE s_order 
1281
            SET trackingcode = :trackingCode
1282
            WHERE ordernumber = :orderNumber',
1283
            [
1284
                ':trackingCode' => $newTracking,
1285
                ':orderNumber' => $localOrderId
1286
            ]
1287
        );
1288
    }
1289
1290
    /**
1291
     * @param $newTrackingCode
1292
     * @param $currentTrackingCode
1293
     * @return string
1294
     */
1295
    private function combineTrackingNumbers($newTrackingCode, $currentTrackingCode)
1296
    {
1297
        $currentTrackingCodes = $this->getTrackingNumberAsArray($currentTrackingCode);
1298
        $newTrackingCodes = $this->getTrackingNumberAsArray($newTrackingCode);
1299
        $newTrackingCodes = array_unique(array_merge($currentTrackingCodes, $newTrackingCodes));
1300
        $newTracking = implode(',', $newTrackingCodes);
1301
1302
        return $newTracking;
1303
    }
1304
1305
    /**
1306
     * @param string $trackingCode
1307
     * @return string[]
1308
     */
1309
    private function getTrackingNumberAsArray($trackingCode)
1310
    {
1311
        if (strpos($trackingCode, ',') !== false) {
1312
            return explode(',', $trackingCode);
1313
        }
1314
1315
        return [$trackingCode];
1316
    }
1317
}
1318