AvailabilityHandler::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 8
dl 0
loc 18
rs 10
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
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Zed\Availability\Business\Model;
9
10
use Generated\Shared\Transfer\AvailabilityNotificationDataTransfer;
11
use Generated\Shared\Transfer\DynamicEntityPostEditRequestTransfer;
12
use Generated\Shared\Transfer\DynamicEntityPostEditResponseTransfer;
13
use Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer;
14
use Generated\Shared\Transfer\ProductAvailabilityDataTransfer;
15
use Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer;
16
use Generated\Shared\Transfer\StockStoreCriteriaTransfer;
17
use Generated\Shared\Transfer\StoreTransfer;
18
use Spryker\DecimalObject\Decimal;
19
use Spryker\Shared\Availability\AvailabilityConfig;
20
use Spryker\Zed\Availability\Business\Exception\ProductNotFoundException;
21
use Spryker\Zed\Availability\Dependency\AvailabilityEvents;
22
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToEventFacadeInterface;
23
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToProductFacadeInterface;
24
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStockFacadeInterface;
25
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface;
26
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToTouchFacadeInterface;
27
use Spryker\Zed\Availability\Persistence\AvailabilityEntityManagerInterface;
28
use Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface;
29
30
class AvailabilityHandler implements AvailabilityHandlerInterface
31
{
32
    /**
33
     * @var string
34
     */
35
    protected const PRODUCT_SKU_NOT_FOUND_EXCEPTION_MESSAGE_FORMAT = 'The product was not found with this SKU: %s';
36
37
    /**
38
     * @var string
39
     */
40
    protected const PRODUCT_ID_NOT_FOUND_EXCEPTION_MESSAGE_FORMAT = 'The product was not found with this ID: %d';
41
42
    /**
43
     * @var string
44
     */
45
    protected const TABLE_NAME = 'spy_stock_product';
46
47
    /**
48
     * @var string
49
     */
50
    protected const FK_PRODUCT = 'fk_product';
51
52
    /**
53
     * @var list<\Generated\Shared\Transfer\StoreTransfer>
54
     */
55
    protected static $allStoreTransfersCache = [];
56
57
    /**
58
     * @var \Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface
59
     */
60
    protected $availabilityRepository;
61
62
    /**
63
     * @var \Spryker\Zed\Availability\Persistence\AvailabilityEntityManagerInterface
64
     */
65
    protected $availabilityEntityManager;
66
67
    /**
68
     * @var \Spryker\Zed\Availability\Business\Model\ProductAvailabilityCalculatorInterface
69
     */
70
    protected $availabilityCalculator;
71
72
    /**
73
     * @var \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToTouchFacadeInterface
74
     */
75
    protected $touchFacade;
76
77
    /**
78
     * @var \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStockFacadeInterface
79
     */
80
    protected $stockFacade;
81
82
    /**
83
     * @var \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToEventFacadeInterface
84
     */
85
    protected $eventFacade;
86
87
    /**
88
     * @var \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToProductFacadeInterface
89
     */
90
    protected AvailabilityToProductFacadeInterface $productFacade;
91
92
    /**
93
     * @var \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface $storeFacade
94
     */
95
    protected AvailabilityToStoreFacadeInterface $storeFacade;
96
97
    /**
98
     * @param \Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface $availabilityRepository
99
     * @param \Spryker\Zed\Availability\Persistence\AvailabilityEntityManagerInterface $availabilityEntityManager
100
     * @param \Spryker\Zed\Availability\Business\Model\ProductAvailabilityCalculatorInterface $availabilityCalculator
101
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToTouchFacadeInterface $touchFacade
102
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStockFacadeInterface $stockFacade
103
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToEventFacadeInterface $eventFacade
104
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToProductFacadeInterface $productFacade
105
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface $storeFacade
106
     */
107
    public function __construct(
108
        AvailabilityRepositoryInterface $availabilityRepository,
109
        AvailabilityEntityManagerInterface $availabilityEntityManager,
110
        ProductAvailabilityCalculatorInterface $availabilityCalculator,
111
        AvailabilityToTouchFacadeInterface $touchFacade,
112
        AvailabilityToStockFacadeInterface $stockFacade,
113
        AvailabilityToEventFacadeInterface $eventFacade,
114
        AvailabilityToProductFacadeInterface $productFacade,
115
        AvailabilityToStoreFacadeInterface $storeFacade
116
    ) {
117
        $this->availabilityCalculator = $availabilityCalculator;
118
        $this->availabilityRepository = $availabilityRepository;
119
        $this->availabilityEntityManager = $availabilityEntityManager;
120
        $this->touchFacade = $touchFacade;
121
        $this->stockFacade = $stockFacade;
122
        $this->eventFacade = $eventFacade;
123
        $this->productFacade = $productFacade;
124
        $this->storeFacade = $storeFacade;
125
    }
126
127
    /**
128
     * @param string $concreteSku
129
     *
130
     * @return void
131
     */
132
    public function updateAvailability(string $concreteSku): void
133
    {
134
        $productAvailabilityDataTransfer = $this->availabilityRepository->getProductConcreteWithAvailability($concreteSku);
135
136
        $this->saveAllAvailabilities($productAvailabilityDataTransfer);
137
    }
138
139
    /**
140
     * @param string $concreteSku
141
     * @param \Spryker\DecimalObject\Decimal $quantity
142
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
143
     *
144
     * @return int
145
     */
146
    public function saveAndTouchAvailability(string $concreteSku, Decimal $quantity, StoreTransfer $storeTransfer): int
147
    {
148
        $wasProductConcreteAvailable = $this->isProductConcreteAvailable(
149
            $this->availabilityRepository->findProductConcreteAvailabilityBySkuAndStore($concreteSku, $storeTransfer),
150
        );
151
152
        /** @var string $productAbstractSku */
153
        $productAbstractSku = $this->availabilityRepository->getAbstractSkuFromProductConcrete($concreteSku);
154
        $productConcreteAvailabilityTransfer = (new ProductConcreteAvailabilityTransfer())
155
            ->setSku($concreteSku)
156
            ->setIsNeverOutOfStock($this->availabilityCalculator->isNeverOutOfStockForStore($concreteSku, $storeTransfer))
157
            ->setAvailability($quantity);
158
159
        $this->updateProductAbstractAvailabilityBySku($productAbstractSku, $storeTransfer);
160
        $idAvailabilityAbstract = $this->availabilityRepository
161
            ->findIdProductAbstractAvailabilityBySku($productAbstractSku, $storeTransfer);
162
163
        $isAvailabilityChanged = $this->availabilityEntityManager->saveProductConcreteAvailability(
164
            $productConcreteAvailabilityTransfer,
165
            $storeTransfer,
166
            $productAbstractSku,
167
        );
168
169
        if ($isAvailabilityChanged) {
170
            $this->touchAvailabilityAbstract($idAvailabilityAbstract);
171
        }
172
173
        if ($isAvailabilityChanged && !$wasProductConcreteAvailable && $this->isProductConcreteAvailable($productConcreteAvailabilityTransfer)) {
174
            $this->triggerProductIsAvailableAgainEvent($concreteSku, $storeTransfer);
175
        }
176
177
        return $idAvailabilityAbstract;
178
    }
179
180
    /**
181
     * @param int $idAvailabilityAbstract
182
     *
183
     * @return void
184
     */
185
    public function touchAvailabilityAbstract($idAvailabilityAbstract)
186
    {
187
        $this->touchFacade->touchActive(AvailabilityConfig::RESOURCE_TYPE_AVAILABILITY_ABSTRACT, $idAvailabilityAbstract);
188
    }
189
190
    /**
191
     * @param int $idProductConcrete
192
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
193
     *
194
     * @return \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer
195
     */
196
    public function updateProductConcreteAvailabilityById(
197
        int $idProductConcrete,
198
        StoreTransfer $storeTransfer
199
    ): ProductConcreteAvailabilityTransfer {
200
        $concreteSku = $this->getProductConcreteSkuByConcreteId($idProductConcrete);
201
202
        return $this->updateProductConcreteAvailabilityBySku($concreteSku, $storeTransfer);
203
    }
204
205
    /**
206
     * @param string $concreteSku
207
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
208
     *
209
     * @return \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer
210
     */
211
    public function updateProductConcreteAvailabilityBySku(
212
        string $concreteSku,
213
        StoreTransfer $storeTransfer
214
    ): ProductConcreteAvailabilityTransfer {
215
        $productAbstractSku = $this->getAbstractSkuFromProductConcrete($concreteSku);
216
217
        $wasProductConcreteAvailable = $this->isProductConcreteAvailable(
218
            $this->availabilityRepository->findProductConcreteAvailabilityBySkuAndStore($concreteSku, $storeTransfer),
219
        );
220
221
        $productConcreteAvailabilityTransfer = $this->availabilityCalculator
222
            ->getCalculatedProductConcreteAvailabilityTransfer($concreteSku, $storeTransfer);
223
224
        $this->updateProductAbstractAvailabilityBySku($productAbstractSku, $storeTransfer);
225
226
        $isAvailabilityChanged = $this->availabilityEntityManager->saveProductConcreteAvailability(
227
            $productConcreteAvailabilityTransfer,
228
            $storeTransfer,
229
            $productAbstractSku,
230
        );
231
232
        /** @var string $sku */
233
        $sku = $productConcreteAvailabilityTransfer->requireSku()->getSku();
234
        if ($isAvailabilityChanged && !$wasProductConcreteAvailable && $this->isProductConcreteAvailable($productConcreteAvailabilityTransfer)) {
235
            $this->triggerProductIsAvailableAgainEvent($sku, $storeTransfer);
236
        }
237
238
        return $productConcreteAvailabilityTransfer;
239
    }
240
241
    /**
242
     * @param string $productAbstractSku
243
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
244
     *
245
     * @return \Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer
246
     */
247
    public function updateProductAbstractAvailabilityBySku(
248
        string $productAbstractSku,
249
        StoreTransfer $storeTransfer
250
    ): ProductAbstractAvailabilityTransfer {
251
        $productAbstractAvailabilityTransfer = $this->availabilityCalculator
252
            ->getCalculatedProductAbstractAvailabilityTransfer($productAbstractSku, $storeTransfer);
253
254
        $this->availabilityEntityManager->saveProductAbstractAvailability(
255
            $productAbstractAvailabilityTransfer,
256
            $storeTransfer,
257
        );
258
259
        return $productAbstractAvailabilityTransfer;
260
    }
261
262
    /**
263
     * @param \Generated\Shared\Transfer\DynamicEntityPostEditRequestTransfer $dynamicEntityPostEditRequestTransfer
264
     *
265
     * @return \Generated\Shared\Transfer\DynamicEntityPostEditResponseTransfer
266
     */
267
    public function updateAvailabilityByDynamicEntityRequest(
268
        DynamicEntityPostEditRequestTransfer $dynamicEntityPostEditRequestTransfer
269
    ): DynamicEntityPostEditResponseTransfer {
270
        if ($dynamicEntityPostEditRequestTransfer->getTableNameOrFail() !== static::TABLE_NAME) {
271
            return $this->createDynamicEntityPostEditResponseTransfer();
272
        }
273
274
        $productIds = $this->getProductIdsFromDynamicEntityRequest($dynamicEntityPostEditRequestTransfer);
275
        $productConcreteSkus = $this->productFacade->getProductConcreteSkusByConcreteIds($productIds);
276
        foreach (array_keys($productConcreteSkus) as $sku) {
277
            $this->updateAvailability($sku);
278
        }
279
280
        return $this->createDynamicEntityPostEditResponseTransfer();
281
    }
282
283
    /**
284
     * @param \Generated\Shared\Transfer\DynamicEntityPostEditRequestTransfer $dynamicEntityPostEditRequestTransfer
285
     *
286
     * @return array<int>
287
     */
288
    protected function getProductIdsFromDynamicEntityRequest(DynamicEntityPostEditRequestTransfer $dynamicEntityPostEditRequestTransfer): array
289
    {
290
        $productIds = [];
291
        foreach ($dynamicEntityPostEditRequestTransfer->getRawDynamicEntities() as $rawDynamicEntity) {
292
            $productIds[] = $rawDynamicEntity->getFields()[static::FK_PRODUCT];
293
        }
294
295
        return $productIds;
296
    }
297
298
    /**
299
     * @param \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer|null $productConcreteAvailabilityTransfer
300
     *
301
     * @return bool
302
     */
303
    protected function isProductConcreteAvailable(?ProductConcreteAvailabilityTransfer $productConcreteAvailabilityTransfer): bool
304
    {
305
        if ($productConcreteAvailabilityTransfer === null) {
306
            return false;
307
        }
308
309
        /** @var \Spryker\DecimalObject\Decimal $availability */
310
        $availability = $productConcreteAvailabilityTransfer->requireAvailability()->getAvailability();
311
312
        return $availability->greaterThan(0) ||
313
            $productConcreteAvailabilityTransfer->getIsNeverOutOfStock() === true;
314
    }
315
316
    /**
317
     * @param string $concreteSku
318
     *
319
     * @throws \Spryker\Zed\Availability\Business\Exception\ProductNotFoundException
320
     *
321
     * @return string
322
     */
323
    protected function getAbstractSkuFromProductConcrete(string $concreteSku): string
324
    {
325
        $productAbstractSku = $this->availabilityRepository->getAbstractSkuFromProductConcrete($concreteSku);
326
327
        if ($productAbstractSku === null) {
328
            throw new ProductNotFoundException(
329
                sprintf(static::PRODUCT_SKU_NOT_FOUND_EXCEPTION_MESSAGE_FORMAT, $concreteSku),
330
            );
331
        }
332
333
        return $productAbstractSku;
334
    }
335
336
    /**
337
     * @param int $idProductConcrete
338
     *
339
     * @throws \Spryker\Zed\Availability\Business\Exception\ProductNotFoundException
340
     *
341
     * @return string
342
     */
343
    protected function getProductConcreteSkuByConcreteId(int $idProductConcrete): string
344
    {
345
        $concreteSku = $this->availabilityRepository->getProductConcreteSkuByConcreteId($idProductConcrete);
346
347
        if ($concreteSku === null) {
348
            throw new ProductNotFoundException(
349
                sprintf(static::PRODUCT_ID_NOT_FOUND_EXCEPTION_MESSAGE_FORMAT, $idProductConcrete),
350
            );
351
        }
352
353
        return $concreteSku;
354
    }
355
356
    /**
357
     * @param string $concreteSku
358
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
359
     *
360
     * @return void
361
     */
362
    protected function triggerProductIsAvailableAgainEvent(string $concreteSku, StoreTransfer $storeTransfer): void
363
    {
364
        $availabilityNotificationDataTransfer = (new AvailabilityNotificationDataTransfer())
365
            ->setSku($concreteSku)
366
            ->setStore($storeTransfer);
367
368
        $this->eventFacade->trigger(
369
            AvailabilityEvents::AVAILABILITY_NOTIFICATION,
370
            $availabilityNotificationDataTransfer,
371
        );
372
    }
373
374
    /**
375
     * @return \Generated\Shared\Transfer\DynamicEntityPostEditResponseTransfer
376
     */
377
    protected function createDynamicEntityPostEditResponseTransfer(): DynamicEntityPostEditResponseTransfer
378
    {
379
        return new DynamicEntityPostEditResponseTransfer();
380
    }
381
382
    /**
383
     * @param \Generated\Shared\Transfer\ProductAvailabilityDataTransfer $productAvailabilityDataTransfer
384
     *
385
     * @return array<int, array<\Generated\Shared\Transfer\StockProductTransfer>>
386
     */
387
    protected function getStockProductTransfersIndexedByIdStore(ProductAvailabilityDataTransfer $productAvailabilityDataTransfer): array
388
    {
389
        $stockIdsGroupedByIdStore = $this->getStockIdsGroupedByIdStore();
390
        $stockProductsIndexedByStock = [];
391
        foreach ($productAvailabilityDataTransfer->getStockProducts() as $stockProductTransfer) {
392
            $stockProductsIndexedByStock[$stockProductTransfer->getFkStock()] = $stockProductTransfer;
393
        }
394
395
        $stockProductTransfersIndexedByIdStore = [];
396
        foreach ($productAvailabilityDataTransfer->getStocks() as $stockTransfer) {
397
            foreach ($stockTransfer->getStoreRelationOrFail()->getStores() as $storeTransfer) {
398
                if (!isset($stockProductsIndexedByStock[$stockTransfer->getIdStock()]) || !isset($stockIdsGroupedByIdStore[$storeTransfer->getIdStore()])) {
399
                    continue;
400
                }
401
402
                if (!in_array($stockTransfer->getIdStock(), $stockIdsGroupedByIdStore[$storeTransfer->getIdStore()])) {
403
                    continue;
404
                }
405
                $stockProductTransfersIndexedByIdStore[(int)$storeTransfer->getIdStore()][] = $stockProductsIndexedByStock[$stockTransfer->getIdStock()];
406
            }
407
        }
408
409
        return $stockProductTransfersIndexedByIdStore;
410
    }
411
412
    /**
413
     * @param \Generated\Shared\Transfer\ProductAvailabilityDataTransfer $productAvailabilityDataTransfer
414
     *
415
     * @return array<int, \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer>
416
     */
417
    protected function getProductConcreteAvailabilityTransfersIndexedByIdStore(ProductAvailabilityDataTransfer $productAvailabilityDataTransfer): array
418
    {
419
        $productConcreteAvailabilityTransfersIndexedByIdStore = [];
420
        foreach ($productAvailabilityDataTransfer->getProductConcreteAvailabilities() as $productConcreteAvailabilityTransfer) {
421
            $productConcreteAvailabilityTransfersIndexedByIdStore[(int)$productConcreteAvailabilityTransfer->getStoreOrFail()->getIdStore()] = $productConcreteAvailabilityTransfer;
422
        }
423
424
        return $productConcreteAvailabilityTransfersIndexedByIdStore;
425
    }
426
427
    /**
428
     * @param \Generated\Shared\Transfer\ProductAvailabilityDataTransfer $productAvailabilityDataTransfer
429
     *
430
     * @return array<int, \Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer>
431
     */
432
    protected function getProductAbstractAvailabilityTransfersIndexedByIdStore(ProductAvailabilityDataTransfer $productAvailabilityDataTransfer): array
433
    {
434
        $productAbstractAvailabilityTransfersIndexedByIdStore = [];
435
        foreach ($productAvailabilityDataTransfer->getProductAbstractAvailabilities() as $productAbstractAvailabilityTransfer) {
436
            $productAbstractAvailabilityTransfersIndexedByIdStore[(int)$productAbstractAvailabilityTransfer->getIdStore()] = $productAbstractAvailabilityTransfer;
437
        }
438
439
        return $productAbstractAvailabilityTransfersIndexedByIdStore;
440
    }
441
442
    /**
443
     * @param string $productAbstractSku
444
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
445
     * @param array<int, \Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer> $productAbstractAvailabilityTransfersIndexedByIdStore
446
     *
447
     * @return void
448
     */
449
    protected function updateProductAbstractAvailability(
450
        string $productAbstractSku,
451
        StoreTransfer $storeTransfer,
452
        array $productAbstractAvailabilityTransfersIndexedByIdStore
453
    ): void {
454
        $calculatedProductAbstractAvailabilityTransfer = $this->availabilityCalculator
455
            ->getCalculatedProductAbstractAvailabilityTransfer($productAbstractSku, $storeTransfer);
456
457
        /** @var \Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer|null $productAbstractAvailabilityTransfer */
458
        $productAbstractAvailabilityTransfer = $productAbstractAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()] ?? null;
459
        $abstractAvailabilityQuantity = isset($productAbstractAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()]) ?
460
            $productAbstractAvailabilityTransfer?->getAvailability() :
461
            null;
462
463
        if ($abstractAvailabilityQuantity === null || !$calculatedProductAbstractAvailabilityTransfer->getAvailabilityOrFail()->equals($abstractAvailabilityQuantity)) {
464
            $this->availabilityEntityManager->saveProductAbstractAvailability(
465
                $calculatedProductAbstractAvailabilityTransfer,
466
                $storeTransfer,
467
            );
468
        }
469
    }
470
471
    /**
472
     * @return list<\Generated\Shared\Transfer\StoreTransfer>
473
     */
474
    protected function getAllStoreTransfersCache(): array
475
    {
476
        if (static::$allStoreTransfersCache) {
477
            return static::$allStoreTransfersCache;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::allStoreTransfersCache returns the type Spryker\Zed\Availability\Business\Model\list which is incompatible with the type-hinted return array.
Loading history...
478
        }
479
        static::$allStoreTransfersCache = $this->storeFacade->getAllStores();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->storeFacade->getAllStores() of type array is incompatible with the declared type Spryker\Zed\Availability\Business\Model\list of property $allStoreTransfersCache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
480
481
        return static::$allStoreTransfersCache;
482
    }
483
484
    /**
485
     * @param \Generated\Shared\Transfer\ProductAvailabilityDataTransfer $productAvailabilityDataTransfer
486
     *
487
     * @return void
488
     */
489
    protected function saveAllAvailabilities(ProductAvailabilityDataTransfer $productAvailabilityDataTransfer): void
490
    {
491
        if (!$productAvailabilityDataTransfer->getProductConcrete() && !$productAvailabilityDataTransfer->getProductAbstract()) {
492
            return;
493
        }
494
495
        $stockProductTransfersIndexedByIdStore = $this->getStockProductTransfersIndexedByIdStore($productAvailabilityDataTransfer);
496
        $productConcreteAvailabilityTransfersIndexedByIdStore = $this->getProductConcreteAvailabilityTransfersIndexedByIdStore($productAvailabilityDataTransfer);
497
        $productAbstractAvailabilityTransfersIndexedByIdStore = $this->getProductAbstractAvailabilityTransfersIndexedByIdStore($productAvailabilityDataTransfer);
498
        $productAbstractSku = $productAvailabilityDataTransfer->getProductAbstractOrFail()->getSkuOrFail();
499
        $productConcreteSku = $productAvailabilityDataTransfer->getProductConcreteOrFail()->getSkuOrFail();
500
501
        foreach ($this->getAllStoreTransfersCache() as $storeTransfer) {
502
            $this->executeSaveAllAvailabilitiesPerStore(
503
                $stockProductTransfersIndexedByIdStore,
504
                $productConcreteAvailabilityTransfersIndexedByIdStore,
505
                $productAbstractAvailabilityTransfersIndexedByIdStore,
506
                $productAbstractSku,
507
                $productConcreteSku,
508
                $storeTransfer,
509
            );
510
        }
511
    }
512
513
    /**
514
     * @param array<int, array<\Generated\Shared\Transfer\StockProductTransfer>> $stockProductTransfersIndexedByIdStore
515
     * @param array<int, \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer> $productConcreteAvailabilityTransfersIndexedByIdStore
516
     * @param array<int, \Generated\Shared\Transfer\ProductAbstractAvailabilityTransfer> $productAbstractAvailabilityTransfersIndexedByIdStore
517
     * @param string $productAbstractSku
518
     * @param string $productConcreteSku
519
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
520
     *
521
     * @return void
522
     */
523
    protected function executeSaveAllAvailabilitiesPerStore(
524
        array $stockProductTransfersIndexedByIdStore,
525
        array $productConcreteAvailabilityTransfersIndexedByIdStore,
526
        array $productAbstractAvailabilityTransfersIndexedByIdStore,
527
        string $productAbstractSku,
528
        string $productConcreteSku,
529
        StoreTransfer $storeTransfer
530
    ): void {
531
        $wasProductConcreteAvailable = $this->isProductConcreteAvailable(
532
            $productConcreteAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()] ?? null,
533
        );
534
535
        $this->updateProductAbstractAvailability($productAbstractSku, $storeTransfer, $productAbstractAvailabilityTransfersIndexedByIdStore);
536
537
        $quantity = new Decimal(0);
538
        if (isset($stockProductTransfersIndexedByIdStore[$storeTransfer->getIdStore()])) {
539
            $quantity = $this->availabilityCalculator->calculateAvailabilityForProductConcrete(
540
                $productConcreteSku,
541
                $storeTransfer,
542
                $stockProductTransfersIndexedByIdStore[$storeTransfer->getIdStore()],
543
            );
544
        }
545
546
        $isNeverOutOfStock = $this->isNeverOutOfStock($stockProductTransfersIndexedByIdStore, $storeTransfer);
547
        $productConcreteAvailabilityTransfer = (new ProductConcreteAvailabilityTransfer())
548
            ->setSku($productConcreteSku)
549
            ->setIsNeverOutOfStock($isNeverOutOfStock)
550
            ->setAvailability($quantity);
551
552
        if (isset($productConcreteAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()])) {
553
            $productConcreteAvailabilityTransfer = $productConcreteAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()];
554
        }
555
556
        $isAvailabilityChanged = false;
557
        if (
558
            !$quantity->equals($productConcreteAvailabilityTransfer->getAvailability() ?? 0) ||
559
            $productConcreteAvailabilityTransfer->getIsNeverOutOfStock() !== $isNeverOutOfStock ||
560
            !isset($productConcreteAvailabilityTransfersIndexedByIdStore[$storeTransfer->getIdStore()])
561
        ) {
562
            $productConcreteAvailabilityTransfer->setAvailability($quantity);
563
            $productConcreteAvailabilityTransfer->setIsNeverOutOfStock($isNeverOutOfStock);
564
            $isAvailabilityChanged = $this->availabilityEntityManager->saveProductConcreteAvailability(
565
                $productConcreteAvailabilityTransfer,
566
                $storeTransfer,
567
                $productAbstractSku,
568
            );
569
        }
570
571
        if ($isAvailabilityChanged && $this->touchFacade->isTouchEnabled()) {
572
            $idAvailabilityAbstract = $productConcreteAvailabilityTransfer->getFkAvailabilityAbstract();
573
            if ($idAvailabilityAbstract === null) {
574
                $idAvailabilityAbstract = $this->availabilityRepository
575
                    ->findIdProductAbstractAvailabilityBySku($productAbstractSku, $storeTransfer);
576
            }
577
            $this->touchAvailabilityAbstract($idAvailabilityAbstract);
578
        }
579
580
        if ($isAvailabilityChanged && !$wasProductConcreteAvailable && $this->isProductConcreteAvailable($productConcreteAvailabilityTransfer)) {
581
            $this->triggerProductIsAvailableAgainEvent($productConcreteSku, $storeTransfer);
582
        }
583
    }
584
585
    /**
586
     * @param array<int, array<\Generated\Shared\Transfer\StockProductTransfer>> $stockProductTransfersIndexedByIdStore
587
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
588
     *
589
     * @return bool
590
     */
591
    protected function isNeverOutOfStock(array $stockProductTransfersIndexedByIdStore, StoreTransfer $storeTransfer): bool
592
    {
593
        if (!isset($stockProductTransfersIndexedByIdStore[$storeTransfer->getIdStore()])) {
594
            return false;
595
        }
596
597
        foreach ($stockProductTransfersIndexedByIdStore[$storeTransfer->getIdStore()] as $stockProductTransfer) {
598
            if ($stockProductTransfer->getIsNeverOutOfStock()) {
599
                return true;
600
            }
601
        }
602
603
        return false;
604
    }
605
606
    /**
607
     * @return array<int, array<int, int>>
608
     */
609
    protected function getStockIdsGroupedByIdStore(): array
610
    {
611
        $stockStoreCollectionTransfer = $this->stockFacade->getStockStoreCollection(new StockStoreCriteriaTransfer());
612
        $stockIdsGroupedByIdStore = [];
613
        /**
614
         * @var \ArrayObject<int, \Generated\Shared\Transfer\StockStoreTransfer>|null $stockStoreTransfers
615
         */
616
        $stockStoreTransfers = $stockStoreCollectionTransfer->getStockStores();
617
        if (!$stockStoreTransfers) {
0 ignored issues
show
introduced by
$stockStoreTransfers is of type ArrayObject, thus it always evaluated to true.
Loading history...
618
            return $stockIdsGroupedByIdStore;
619
        }
620
621
        foreach ($stockStoreTransfers as $stockStoreTransfer) {
622
            $stockIdsGroupedByIdStore[(int)$stockStoreTransfer->getFkStore()][] = (int)$stockStoreTransfer->getFkStock();
623
        }
624
625
        return $stockIdsGroupedByIdStore;
626
    }
627
}
628