Completed
Pull Request — master (#402)
by Jonas
02:51
created

ProductToShop::saveVat()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 2
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
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\Product;
15
use Shopware\Models\Article\Article as ProductModel;
16
use Shopware\Models\Article\Detail as DetailModel;
17
use Shopware\Models\Attribute\Article as AttributeModel;
18
use Shopware\Components\Model\ModelManager;
19
use Shopware\Connect\Struct\PriceRange;
20
use Shopware\Connect\Struct\ProductUpdate;
21
use Shopware\CustomModels\Connect\ProductStreamAttribute;
22
use Shopware\Models\Customer\Group;
23
use Shopware\Connect\Struct\Property;
24
use Shopware\Models\ProductStream\ProductStream;
25
use Shopware\Models\Property\Group as PropertyGroup;
26
use Shopware\Models\Property\Option as PropertyOption;
27
use Shopware\Models\Property\Value as PropertyValue;
28
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamRepository;
29
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
30
use ShopwarePlugins\Connect\Components\Translations\LocaleMapper;
31
use ShopwarePlugins\Connect\Components\Gateway\ProductTranslationsGateway;
32
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway;
33
use ShopwarePlugins\Connect\Components\Utils\UnitMapper;
34
use Shopware\CustomModels\Connect\Attribute as ConnectAttribute;
35
use Shopware\Models\Article\Image;
36
use Shopware\Models\Article\Supplier;
37
use Shopware\Models\Tax\Tax;
38
39
/**
40
 * The interface for products imported *from* connect *to* the local shop
41
 *
42
 * @category  Shopware
43
 * @package   Shopware\Plugins\SwagConnect
44
 */
45
class ProductToShop implements ProductToShopBase
46
{
47
    /**
48
     * @var Helper
49
     */
50
    private $helper;
51
52
    /**
53
     * @var ModelManager
54
     */
55
    private $manager;
56
57
    /**
58
     * @var \ShopwarePlugins\Connect\Components\Config
59
     */
60
    private $config;
61
62
    /**
63
     * @var ImageImport
64
     */
65
    private $imageImport;
66
67
    /**
68
     * @var \ShopwarePlugins\Connect\Components\VariantConfigurator
69
     */
70
    private $variantConfigurator;
71
72
    /**
73
     * @var MarketplaceGateway
74
     */
75
    private $marketplaceGateway;
76
77
    /**
78
     * @var ProductTranslationsGateway
79
     */
80
    private $productTranslationsGateway;
81
82
    /**
83
     * @var \Shopware\Models\Shop\Repository
84
     */
85
    private $shopRepository;
86
87
    private $localeRepository;
88
89
    /**
90
     * @var CategoryResolver
91
     */
92
    private $categoryResolver;
93
94
    /**
95
     * @var \Shopware\Connect\Gateway
96
     */
97
    private $connectGateway;
98
99
    /**
100
     * @var \Enlight_Event_EventManager
101
     */
102
    private $eventManager;
103
104
    /**
105
     * @var CategoryDenormalization
106
     */
107
    private $categoryDenormalization;
108
109
    /**
110
     * @param Helper $helper
111
     * @param ModelManager $manager
112
     * @param ImageImport $imageImport
113
     * @param \ShopwarePlugins\Connect\Components\Config $config
114
     * @param VariantConfigurator $variantConfigurator
115
     * @param \ShopwarePlugins\Connect\Components\Marketplace\MarketplaceGateway $marketplaceGateway
116
     * @param ProductTranslationsGateway $productTranslationsGateway
117
     * @param CategoryResolver $categoryResolver
118
     * @param Gateway $connectGateway
119
     * @param \Enlight_Event_EventManager $eventManager
120
     * @param CategoryDenormalization $categoryDenormalization
121
     */
122
    public function __construct(
123
        Helper $helper,
124
        ModelManager $manager,
125
        ImageImport $imageImport,
126
        Config $config,
127
        VariantConfigurator $variantConfigurator,
128
        MarketplaceGateway $marketplaceGateway,
129
        ProductTranslationsGateway $productTranslationsGateway,
130
        CategoryResolver $categoryResolver,
131
        Gateway $connectGateway,
132
        \Enlight_Event_EventManager $eventManager,
133
        CategoryDenormalization $categoryDenormalization
134
    ) {
135
        $this->helper = $helper;
136
        $this->manager = $manager;
137
        $this->config = $config;
138
        $this->imageImport = $imageImport;
139
        $this->variantConfigurator = $variantConfigurator;
140
        $this->marketplaceGateway = $marketplaceGateway;
141
        $this->productTranslationsGateway = $productTranslationsGateway;
142
        $this->categoryResolver = $categoryResolver;
143
        $this->connectGateway = $connectGateway;
144
        $this->eventManager = $eventManager;
145
        $this->categoryDenormalization = $categoryDenormalization;
146
    }
147
148
    /**
149
     * Start transaction
150
     *
151
     * Starts a transaction, which includes all insertOrUpdate and delete
152
     * operations, as well as the revision updates.
153
     *
154
     * @return void
155
     */
156
    public function startTransaction()
157
    {
158
        $this->manager->getConnection()->beginTransaction();
159
    }
160
161
    /**
162
     * Commit transaction
163
     *
164
     * Commits the transactions, once all operations are queued.
165
     *
166
     * @return void
167
     */
168
    public function commit()
169
    {
170
        $this->manager->getConnection()->commit();
171
    }
172
173
    /**
174
     * Import or update given product
175
     *
176
     * Store product in your shop database as an external product. The
177
     * associated sourceId
178
     *
179
     * @param Product $product
180
     */
181
    public function insertOrUpdate(Product $product)
182
    {
183
        /** @var Product $product */
184
        $product = $this->eventManager->filter(
185
            'Connect_ProductToShop_InsertOrUpdate_Before',
186
            $product
187
        );
188
189
        // todo@dn: Set dummy values and make product inactive
190
        if (empty($product->title) || empty($product->vendor)) {
191
            return;
192
        }
193
194
        if (!empty($product->sku)) {
195
            $number = 'SC-' . $product->shopId . '-' . $product->sku;
196
            $duplicatedDetail = $this->helper->getDetailByNumber($number);
197
            if ($duplicatedDetail
198
                && $this->helper->getConnectAttributeByModel($duplicatedDetail)->getSourceId() != $product->sourceId
199
            ) {
200
                $this->deleteDetail($duplicatedDetail);
201
            }
202
        } else {
203
            $number = 'SC-' . $product->shopId . '-' . $product->sourceId;
204
        }
205
206
        $detail = $this->helper->getArticleDetailModelByProduct($product);
207
        $detail = $this->eventManager->filter(
208
            'Connect_Merchant_Get_Article_Detail_After',
209
            $detail,
210
            [
211
                'product' => $product,
212
                'subject' => $this
213
            ]
214
        );
215
216
        $isMainVariant = false;
217
        if ($detail === null) {
218
            $active = $this->config->getConfig('activateProductsAutomatically', false) ? true : false;
219
            if ($product->groupId !== null) {
220
                $model = $this->helper->getArticleByRemoteProduct($product);
221
                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...
222
                    $model = $this->helper->createProductModel($product);
223
                    $model->setActive($active);
224
                    $isMainVariant = true;
225
                }
226
            } else {
227
                $model = $this->helper->getConnectArticleModel($product->sourceId, $product->shopId);
228
                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...
229
                    $model = $this->helper->createProductModel($product);
230
                    $model->setActive($active);
231
                }
232
            }
233
234
            $detail = new DetailModel();
235
            $detail->setActive($model->getActive());
236
            $this->manager->persist($detail);
237
            $detail->setArticle($model);
238
            $model->getDetails()->add($detail);
239
            if (!empty($product->variant)) {
240
                $this->variantConfigurator->configureVariantAttributes($product, $detail);
241
            }
242
        } else {
243
            $model = $detail->getArticle();
244
            // fix for isMainVariant flag
245
            // in connect attribute table
246
            $mainDetail = $model->getMainDetail();
247
            if ($detail->getId() === $mainDetail->getId()) {
248
                $isMainVariant = true;
249
            }
250
251
            if (empty($product->variant) && $model->getConfiguratorSet()) {
252
                $this->manager->getConnection()->executeQuery(
253
                    'UPDATE s_articles SET configurator_set_id = NULL WHERE id = ?',
254
                    [$model->getId()]
255
                );
256
            }
257
        }
258
259
        $detail->setNumber($number);
260
261
        /** @var \Shopware\Models\Category\Category $category */
262
        foreach ($model->getCategories() as $category) {
263
            $attribute = $category->getAttribute();
264
            if (!$attribute) {
265
                continue;
266
            }
267
268
            if ($attribute->getConnectImported()) {
269
                $model->removeCategory($category);
270
            }
271
        }
272
273
        $detailAttribute = $detail->getAttribute();
274
        if (!$detailAttribute) {
275
            $detailAttribute = new AttributeModel();
276
            $detail->setAttribute($detailAttribute);
277
            $model->setAttribute($detailAttribute);
278
            $detailAttribute->setArticle($model);
279
            $detailAttribute->setArticleDetail($detail);
280
        }
281
282
        $connectAttribute = $this->helper->getConnectAttributeByModel($detail) ?: new ConnectAttribute;
283
        // configure main variant and groupId
284
        if ($isMainVariant === true) {
285
            $connectAttribute->setIsMainVariant(true);
286
        }
287
        $connectAttribute->setGroupId($product->groupId);
288
289
        list($updateFields, $flag) = $this->getUpdateFields($model, $detail, $connectAttribute, $product);
290
        /*
291
         * Make sure, that the following properties are set for
292
         * - new products
293
         * - products that have been configured to receive these updates
294
         */
295
        if ($updateFields['name']) {
296
            $model->setName($product->title);
297
        }
298
        if ($updateFields['shortDescription']) {
299
            $model->setDescription($product->shortDescription);
300
        }
301
        if ($updateFields['longDescription']) {
302
            $model->setDescriptionLong($product->longDescription);
303
        }
304
305
        if ($updateFields['additionalDescription']) {
306
            $detailAttribute->setConnectProductDescription($product->additionalDescription);
307
        }
308
309
        $this->saveVat($product, $model);
310
311
        if ($product->vendor !== null) {
312
            $repo = $this->manager->getRepository('Shopware\Models\Article\Supplier');
313
            $supplier = $repo->findOneBy(['name' => $product->vendor]);
314
            if ($supplier === null) {
315
                $supplier = $this->createSupplier($product->vendor);
316
            }
317
            $model->setSupplier($supplier);
318
        }
319
320
        //set product properties
321
        $this->applyProductProperties($model, $product);
322
323
        // apply marketplace attributes
324
        $detailAttribute = $this->applyMarketplaceAttributes($detailAttribute, $product);
325
326
        $connectAttribute->setShopId($product->shopId);
327
        $connectAttribute->setSourceId($product->sourceId);
328
        $connectAttribute->setExportStatus(null);
329
        $connectAttribute->setPurchasePrice($product->purchasePrice);
330
        $connectAttribute->setFixedPrice($product->fixedPrice);
331
        $connectAttribute->setStream($product->stream);
332
333
        // store product categories to connect attribute
334
        $connectAttribute->setCategory($product->categories);
335
336
        $connectAttribute->setLastUpdateFlag($flag);
337
        // store purchasePriceHash and offerValidUntil
338
        $connectAttribute->setPurchasePriceHash($product->purchasePriceHash);
339
        $connectAttribute->setOfferValidUntil($product->offerValidUntil);
340
341
        $detail->setInStock($product->availability);
342
        $detail->setEan($product->ean);
343
        $detail->setShippingTime($product->deliveryWorkDays);
344
        $releaseDate = new \DateTime();
345
        $releaseDate->setTimestamp($product->deliveryDate);
346
        $detail->setReleaseDate($releaseDate);
347
        $detail->setMinPurchase($product->minPurchaseQuantity);
348
349
        // some shops have feature "sell not in stock",
350
        // then end customer should be able to by the product with stock = 0
351
        $shopConfiguration = $this->connectGateway->getShopConfiguration($product->shopId);
352
        if ($shopConfiguration && $shopConfiguration->sellNotInStock) {
353
            $model->setLastStock(false);
354
        } else {
355
            $model->setLastStock(true);
356
        }
357
358
        // if connect product has unit
359
        // find local unit with units mapping
360
        // and add to detail model
361
        if (array_key_exists('unit', $product->attributes) && $product->attributes['unit']) {
362
            $detailAttribute->setConnectRemoteUnit($product->attributes['unit']);
363
            if ($this->config->getConfig($product->attributes['unit']) == null) {
364
                $this->config->setConfig($product->attributes['unit'], '', null, 'units');
365
            }
366
367
            /** @var \ShopwarePlugins\Connect\Components\Utils\UnitMapper $unitMapper */
368
            $unitMapper = new UnitMapper($this->config, $this->manager);
369
370
            $shopwareUnit = $unitMapper->getShopwareUnit($product->attributes['unit']);
371
372
            /** @var \Shopware\Models\Article\Unit $unit */
373
            $unit = $this->helper->getUnit($shopwareUnit);
374
            $detail->setUnit($unit);
375
            $detail->setPurchaseUnit($product->attributes['quantity']);
376
            $detail->setReferenceUnit($product->attributes['ref_quantity']);
377
        } else {
378
            $detail->setUnit(null);
379
            $detail->setPurchaseUnit(null);
380
            $detail->setReferenceUnit(null);
381
        }
382
383
        // set dimension
384
        if (array_key_exists('dimension', $product->attributes) && $product->attributes['dimension']) {
385
            $dimension = explode('x', $product->attributes['dimension']);
386
            $detail->setLen($dimension[0]);
387
            $detail->setWidth($dimension[1]);
388
            $detail->setHeight($dimension[2]);
389
        } else {
390
            $detail->setLen(null);
391
            $detail->setWidth(null);
392
            $detail->setHeight(null);
393
        }
394
395
        // set weight
396
        if (array_key_exists('weight', $product->attributes) && $product->attributes['weight']) {
397
            $detail->setWeight($product->attributes['weight']);
398
        }
399
400
        //set package unit
401
        if (array_key_exists(Product::ATTRIBUTE_PACKAGEUNIT, $product->attributes)) {
402
            $detail->setPackUnit($product->attributes[Product::ATTRIBUTE_PACKAGEUNIT]);
403
        }
404
405
        //set basic unit
406
        if (array_key_exists(Product::ATTRIBUTE_BASICUNIT, $product->attributes)) {
407
            $detail->setMinPurchase($product->attributes[Product::ATTRIBUTE_BASICUNIT]);
408
        }
409
410
        //set manufacturer no.
411
        if (array_key_exists(Product::ATTRIBUTE_MANUFACTURERNUMBER, $product->attributes)) {
412
            $detail->setSupplierNumber($product->attributes[Product::ATTRIBUTE_MANUFACTURERNUMBER]);
413
        }
414
415
        // Whenever a product is updated, store a json encoded list of all fields that are updated optionally
416
        // This way a customer will be able to apply the most recent changes any time later
417
        $connectAttribute->setLastUpdate(json_encode([
418
            'shortDescription' => $product->shortDescription,
419
            'longDescription' => $product->longDescription,
420
            'additionalDescription' => $product->additionalDescription,
421
            'purchasePrice' => $product->purchasePrice,
422
            'image' => $product->images,
423
            'variantImages' => $product->variantImages,
424
            'price' => $product->price * ($product->vat + 1),
425
            'name' => $product->title,
426
            'vat' => $product->vat
427
        ]));
428
429
        if ($model->getMainDetail() === null) {
430
            $model->setMainDetail($detail);
431
        }
432
433
        if ($detail->getAttribute() === null) {
434
            $detail->setAttribute($detailAttribute);
435
            $detailAttribute->setArticle($model);
436
        }
437
438
        $connectAttribute->setArticle($model);
439
        $connectAttribute->setArticleDetail($detail);
440
441
        $this->eventManager->notify(
442
            'Connect_Merchant_Saving_ArticleAttribute_Before',
443
            [
444
                'subject' => $this,
445
                'connectAttribute' => $connectAttribute
446
            ]
447
        );
448
449
        $this->manager->persist($model);
450
        $this->manager->flush();
451
452
        $this->categoryResolver->storeRemoteCategories($product->categories, $model->getId());
453
        $categories = $this->categoryResolver->resolve($product->categories);
454
        if (count($categories) > 0) {
455
            $detailAttribute->setConnectMappedCategory(true);
456
        }
457
458
        $this->manager->persist($connectAttribute);
459
        $this->manager->persist($detail);
460
        //article has to be flushed
461
        $this->manager->persist($detailAttribute);
462
        $this->manager->flush();
463
464
        $this->categoryDenormalization->disableTransactions();
465
        foreach ($categories as $category) {
466
            $this->categoryDenormalization->addAssignment($model->getId(), $category);
467
            $this->manager->getConnection()->executeQuery(
468
                'INSERT IGNORE INTO `s_articles_categories` (`articleID`, `categoryID`) VALUES (?,?)',
469
                [$model->getId(),  $category]
470
            );
471
        }
472
        $this->categoryDenormalization->enableTransactions();
473
474
        $defaultCustomerGroup = $this->helper->getDefaultCustomerGroup();
475
        // Only set prices, if fixedPrice is active or price updates are configured
476
        if (count($detail->getPrices()) == 0 || $connectAttribute->getFixedPrice() || $updateFields['price']) {
477
            $this->setPrice($model, $detail, $product);
478
        }
479
        // If the price is not being update, update the purchasePrice anyway
480
        $this->setPurchasePrice($detail, $product->purchasePrice, $defaultCustomerGroup);
481
482
        $this->manager->clear();
483
484
        $this->addArticleTranslations($model, $product);
485
486
        //clear cache for that article
487
        $this->helper->clearArticleCache($model->getId());
488
489
        if ($updateFields['image']) {
490
            // Reload the model in order to not to work an the already flushed model
491
            $model = $this->helper->getArticleModelByProduct($product);
492
            // import only global images for article
493
            $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 491 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...
494
            // Reload the article detail model in order to not to work an the already flushed model
495
            $detail = $this->helper->getArticleDetailModelByProduct($product);
496
            // import only specific images for variant
497
            $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 495 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...
498
        }
499
500
        $this->eventManager->notify(
501
            'Connect_ProductToShop_InsertOrUpdate_After',
502
            [
503
                'connectProduct' => $product,
504
                'shopArticleDetail' => $detail
505
            ]
506
        );
507
508
        $stream = $this->getOrCreateStream($product);
509
        $this->addProductToStream($stream, $model);
510
    }
511
512
    /**
513
     * @param ProductModel $article
514
     * @param Product $product
515
     */
516
    private function applyProductProperties(ProductModel $article, Product $product)
517
    {
518
        if (empty($product->properties)) {
519
            return;
520
        }
521
522
        /** @var Property $firstProperty */
523
        $firstProperty = reset($product->properties);
524
        $groupRepo = $this->manager->getRepository(PropertyGroup::class);
525
        $group = $groupRepo->findOneBy(['name' => $firstProperty->groupName]);
526
527
        if (!$group) {
528
            $group = new PropertyGroup();
529
            $group->setName($firstProperty->groupName);
530
            $group->setComparable($firstProperty->comparable);
531
            $group->setSortMode($firstProperty->sortMode);
532
            $group->setPosition($firstProperty->groupPosition);
533
534
            $attribute = new \Shopware\Models\Attribute\PropertyGroup();
535
            $attribute->setPropertyGroup($group);
536
            $attribute->setConnectIsRemote(true);
537
            $group->setAttribute($attribute);
538
539
            $this->manager->persist($attribute);
540
            $this->manager->persist($group);
541
            $this->manager->flush();
542
        }
543
544
        $propertyValues = $article->getPropertyValues();
545
        $propertyValues->clear();
546
        $this->manager->persist($article);
547
        $this->manager->flush();
548
549
        $article->setPropertyGroup($group);
550
551
        $optionRepo = $this->manager->getRepository(PropertyOption::class);
552
        $valueRepo = $this->manager->getRepository(PropertyValue::class);
553
554
        foreach ($product->properties as $property) {
555
            $option = $optionRepo->findOneBy(['name' => $property->option]);
556
            $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...
557
            if (!$option) {
558
                $option = new PropertyOption();
559
                $option->setName($property->option);
560
                $option->setFilterable($property->filterable);
561
562
                $attribute = new \Shopware\Models\Attribute\PropertyOption();
563
                $attribute->setPropertyOption($option);
564
                $attribute->setConnectIsRemote(true);
565
                $option->setAttribute($attribute);
566
567
                $this->manager->persist($option);
568
                $this->manager->flush($option);
569
            }
570
571
            if (!$optionExists || !$value = $valueRepo->findOneBy(['option' => $option, 'value' => $property->value])) {
572
                $value = new PropertyValue($option, $property->value);
573
                $value->setPosition($property->valuePosition);
574
575
                $attribute = new \Shopware\Models\Attribute\PropertyValue();
576
                $attribute->setPropertyValue($value);
577
                $attribute->setConnectIsRemote(true);
578
                $value->setAttribute($attribute);
579
580
                $this->manager->persist($value);
581
            }
582
583
            if (!$propertyValues->contains($value)) {
584
                //add only new values
585
                $propertyValues->add($value);
586
            }
587
588
            $filters = [
589
                ['property' => 'options.name', 'expression' => '=', 'value' => $property->option],
590
                ['property' => 'groups.name', 'expression' => '=', 'value' => $property->groupName],
591
            ];
592
593
            $query = $groupRepo->getPropertyRelationQuery($filters, null, 1, 0);
594
            $relation = $query->getOneOrNullResult();
595
596
            if (!$relation) {
597
                $group->addOption($option);
598
                $this->manager->persist($group);
599
                $this->manager->flush($group);
600
            }
601
        }
602
603
        $article->setPropertyValues($propertyValues);
604
605
        $this->manager->persist($article);
606
        $this->manager->flush();
607
    }
608
609
    /**
610
     * @param Product $product
611
     * @return ProductStream
612
     */
613
    private function getOrCreateStream(Product $product)
614
    {
615
        /** @var ProductStreamRepository $repo */
616
        $repo = $this->manager->getRepository(ProductStreamAttribute::class);
617
        $stream = $repo->findConnectByName($product->stream);
618
619
        if (!$stream) {
620
            $stream = new ProductStream();
621
            $stream->setName($product->stream);
622
            $stream->setType(ProductStreamService::STATIC_STREAM);
623
            $stream->setSorting(json_encode(
624
                [ReleaseDateSorting::class => ['direction' => 'desc']]
625
            ));
626
627
            //add attributes
628
            $attribute = new \Shopware\Models\Attribute\ProductStream();
629
            $attribute->setProductStream($stream);
630
            $attribute->setConnectIsRemote(true);
631
            $stream->setAttribute($attribute);
632
633
            $this->manager->persist($attribute);
634
            $this->manager->persist($stream);
635
            $this->manager->flush();
636
        }
637
638
        return $stream;
639
    }
640
641
    /**
642
     * @param ProductStream $stream
643
     * @param ProductModel $article
644
     * @throws \Doctrine\DBAL\DBALException
645
     */
646
    private function addProductToStream(ProductStream $stream, ProductModel $article)
647
    {
648
        $conn = $this->manager->getConnection();
649
        $sql = 'INSERT INTO `s_product_streams_selection` (`stream_id`, `article_id`)
650
                VALUES (:streamId, :articleId)
651
                ON DUPLICATE KEY UPDATE stream_id = :streamId, article_id = :articleId';
652
        $stmt = $conn->prepare($sql);
653
        $stmt->execute([':streamId' => $stream->getId(), ':articleId' => $article->getId()]);
654
    }
655
656
    /**
657
     * Set detail purchase price with plain SQL
658
     * Entity usage throws exception when error handlers are disabled
659
     *
660
     * @param ProductModel $article
661
     * @param DetailModel $detail
662
     * @param Product $product
663
     * @throws \Doctrine\DBAL\DBALException
664
     */
665
    private function setPrice(ProductModel $article, DetailModel $detail, Product $product)
666
    {
667
        // set price via plain SQL because shopware throws exception
668
        // undefined index: key when error handler is disabled
669
        $customerGroup = $this->helper->getDefaultCustomerGroup();
670
671
        if (!empty($product->priceRanges)) {
672
            $this->setPriceRange($article, $detail, $product->priceRanges, $customerGroup);
673
674
            return;
675
        }
676
677
        $id = $this->manager->getConnection()->fetchColumn(
678
            'SELECT id FROM `s_articles_prices`
679
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
680
            [$customerGroup->getKey(), 1, 'beliebig', $article->getId(), $detail->getId()]
681
        );
682
683
        // todo@sb: test update prices
684
        if ($id > 0) {
685
            $this->manager->getConnection()->executeQuery(
686
                'UPDATE `s_articles_prices` SET `price` = ?, `baseprice` = ? WHERE `id` = ?',
687
                [$product->price, $product->purchasePrice, $id]
688
            );
689
        } else {
690
            $this->manager->getConnection()->executeQuery(
691
                'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`, `baseprice`)
692
              VALUES (?, 1, "beliebig", ?, ?, ?, ?);',
693
                [$customerGroup->getKey(), $article->getId(), $detail->getId(), $product->price, $product->purchasePrice]
694
            );
695
        }
696
    }
697
698
    /**
699
     * @param ProductModel $article
700
     * @param DetailModel $detail
701
     * @param array $priceRanges
702
     * @param Group $group
703
     * @throws \Doctrine\DBAL\ConnectionException
704
     * @throws \Exception
705
     */
706
    private function setPriceRange(ProductModel $article, DetailModel $detail, array $priceRanges, Group $group)
707
    {
708
        $this->manager->getConnection()->beginTransaction();
709
710
        try {
711
            // We always delete the prices,
712
            // because we can not know which record is update
713
            $this->manager->getConnection()->executeQuery(
714
                'DELETE FROM `s_articles_prices` WHERE `articleID` = ? AND `articledetailsID` = ?',
715
                [$article->getId(), $detail->getId()]
716
            );
717
718
            /** @var PriceRange $priceRange */
719
            foreach ($priceRanges as $priceRange) {
720
                $priceTo = $priceRange->to == PriceRange::ANY ? 'beliebig' : $priceRange->to;
721
722
                //todo: maybe batch insert if possible?
723
                $this->manager->getConnection()->executeQuery(
724
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `price`)
725
                      VALUES (?, ?, ?, ?, ?, ?);',
726
                    [
727
                        $group->getKey(),
728
                        $priceRange->from,
729
                        $priceTo,
730
                        $article->getId(),
731
                        $detail->getId(),
732
                        $priceRange->price
733
                    ]
734
                );
735
            }
736
            $this->manager->getConnection()->commit();
737
        } catch (\Exception $e) {
738
            $this->manager->getConnection()->rollBack();
739
            throw new \Exception($e->getMessage());
740
        }
741
    }
742
743
    /**
744
     * Adds translation record for given article
745
     *
746
     * @param ProductModel $article
747
     * @param Product $sdkProduct
748
     */
749
    private function addArticleTranslations(ProductModel $article, Product $sdkProduct)
750
    {
751
        /** @var \Shopware\Connect\Struct\Translation $translation */
752
        foreach ($sdkProduct->translations as $key => $translation) {
753
            /** @var \Shopware\Models\Shop\Locale $locale */
754
            $locale = $this->getLocaleRepository()->findOneBy(['locale' => LocaleMapper::getShopwareLocale($key)]);
755
            /** @var \Shopware\Models\Shop\Shop $shop */
756
            $shop = $this->getShopRepository()->findOneBy(['locale' => $locale]);
757
            if (!$shop) {
758
                continue;
759
            }
760
761
            $this->productTranslationsGateway->addArticleTranslation($translation, $article->getId(), $shop->getId());
762
        }
763
    }
764
765
    /**
766
     * dsadsa
767
     * @return \Shopware\Components\Model\ModelRepository
768
     */
769
    private function getLocaleRepository()
770
    {
771
        if (!$this->localeRepository) {
772
            $this->localeRepository = $this->manager->getRepository('Shopware\Models\Shop\Locale');
773
        }
774
775
        return $this->localeRepository;
776
    }
777
778
    private function getShopRepository()
779
    {
780
        if (!$this->shopRepository) {
781
            $this->shopRepository = $this->manager->getRepository('Shopware\Models\Shop\Shop');
782
        }
783
784
        return $this->shopRepository;
785
    }
786
787
    /**
788
     * Delete product or product variant with given shopId and sourceId.
789
     *
790
     * Only the combination of both identifies a product uniquely. Do NOT
791
     * delete products just by their sourceId.
792
     *
793
     * You might receive delete requests for products, which are not available
794
     * in your shop. Just ignore them.
795
     *
796
     * @param string $shopId
797
     * @param string $sourceId
798
     * @return void
799
     */
800
    public function delete($shopId, $sourceId)
801
    {
802
        $detail = $this->helper->getArticleDetailModelByProduct(new Product([
803
            'shopId' => $shopId,
804
            'sourceId' => $sourceId,
805
        ]));
806
        if ($detail === null) {
807
            return;
808
        }
809
810
        $this->deleteDetail($detail);
811
    }
812
813
    /**
814
     * @param DetailModel $detailModel
815
     */
816
    private function deleteDetail(DetailModel $detailModel)
817
    {
818
        $this->eventManager->notify(
819
            'Connect_Merchant_Delete_Product_Before',
820
            [
821
                'subject' => $this,
822
                'articleDetail' => $detailModel
823
            ]
824
        );
825
826
        $article = $detailModel->getArticle();
827
        // Not sure why, but the Attribute can be NULL
828
        $attribute = $this->helper->getConnectAttributeByModel($detailModel);
829
        $this->manager->remove($detailModel);
830
831
        if ($attribute) {
832
            $this->manager->remove($attribute);
833
        }
834
835
        // if removed variant is main variant
836
        // find first variant which is not main and mark it
837
        if ($detailModel->getKind() === 1) {
838
            /** @var \Shopware\Models\Article\Detail $variant */
839
            foreach ($article->getDetails() as $variant) {
840
                if ($variant->getId() != $detailModel->getId()) {
841
                    $variant->setKind(1);
842
                    $article->setMainDetail($variant);
843
                    $connectAttribute = $this->helper->getConnectAttributeByModel($variant);
844
                    if (!$connectAttribute) {
845
                        continue;
846
                    }
847
                    $connectAttribute->setIsMainVariant(true);
848
                    $this->manager->persist($connectAttribute);
849
                    $this->manager->persist($article);
850
                    $this->manager->persist($variant);
851
                    break;
852
                }
853
            }
854
        }
855
856
        if (count($details = $article->getDetails()) === 1) {
857
            $details->clear();
858
            $this->manager->remove($article);
859
        }
860
861
        // Do not remove flush. It's needed when remove article,
862
        // because duplication of ordernumber. Even with remove before
863
        // persist calls mysql throws exception "Duplicate entry"
864
        $this->manager->flush();
865
        // always clear entity manager, because $article->getDetails() returns
866
        // more than 1 detail, but all of them were removed except main one.
867
        $this->manager->clear();
868
    }
869
870
    /**
871
     * Get array of update info for the known fields
872
     *
873
     * @param $model
874
     * @param $detail
875
     * @param $attribute
876
     * @param $product
877
     * @return array
878
     */
879
    public function getUpdateFields($model, $detail, $attribute, $product)
880
    {
881
        // This also defines the flags of these fields
882
        $fields = $this->helper->getUpdateFlags();
883
        $flagsByName = array_flip($fields);
884
885
        $flag = 0;
886
        $output = [];
887
        foreach ($fields as $key => $field) {
888
            // Don't handle the imageInitialImport flag
889
            if ($field == 'imageInitialImport') {
890
                continue;
891
            }
892
893
            // If this is a new product
894
            if (!$model->getId() && $field == 'image' && !$this->config->getConfig('importImagesOnFirstImport', false)) {
895
                $output[$field] = false;
896
                $flag |= $flagsByName['imageInitialImport'];
897
                continue;
898
            }
899
900
            $updateAllowed = $this->isFieldUpdateAllowed($field, $model, $attribute);
901
            $output[$field] = $updateAllowed;
902
            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...
903
                $flag |= $key;
904
            }
905
        }
906
907
        return [$output, $flag];
908
    }
909
910
    /**
911
     * Determine if a given field has changed
912
     *
913
     * @param $field
914
     * @param ProductModel $model
915
     * @param DetailModel $detail
916
     * @param Product $product
917
     * @return bool
918
     */
919
    public function hasFieldChanged($field, ProductModel $model, DetailModel $detail, Product $product)
920
    {
921
        switch ($field) {
922
            case 'shortDescription':
923
                return $model->getDescription() != $product->shortDescription;
924
            case 'longDescription':
925
                return $model->getDescriptionLong() != $product->longDescription;
926
            case 'additionalDescription':
927
                return $detail->getAttribute()->getConnectProductDescription() != $product->additionalDescription;
928
            case 'name':
929
                return $model->getName() != $product->title;
930
            case 'image':
931
                return count($model->getImages()) != count($product->images);
932
            case 'price':
933
                $prices = $detail->getPrices();
934
                if (empty($prices)) {
935
                    return true;
936
                }
937
                $price = $prices->first();
938
                if (!$price) {
939
                    return true;
940
                }
941
942
                return $prices->first()->getPrice() != $product->price;
943
        }
944
945
        throw new \InvalidArgumentException('Unrecognized field');
946
    }
947
948
    /**
949
     * Helper method to determine if a given $fields may/must be updated.
950
     * This method will check for the model->id in order to determine, if it is a new entity. Therefore
951
     * this method cannot be used after the model in question was already flushed.
952
     *
953
     * @param $field
954
     * @param $model ProductModel
955
     * @param $attribute ConnectAttribute
956
     * @throws \RuntimeException
957
     * @return bool|null
958
     */
959
    public function isFieldUpdateAllowed($field, ProductModel $model, ConnectAttribute $attribute)
960
    {
961
        $allowed = [
962
            'ShortDescription',
963
            'LongDescription',
964
            'AdditionalDescription',
965
            'Image',
966
            'Price',
967
            'Name',
968
        ];
969
970
        // Always allow updates for new models
971
        if (!$model->getId()) {
972
            return true;
973
        }
974
975
        $field = ucfirst($field);
976
        $attributeGetter = 'getUpdate' . $field;
977
        $configName = 'overwriteProduct' . $field;
978
979
        if (!in_array($field, $allowed)) {
980
            throw new \RuntimeException("Unknown update field {$field}");
981
        }
982
983
        $attributeValue = $attribute->$attributeGetter();
984
985
986
987
        // If the value is 'null' or 'inherit', the behaviour will be inherited from the global configuration
988
        // Once we have a supplier based configuration, we need to take it into account here
989
        if ($attributeValue == null || $attributeValue == 'inherit') {
990
            return $this->config->getConfig($configName, true);
991
        }
992
993
        return $attributeValue == 'overwrite';
994
    }
995
996
    /**
997
     * Read product attributes mapping and set to shopware attribute model
998
     *
999
     * @param AttributeModel $detailAttribute
1000
     * @param Product $product
1001
     * @return AttributeModel
1002
     */
1003
    private function applyMarketplaceAttributes(AttributeModel $detailAttribute, Product $product)
1004
    {
1005
        $detailAttribute->setConnectReference($product->sourceId);
1006
        $detailAttribute->setConnectArticleShipping($product->shipping);
1007
        //todo@sb: check if connectAttribute matches position of the marketplace attribute
1008
        array_walk($product->attributes, function ($value, $key) use ($detailAttribute) {
1009
            $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...
1010
            if (strlen($shopwareAttribute) > 0) {
1011
                $setter = 'set' . ucfirst($shopwareAttribute);
1012
                $detailAttribute->$setter($value);
1013
            }
1014
        });
1015
1016
        return $detailAttribute;
1017
    }
1018
1019
    /**
1020
     * @param $vendor
1021
     * @return Supplier
1022
     */
1023
    private function createSupplier($vendor)
1024
    {
1025
        $supplier = new Supplier();
1026
1027
        if (is_array($vendor)) {
1028
            $supplier->setName($vendor['name']);
1029
            $supplier->setDescription($vendor['description']);
1030
            if (array_key_exists('url', $vendor) && $vendor['url']) {
1031
                $supplier->setLink($vendor['url']);
1032
            }
1033
1034
            $supplier->setMetaTitle($vendor['page_title']);
1035
1036
            if (array_key_exists('logo_url', $vendor) && $vendor['logo_url']) {
1037
                $this->imageImport->importImageForSupplier($vendor['logo_url'], $supplier);
1038
            }
1039
        } else {
1040
            $supplier->setName($vendor);
1041
        }
1042
1043
        //sets supplier attributes
1044
        $attr = new \Shopware\Models\Attribute\ArticleSupplier();
1045
        $attr->setConnectIsRemote(true);
1046
1047
        $supplier->setAttribute($attr);
1048
1049
        return $supplier;
1050
    }
1051
1052
    /**
1053
     * Set detail purchase price with plain SQL
1054
     * Entity usage throws exception when error handlers are disabled
1055
     *
1056
     * @param DetailModel $detail
1057
     * @param float $purchasePrice
1058
     * @param Group $defaultGroup
1059
     * @throws \Doctrine\DBAL\DBALException
1060
     */
1061
    private function setPurchasePrice(DetailModel $detail, $purchasePrice, Group $defaultGroup)
1062
    {
1063
        if (method_exists($detail, 'setPurchasePrice')) {
1064
            $this->manager->getConnection()->executeQuery(
1065
                    'UPDATE `s_articles_details` SET `purchaseprice` = ? WHERE `id` = ?',
1066
                    [$purchasePrice, $detail->getId()]
1067
                );
1068
        } else {
1069
            $id = $this->manager->getConnection()->fetchColumn(
1070
                'SELECT id FROM `s_articles_prices`
1071
              WHERE `pricegroup` = ? AND `from` = ? AND `to` = ? AND `articleID` = ? AND `articledetailsID` = ?',
1072
                [$defaultGroup->getKey(), 1, 'beliebig', $detail->getArticleId(), $detail->getId()]
1073
            );
1074
1075
            if ($id > 0) {
1076
                $this->manager->getConnection()->executeQuery(
1077
                    'UPDATE `s_articles_prices` SET `baseprice` = ? WHERE `id` = ?',
1078
                    [$purchasePrice, $id]
1079
                );
1080
            } else {
1081
                $this->manager->getConnection()->executeQuery(
1082
                    'INSERT INTO `s_articles_prices`(`pricegroup`, `from`, `to`, `articleID`, `articledetailsID`, `baseprice`)
1083
              VALUES (?, 1, "beliebig", ?, ?, ?);',
1084
                    [$defaultGroup->getKey(), $detail->getArticleId(), $detail->getId(), $purchasePrice]
1085
                );
1086
            }
1087
        }
1088
    }
1089
1090
    public function update($shopId, $sourceId, ProductUpdate $product)
1091
    {
1092
        // find article detail id
1093
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1094
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1095
            [$sourceId, $shopId]
1096
        );
1097
1098
        $this->eventManager->notify(
1099
            'Connect_Merchant_Update_GeneralProductInformation',
1100
            [
1101
                'subject' => $this,
1102
                'shopId' => $shopId,
1103
                'sourceId' => $sourceId,
1104
                'articleDetailId' => $articleDetailId
1105
            ]
1106
        );
1107
1108
        // update purchasePriceHash, offerValidUntil and purchasePrice in connect attribute
1109
        $this->manager->getConnection()->executeUpdate(
1110
            'UPDATE s_plugin_connect_items SET purchase_price_hash = ?, offer_valid_until = ?, purchase_price = ?
1111
            WHERE source_id = ? AND shop_id = ?',
1112
            [
1113
                $product->purchasePriceHash,
1114
                $product->offerValidUntil,
1115
                $product->purchasePrice,
1116
                $sourceId,
1117
                $shopId,
1118
            ]
1119
        );
1120
1121
        // update stock in article detail
1122
        // update prices
1123
        // if purchase price is stored in article detail
1124
        // update it together with stock
1125
        // since shopware 5.2
1126
        if (method_exists('Shopware\Models\Article\Detail', 'getPurchasePrice')) {
1127
            $this->manager->getConnection()->executeUpdate(
1128
                'UPDATE s_articles_details SET instock = ?, purchaseprice = ? WHERE id = ?',
1129
                [$product->availability, $product->purchasePrice, $articleDetailId]
1130
            );
1131
        } else {
1132
            $this->manager->getConnection()->executeUpdate(
1133
                'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1134
                [$product->availability, $articleDetailId]
1135
            );
1136
        }
1137
        $this->manager->getConnection()->executeUpdate(
1138
            "UPDATE s_articles_prices SET price = ?, baseprice = ? WHERE articledetailsID = ? AND pricegroup = 'EK'",
1139
            [$product->price, $product->purchasePrice, $articleDetailId]
1140
        );
1141
    }
1142
1143
    public function changeAvailability($shopId, $sourceId, $availability)
1144
    {
1145
        // find article detail id
1146
        $articleDetailId = $this->manager->getConnection()->fetchColumn(
1147
            'SELECT article_detail_id FROM s_plugin_connect_items WHERE source_id = ? AND shop_id = ?',
1148
            [$sourceId, $shopId]
1149
        );
1150
1151
        $this->eventManager->notify(
1152
            'Connect_Merchant_Update_GeneralProductInformation',
1153
            [
1154
                'subject' => $this,
1155
                'shopId' => $shopId,
1156
                'sourceId' => $sourceId,
1157
                'articleDetailId' => $articleDetailId
1158
            ]
1159
        );
1160
1161
        // update stock in article detail
1162
        $this->manager->getConnection()->executeUpdate(
1163
            'UPDATE s_articles_details SET instock = ? WHERE id = ?',
1164
            [$availability, $articleDetailId]
1165
        );
1166
    }
1167
1168
    /**
1169
     * @inheritDoc
1170
     */
1171
    public function makeMainVariant($shopId, $sourceId, $groupId)
1172
    {
1173
        //find article detail which should be selected as main one
1174
        $newMainDetail = $this->helper->getConnectArticleDetailModel($sourceId, $shopId);
1175
        if (!$newMainDetail) {
1176
            return;
1177
        }
1178
1179
        /** @var \Shopware\Models\Article\Article $article */
1180
        $article = $newMainDetail->getArticle();
1181
1182
        $this->eventManager->notify(
1183
            'Connect_Merchant_Update_ProductMainVariant_Before',
1184
            [
1185
                'subject' => $this,
1186
                'shopId' => $shopId,
1187
                'sourceId' => $sourceId,
1188
                'articleId' => $article->getId(),
1189
                'articleDetailId' => $newMainDetail->getId()
1190
            ]
1191
        );
1192
1193
        // replace current main detail with new one
1194
        $currentMainDetail = $article->getMainDetail();
1195
        $currentMainDetail->setKind(2);
1196
        $newMainDetail->setKind(1);
1197
        $article->setMainDetail($newMainDetail);
1198
1199
        $this->manager->persist($newMainDetail);
1200
        $this->manager->persist($currentMainDetail);
1201
        $this->manager->persist($article);
1202
        $this->manager->flush();
1203
    }
1204
1205
    /**
1206
     * @param Product $product
1207
     * @param ProductModel $model
1208
     */
1209
    private function saveVat(Product $product, $model)
1210
    {
1211
        if ($product->vat !== null) {
1212
            $repo = $this->manager->getRepository('Shopware\Models\Tax\Tax');
1213
            $taxRate = round($product->vat * 100, 2);
1214
            /** @var \Shopware\Models\Tax\Tax $tax */
1215
            $tax = $repo->findOneBy(['tax' => $taxRate]);
1216
            if (!$tax) {
1217
                $tax = new Tax();
1218
                $tax->setTax($taxRate);
1219
                //this is to get rid of zeroes behind the decimal point
1220
                $name = strval(round($taxRate, 2)) . '%';
1221
                $tax->setName($name);
1222
                $this->manager->persist($tax);
1223
            }
1224
            $model->setTax($tax);
1225
        }
1226
    }
1227
}
1228