Reader::getValidPrices()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 1
dl 0
loc 19
rs 9.8666
c 0
b 0
f 0
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\PriceProduct\Business\Model;
9
10
use Generated\Shared\Transfer\MoneyValueTransfer;
11
use Generated\Shared\Transfer\PriceProductCriteriaTransfer;
12
use Generated\Shared\Transfer\PriceProductDimensionTransfer;
13
use Generated\Shared\Transfer\PriceProductFilterIdentifierTransfer;
14
use Generated\Shared\Transfer\PriceProductFilterTransfer;
15
use Generated\Shared\Transfer\PriceProductTransfer;
16
use Spryker\Service\PriceProduct\PriceProductServiceInterface;
17
use Spryker\Zed\PriceProduct\Business\Model\PriceType\PriceProductTypeReaderInterface;
18
use Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductAbstractReaderInterface;
19
use Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductConcreteReaderInterface;
20
use Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductExpanderInterface;
21
use Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductMapperInterface;
22
use Spryker\Zed\PriceProduct\Dependency\Facade\PriceProductToProductFacadeInterface;
23
use Spryker\Zed\PriceProduct\Persistence\PriceProductRepositoryInterface;
24
use Spryker\Zed\PriceProduct\PriceProductConfig;
25
26
class Reader implements ReaderInterface
27
{
28
    /**
29
     * @var string|null
30
     */
31
    protected static $priceModeIdentifierForNetType;
32
33
    /**
34
     * @var array<\Generated\Shared\Transfer\PriceProductTransfer>
35
     */
36
    protected static $resolvedPriceProductTransferCollection = [];
37
38
    /**
39
     * @var string
40
     */
41
    protected const FIELD_PRICE_PAIR_ABSTRACTS = 'abstracts';
42
43
    /**
44
     * @var string
45
     */
46
    protected const FIELD_PRICE_PAIR_CONCRETES = 'concretes';
47
48
    /**
49
     * @param \Spryker\Zed\PriceProduct\Dependency\Facade\PriceProductToProductFacadeInterface $productFacade
50
     * @param \Spryker\Zed\PriceProduct\Business\Model\PriceType\PriceProductTypeReaderInterface $priceProductTypeReader
51
     * @param \Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductConcreteReaderInterface $priceProductConcreteReader
52
     * @param \Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductAbstractReaderInterface $priceProductAbstractReader
53
     * @param \Spryker\Zed\PriceProduct\Business\Model\PriceProductCriteriaBuilderInterface $priceProductCriteriaBuilder
54
     * @param \Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductMapperInterface $priceProductMapper
55
     * @param \Spryker\Zed\PriceProduct\PriceProductConfig $config
56
     * @param \Spryker\Service\PriceProduct\PriceProductServiceInterface $priceProductService
57
     * @param \Spryker\Zed\PriceProduct\Persistence\PriceProductRepositoryInterface $priceProductRepository
58
     * @param \Spryker\Zed\PriceProduct\Business\Model\Product\PriceProductExpanderInterface $priceProductExpander
59
     */
60
    public function __construct(
61
        protected PriceProductToProductFacadeInterface $productFacade,
62
        protected PriceProductTypeReaderInterface $priceProductTypeReader,
63
        protected PriceProductConcreteReaderInterface $priceProductConcreteReader,
64
        protected PriceProductAbstractReaderInterface $priceProductAbstractReader,
65
        protected PriceProductCriteriaBuilderInterface $priceProductCriteriaBuilder,
66
        protected PriceProductMapperInterface $priceProductMapper,
67
        protected PriceProductConfig $config,
68
        protected PriceProductServiceInterface $priceProductService,
69
        protected PriceProductRepositoryInterface $priceProductRepository,
70
        protected PriceProductExpanderInterface $priceProductExpander
71
    ) {
72
    }
73
74
    /**
75
     * @param string $sku
76
     * @param string|null $priceTypeName
77
     *
78
     * @return int|null
79
     */
80
    public function findPriceBySku($sku, $priceTypeName = null)
81
    {
82
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaWithDefaultValues($priceTypeName);
83
84
        $priceProductTransfer = $this->findProductPrice($sku, $priceProductCriteriaTransfer);
85
86
        if ($priceProductTransfer === null) {
87
            return null;
88
        }
89
90
        /** @var \Generated\Shared\Transfer\MoneyValueTransfer $moneyValueTransfer */
91
        $moneyValueTransfer = $priceProductTransfer->requireMoneyValue()->getMoneyValue();
92
        /** @var string $priceMode */
93
        $priceMode = $priceProductCriteriaTransfer->requirePriceMode()->getPriceMode();
94
95
        return $this->getPriceByPriceMode($moneyValueTransfer, $priceMode);
96
    }
97
98
    /**
99
     * @param \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer
100
     *
101
     * @return int|null
102
     */
103
    public function findPriceFor(PriceProductFilterTransfer $priceProductFilterTransfer)
104
    {
105
        $priceProductTransfer = $this->findPriceProductFor($priceProductFilterTransfer);
106
107
        if ($priceProductTransfer === null) {
108
            return null;
109
        }
110
111
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaFromFilter($priceProductFilterTransfer);
112
113
        /** @var \Generated\Shared\Transfer\MoneyValueTransfer $moneyValueTransfer */
114
        $moneyValueTransfer = $priceProductTransfer->requireMoneyValue()->getMoneyValue();
115
        /** @var string $priceMode */
116
        $priceMode = $priceProductCriteriaTransfer->requirePriceMode()->getPriceMode();
117
118
        return $this->getPriceByPriceMode($moneyValueTransfer, $priceMode);
119
    }
120
121
    /**
122
     * @param \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer
123
     *
124
     * @return \Generated\Shared\Transfer\PriceProductTransfer|null
125
     */
126
    public function findPriceProductFor(PriceProductFilterTransfer $priceProductFilterTransfer): ?PriceProductTransfer
127
    {
128
        $priceProductFilterTransfer->requireSku();
129
130
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaFromFilter($priceProductFilterTransfer);
131
132
        /** @var string $sku */
133
        $sku = $priceProductFilterTransfer->requireSku()->getSku();
134
135
        return $this->findProductPrice($sku, $priceProductCriteriaTransfer);
136
    }
137
138
    /**
139
     * @param int $idProductConcrete
140
     * @param int $idProductAbstract
141
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer|null $priceProductCriteriaTransfer
142
     *
143
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
144
     */
145
    public function findProductConcretePrices(
146
        int $idProductConcrete,
147
        int $idProductAbstract,
148
        ?PriceProductCriteriaTransfer $priceProductCriteriaTransfer = null
149
    ): array {
150
        $concretePriceProductTransfers = $this->priceProductConcreteReader
151
            ->findProductConcretePricesById($idProductConcrete, $priceProductCriteriaTransfer);
152
153
        if ($priceProductCriteriaTransfer !== null && $priceProductCriteriaTransfer->getOnlyConcretePrices()) {
154
            return $concretePriceProductTransfers;
155
        }
156
157
        $abstractPriceProductTransfers = $this->priceProductAbstractReader
158
            ->findProductAbstractPricesById($idProductAbstract, $priceProductCriteriaTransfer);
159
160
        return $this->mergeConcreteAndAbstractPrices($abstractPriceProductTransfers, $concretePriceProductTransfers);
161
    }
162
163
    /**
164
     * @param string $sku
165
     * @param string|null $priceTypeName
166
     *
167
     * @return bool
168
     */
169
    public function hasValidPrice($sku, $priceTypeName = null)
170
    {
171
        $priceTypeName = $this->priceProductTypeReader->handleDefaultPriceType($priceTypeName);
172
173
        if (!$this->priceProductTypeReader->hasPriceType($priceTypeName)) {
174
            return false;
175
        }
176
177
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaWithDefaultValues();
178
179
        return $this->isValidPrice($sku, $priceProductCriteriaTransfer);
180
    }
181
182
    /**
183
     * @param \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer
184
     *
185
     * @return bool
186
     */
187
    public function hasValidPriceFor(PriceProductFilterTransfer $priceProductFilterTransfer)
188
    {
189
        $priceProductFilterTransfer->requireSku();
190
191
        $priceTypeName = $this->priceProductTypeReader->handleDefaultPriceType(
192
            $priceProductFilterTransfer->getPriceTypeName(),
193
        );
194
195
        if (!$this->priceProductTypeReader->hasPriceType($priceTypeName)) {
196
            return false;
197
        }
198
199
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaFromFilter($priceProductFilterTransfer);
200
201
        /** @var string $sku */
202
        $sku = $priceProductFilterTransfer->requireSku()->getSku();
203
204
        return $this->isValidPrice($sku, $priceProductCriteriaTransfer);
205
    }
206
207
    /**
208
     * @param string $sku
209
     * @param string $priceTypeName
210
     * @param string $currencyIsoCode
211
     *
212
     * @return int
213
     */
214
    public function getProductPriceIdBySku($sku, $priceTypeName, $currencyIsoCode)
215
    {
216
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaWithDefaultValues();
217
218
        if ($this->priceProductConcreteReader->hasPriceForProductConcrete($sku, $priceProductCriteriaTransfer)) {
219
            /** @var int $idPriceProduct */
220
            $idPriceProduct = $this->priceProductConcreteReader->findPriceProductId($sku, $priceProductCriteriaTransfer);
221
222
            return $idPriceProduct;
223
        }
224
225
        if (!$this->priceProductAbstractReader->hasPriceForProductAbstract($sku, $priceProductCriteriaTransfer)) {
226
            $sku = $this->productFacade->getAbstractSkuFromProductConcrete($sku);
227
        }
228
229
        /** @var int $idPriceProduct */
230
        $idPriceProduct = $this->priceProductAbstractReader->findPriceProductId($sku, $priceProductCriteriaTransfer);
231
232
        return $idPriceProduct;
233
    }
234
235
    /**
236
     * @param string $sku
237
     * @param \Generated\Shared\Transfer\PriceProductDimensionTransfer|null $priceProductDimensionTransfer
238
     *
239
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
240
     */
241
    public function findPricesBySkuForCurrentStore(
242
        string $sku,
243
        ?PriceProductDimensionTransfer $priceProductDimensionTransfer = null
244
    ): array {
245
        $priceProductDimensionTransfer = $this->getPriceProductDimensionTransfer($priceProductDimensionTransfer);
246
247
        $abstractPriceProductTransfers = $this->priceProductAbstractReader
248
            ->findProductAbstractPricesBySkuForCurrentStore($sku, $priceProductDimensionTransfer);
249
250
        $concretePriceProductTransfers = $this->priceProductConcreteReader
251
            ->findProductConcretePricesBySkuForCurrentStore($sku, $priceProductDimensionTransfer);
252
253
        if (!$concretePriceProductTransfers) {
254
            return $abstractPriceProductTransfers;
255
        }
256
257
        if (!$abstractPriceProductTransfers) {
258
            return $concretePriceProductTransfers;
259
        }
260
261
        return $this->mergeConcreteAndAbstractPrices($abstractPriceProductTransfers, $concretePriceProductTransfers);
262
    }
263
264
    /**
265
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $abstractPriceProductTransfers
266
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $concretePriceProductTransfers
267
     *
268
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
269
     */
270
    protected function mergeConcreteAndAbstractPrices(
271
        array $abstractPriceProductTransfers,
272
        array $concretePriceProductTransfers
273
    ) {
274
        $priceProductTransfers = [];
275
        foreach ($abstractPriceProductTransfers as $priceProductAbstractTransfer) {
276
            $abstractKey = $this->buildPriceProductIdentifier($priceProductAbstractTransfer);
277
278
            $priceProductTransfers = $this->mergeConcreteProduct(
279
                $concretePriceProductTransfers,
280
                $abstractKey,
281
                $priceProductAbstractTransfer,
282
                $priceProductTransfers,
283
            );
284
285
            if (!isset($priceProductTransfers[$abstractKey])) {
286
                $priceProductTransfers[$abstractKey] = $priceProductAbstractTransfer;
287
            }
288
        }
289
290
        return $this->addConcreteNotMergedPrices($concretePriceProductTransfers, $priceProductTransfers);
291
    }
292
293
    /**
294
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $concretePriceProductTransfers
295
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $priceProductTransfers
296
     *
297
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
298
     */
299
    protected function addConcreteNotMergedPrices(array $concretePriceProductTransfers, array $priceProductTransfers)
300
    {
301
        foreach ($concretePriceProductTransfers as $priceProductConcreteTransfer) {
302
            $concreteKey = $this->buildPriceProductIdentifier($priceProductConcreteTransfer);
303
304
            if (isset($priceProductTransfers[$concreteKey])) {
305
                continue;
306
            }
307
308
            $priceProductTransfers[$concreteKey] = $priceProductConcreteTransfer;
309
        }
310
311
        return $priceProductTransfers;
312
    }
313
314
    /**
315
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $concretePriceProductTransfers
316
     * @param string $abstractKey
317
     * @param \Generated\Shared\Transfer\PriceProductTransfer $priceProductAbstractTransfer
318
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $priceProductTransfers
319
     *
320
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
321
     */
322
    protected function mergeConcreteProduct(
323
        array $concretePriceProductTransfers,
324
        $abstractKey,
325
        PriceProductTransfer $priceProductAbstractTransfer,
326
        array $priceProductTransfers
327
    ) {
328
        foreach ($concretePriceProductTransfers as $priceProductConcreteTransfer) {
329
            $concreteKey = $this->buildPriceProductIdentifier($priceProductConcreteTransfer);
330
331
            if ($abstractKey !== $concreteKey) {
332
                continue;
333
            }
334
335
            $priceProductTransfers[$concreteKey] = $this->resolveConcreteProductPrice(
336
                $priceProductAbstractTransfer,
337
                $priceProductConcreteTransfer,
338
            );
339
        }
340
341
        if (!isset($priceProductTransfers[$abstractKey])) {
342
            /** @var \Generated\Shared\Transfer\PriceProductDimensionTransfer $priceDimensionTransfer */
343
            $priceDimensionTransfer = $priceProductAbstractTransfer->requirePriceDimension()->getPriceDimension();
344
            /** @var \Generated\Shared\Transfer\MoneyValueTransfer $moneyValueTransfer */
345
            $moneyValueTransfer = $priceProductAbstractTransfer->requireMoneyValue()->getMoneyValue();
346
            $priceDimensionTransfer->setIdPriceProductDefault(null);
347
            $moneyValueTransfer->setIdEntity(null);
348
            $priceProductTransfers[$abstractKey] = $priceProductAbstractTransfer->setPriceDimension($priceDimensionTransfer)
349
                ->setMoneyValue($moneyValueTransfer);
350
        }
351
352
        return $priceProductTransfers;
353
    }
354
355
    /**
356
     * @param \Generated\Shared\Transfer\PriceProductTransfer $priceProductAbstractTransfer
357
     * @param \Generated\Shared\Transfer\PriceProductTransfer $priceProductConcreteTransfer
358
     *
359
     * @return \Generated\Shared\Transfer\PriceProductTransfer
360
     */
361
    protected function resolveConcreteProductPrice(
362
        PriceProductTransfer $priceProductAbstractTransfer,
363
        PriceProductTransfer $priceProductConcreteTransfer
364
    ) {
365
        /** @var \Generated\Shared\Transfer\MoneyValueTransfer $abstractMoneyValueTransfer */
366
        $abstractMoneyValueTransfer = $priceProductAbstractTransfer->requireMoneyValue()->getMoneyValue();
367
        /** @var \Generated\Shared\Transfer\MoneyValueTransfer $concreteMoneyValueTransfer */
368
        $concreteMoneyValueTransfer = $priceProductConcreteTransfer->requireMoneyValue()->getMoneyValue();
369
370
        if ($concreteMoneyValueTransfer->getGrossAmount() === null) {
371
            $concreteMoneyValueTransfer->setGrossAmount($abstractMoneyValueTransfer->getGrossAmount());
372
        }
373
374
        if ($concreteMoneyValueTransfer->getNetAmount() === null) {
375
            $concreteMoneyValueTransfer->setNetAmount($abstractMoneyValueTransfer->getNetAmount());
376
        }
377
378
        return $priceProductConcreteTransfer;
379
    }
380
381
    /**
382
     * @param string $sku
383
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer $priceProductCriteriaTransfer
384
     *
385
     * @return \Generated\Shared\Transfer\PriceProductTransfer|null
386
     */
387
    protected function findProductPrice(string $sku, PriceProductCriteriaTransfer $priceProductCriteriaTransfer): ?PriceProductTransfer
388
    {
389
        $priceProductConcreteTransfers = $this->priceProductConcreteReader
390
            ->findProductConcretePricesBySkuAndCriteria($sku, $priceProductCriteriaTransfer);
391
392
        if ($this->productFacade->hasProductConcrete($sku)) {
393
            $sku = $this->productFacade->getAbstractSkuFromProductConcrete($sku);
394
        }
395
396
        $priceProductAbstractTransfers = $this->priceProductAbstractReader
397
            ->findProductAbstractPricesBySkuAndCriteria($sku, $priceProductCriteriaTransfer);
398
399
        if (!$priceProductConcreteTransfers) {
400
            return $this->priceProductService
401
                ->resolveProductPriceByPriceProductCriteria($priceProductAbstractTransfers, $priceProductCriteriaTransfer);
402
        }
403
404
        $priceProductTransfers = $this->priceProductService
405
            ->mergeConcreteAndAbstractPrices($priceProductAbstractTransfers, $priceProductConcreteTransfers);
406
407
        return $this->priceProductService
408
            ->resolveProductPriceByPriceProductCriteria($priceProductTransfers, $priceProductCriteriaTransfer);
409
    }
410
411
    /**
412
     * @param \Generated\Shared\Transfer\MoneyValueTransfer $moneyValueTransfer
413
     * @param string $priceMode
414
     *
415
     * @return int|null
416
     */
417
    protected function getPriceByPriceMode(MoneyValueTransfer $moneyValueTransfer, string $priceMode): ?int
418
    {
419
        if ($priceMode === $this->priceProductMapper->getNetPriceModeIdentifier()) {
420
            return $moneyValueTransfer->getNetAmount();
421
        }
422
423
        return $moneyValueTransfer->getGrossAmount();
424
    }
425
426
    /**
427
     * @param string $sku
428
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer $priceProductCriteriaTransfer
429
     *
430
     * @return bool
431
     */
432
    protected function isValidPrice($sku, PriceProductCriteriaTransfer $priceProductCriteriaTransfer)
433
    {
434
        if ($this->priceProductConcreteReader->hasPriceForProductConcrete($sku, $priceProductCriteriaTransfer)) {
435
            return true;
436
        }
437
438
        $abstractSku = $this->productFacade->getAbstractSkuFromProductConcrete($sku);
439
        if ($this->priceProductAbstractReader->hasPriceForProductAbstract($abstractSku, $priceProductCriteriaTransfer)) {
440
            return true;
441
        }
442
443
        return false;
444
    }
445
446
    /**
447
     * @param \Generated\Shared\Transfer\PriceProductDimensionTransfer|null $priceProductDimensionTransfer
448
     *
449
     * @return \Generated\Shared\Transfer\PriceProductDimensionTransfer
450
     */
451
    protected function getPriceProductDimensionTransfer(?PriceProductDimensionTransfer $priceProductDimensionTransfer = null): PriceProductDimensionTransfer
452
    {
453
        if (!$priceProductDimensionTransfer) {
454
            $priceProductDimensionTransfer = (new PriceProductDimensionTransfer())
455
                ->setType($this->config->getPriceDimensionDefault());
456
        }
457
458
        return $priceProductDimensionTransfer;
459
    }
460
461
    /**
462
     * @param int $idProductConcrete
463
     * @param int $idProductAbstract
464
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer|null $priceProductCriteriaTransfer
465
     *
466
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
467
     */
468
    public function findProductConcretePricesWithoutPriceExtraction(
469
        int $idProductConcrete,
470
        int $idProductAbstract,
471
        ?PriceProductCriteriaTransfer $priceProductCriteriaTransfer = null
472
    ): array {
473
        $concretePriceProductTransfers = $this->priceProductConcreteReader
474
            ->findProductConcretePricesWithoutPriceExtraction($idProductConcrete, $priceProductCriteriaTransfer);
475
476
        if ($priceProductCriteriaTransfer !== null && $priceProductCriteriaTransfer->getOnlyConcretePrices()) {
477
            return $concretePriceProductTransfers;
478
        }
479
480
        $abstractPriceProductTransfers = $this->priceProductAbstractReader
481
            ->findProductAbstractPricesWithoutPriceExtraction($idProductAbstract, $priceProductCriteriaTransfer);
482
483
        return $this->mergeConcreteAndAbstractPrices($abstractPriceProductTransfers, $concretePriceProductTransfers);
484
    }
485
486
    /**
487
     * @param \Generated\Shared\Transfer\PriceProductTransfer $priceProductTransfer
488
     *
489
     * @return string
490
     */
491
    protected function buildPriceProductIdentifier(PriceProductTransfer $priceProductTransfer): string
492
    {
493
        /** @var \Generated\Shared\Transfer\MoneyValueTransfer $moneyValueTransfer */
494
        $moneyValueTransfer = $priceProductTransfer->requireMoneyValue()->getMoneyValue();
495
        /** @var \Generated\Shared\Transfer\PriceTypeTransfer $priceTypeTransfer */
496
        $priceTypeTransfer = $priceProductTransfer->requirePriceType()->getPriceType();
497
498
        return implode(
499
            '-',
500
            [
501
                $moneyValueTransfer->getFkStore(),
502
                $moneyValueTransfer->getFkCurrency(),
503
                $priceTypeTransfer->getName(),
504
                $priceTypeTransfer->getPriceModeConfiguration(),
505
            ],
506
        );
507
    }
508
509
    /**
510
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
511
     *
512
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
513
     */
514
    public function getValidPrices(array $priceProductFilterTransfers): array
515
    {
516
        if (count($priceProductFilterTransfers) === 0) {
517
            return [];
518
        }
519
        $priceProductFilterTransfers = $this->filterPriceProductFilterTransfersWithoutExistingPriceType($priceProductFilterTransfers);
520
        if (!$priceProductFilterTransfers) {
521
            return [];
522
        }
523
524
        $concretePricesBySku = $this->findPricesForConcreteProducts($priceProductFilterTransfers);
525
        $abstractPricesBySku = $this->findPricesForAbstractProducts(
526
            $this->getProductConcreteSkus($priceProductFilterTransfers),
527
            $priceProductFilterTransfers,
528
        );
529
530
        return $this->resolveProductPrices(
531
            $this->mergeIndexedPriceProductTransfers($abstractPricesBySku, $concretePricesBySku),
532
            $priceProductFilterTransfers,
533
        );
534
    }
535
536
    /**
537
     * @param array<array<\Generated\Shared\Transfer\PriceProductTransfer>> $indexedAbstractPriceProductTransfers
538
     * @param array<array<\Generated\Shared\Transfer\PriceProductTransfer>> $indexedConcretePriceProductTransfers
539
     *
540
     * @return array<array<\Generated\Shared\Transfer\PriceProductTransfer>>
541
     */
542
    protected function mergeIndexedPriceProductTransfers(array $indexedAbstractPriceProductTransfers, array $indexedConcretePriceProductTransfers): array
543
    {
544
        $mergedPriceProductTransfers = [];
545
546
        foreach ($indexedAbstractPriceProductTransfers as $sku => $abstractPriceProductTransfers) {
547
            $mergedPriceProductTransfers[$sku] = $this->priceProductService->mergeConcreteAndAbstractPrices(
548
                $abstractPriceProductTransfers,
549
                $indexedConcretePriceProductTransfers[$sku] ?? [],
550
            );
551
        }
552
553
        foreach ($indexedConcretePriceProductTransfers as $sku => $concretePriceProductTransfers) {
554
            $mergedPriceProductTransfers[$sku] = $this->priceProductService->mergeConcreteAndAbstractPrices(
555
                $indexedAbstractPriceProductTransfers[$sku] ?? [],
556
                $concretePriceProductTransfers,
557
            );
558
        }
559
560
        return $mergedPriceProductTransfers;
561
    }
562
563
    /**
564
     * @param array<array<\Generated\Shared\Transfer\PriceProductTransfer>> $priceProductTransfers
565
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
566
     *
567
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
568
     */
569
    protected function resolveProductPrices(array $priceProductTransfers, array $priceProductFilterTransfers): array
570
    {
571
        $priceProductCriteriaTransfers = $this
572
            ->priceProductCriteriaBuilder
573
            ->buildCriteriaTransfersFromFilterTransfers($priceProductFilterTransfers);
574
575
        $resolvedPriceProductTransfers = [];
576
        foreach ($priceProductCriteriaTransfers as $index => $priceProductCriteriaTransfer) {
577
            $priceProductFilterTransfer = $this->fillPriceProductFilterIdentifier($priceProductFilterTransfers[$index]);
578
            $resolvedItemPrice = $this->resolveProductPriceByPriceProductCriteria(
579
                $priceProductFilterTransfer->getIdentifierOrFail(),
580
                $priceProductTransfers,
581
                $priceProductCriteriaTransfer,
582
            );
583
584
            if ($resolvedItemPrice) {
585
                $resolvedPriceProductTransfers[] = $resolvedItemPrice;
586
            }
587
        }
588
589
        return $resolvedPriceProductTransfers;
590
    }
591
592
    /**
593
     * @param string $priceProductCriteriaIdentifier
594
     * @param array<array<\Generated\Shared\Transfer\PriceProductTransfer>> $priceProductTransfers
595
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer $priceProductCriteriaTransfer
596
     *
597
     * @return \Generated\Shared\Transfer\PriceProductTransfer|null
598
     */
599
    protected function resolveProductPriceByPriceProductCriteria(
600
        string $priceProductCriteriaIdentifier,
601
        array $priceProductTransfers,
602
        PriceProductCriteriaTransfer $priceProductCriteriaTransfer
603
    ): ?PriceProductTransfer {
604
        if (!isset(static::$resolvedPriceProductTransferCollection[$priceProductCriteriaIdentifier])) {
605
            /** @var \Generated\Shared\Transfer\PriceProductTransfer $priceProductTransfer */
606
            $priceProductTransfer = $this->priceProductService->resolveProductPriceByPriceProductCriteria(
607
                $priceProductTransfers[$priceProductCriteriaIdentifier],
608
                $priceProductCriteriaTransfer,
609
            );
610
            static::$resolvedPriceProductTransferCollection[$priceProductCriteriaIdentifier] = $priceProductTransfer;
611
        }
612
613
        return static::$resolvedPriceProductTransferCollection[$priceProductCriteriaIdentifier];
614
    }
615
616
    /**
617
     * @return string
618
     */
619
    protected function getPriceModeIdentifierForNetType(): string
620
    {
621
        if (static::$priceModeIdentifierForNetType === null) {
622
            static::$priceModeIdentifierForNetType = $this->config->getPriceModeIdentifierForNetType();
623
        }
624
625
        return static::$priceModeIdentifierForNetType;
626
    }
627
628
    /**
629
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
630
     *
631
     * @return array<array<\Generated\Shared\Transfer\PriceProductTransfer>>
632
     */
633
    protected function findPricesForConcreteProducts(array $priceProductFilterTransfers): array
634
    {
635
        $priceProductFilterTransfer = $this->getCommonPriceProductFilterTransfer($priceProductFilterTransfers);
636
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaFromFilter($priceProductFilterTransfer);
637
        $productConcreteSkus = array_map(function (PriceProductFilterTransfer $priceProductFilterTransfer) {
638
            /** @var string $sku */
639
            $sku = $priceProductFilterTransfer->getSku();
640
641
            return $sku;
642
        }, $priceProductFilterTransfers);
643
644
        $concretePriceProductTransfers = $this->priceProductConcreteReader->getProductConcretePricesByConcreteSkusAndCriteria(
645
            $productConcreteSkus,
646
            $priceProductCriteriaTransfer,
647
        );
648
649
        return $this->groupPriceProductTransfersByFilter($priceProductFilterTransfers, $concretePriceProductTransfers);
650
    }
651
652
    /**
653
     * @param array<string> $productConcreteSkus
654
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
655
     *
656
     * @return array<array<\Generated\Shared\Transfer\PriceProductTransfer>>
657
     */
658
    protected function findPricesForAbstractProducts(array $productConcreteSkus, array $priceProductFilterTransfers): array
659
    {
660
        $priceProductFilterTransfer = $this->getCommonPriceProductFilterTransfer($priceProductFilterTransfers);
661
        $priceProductCriteriaTransfer = $this->priceProductCriteriaBuilder->buildCriteriaFromFilter($priceProductFilterTransfer);
662
663
        $priceProductTransfers = $this->priceProductAbstractReader->getProductAbstractPricesByConcreteSkusAndCriteria(
664
            $productConcreteSkus,
665
            $priceProductCriteriaTransfer,
666
        );
667
668
        return $this->groupPriceProductTransfersByFilter($priceProductFilterTransfers, $priceProductTransfers);
669
    }
670
671
    /**
672
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
673
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $priceProductTransfers
674
     *
675
     * @return array<array<\Generated\Shared\Transfer\PriceProductTransfer>>
676
     */
677
    protected function groupPriceProductTransfersByFilter(array $priceProductFilterTransfers, array $priceProductTransfers): array
678
    {
679
        $priceProductTransfersGroupedByFilterIdentifier = [];
680
681
        foreach ($priceProductFilterTransfers as $priceProductFilterTransfer) {
682
            $priceProductFilterTransfer = $this->fillPriceProductFilterIdentifier($priceProductFilterTransfer);
683
            $priceProductFilterIdentifier = $priceProductFilterTransfer->getIdentifierOrFail();
684
            $priceProductTransfersGroupedByFilterIdentifier[$priceProductFilterIdentifier] = $this->priceProductService->resolveProductPricesByPriceProductFilter(
685
                $priceProductTransfers,
686
                $priceProductFilterTransfer,
687
            );
688
        }
689
690
        return $priceProductTransfersGroupedByFilterIdentifier;
691
    }
692
693
    /**
694
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
695
     *
696
     * @return array<string>
697
     */
698
    protected function getProductConcreteSkus(array $priceProductFilterTransfers): array
699
    {
700
        $productConcreteSkus = [];
701
702
        foreach ($priceProductFilterTransfers as $priceProductFilterTransfer) {
703
            /** @var string $sku */
704
            $sku = $priceProductFilterTransfer->getSku();
705
706
            $productConcreteSkus[] = $sku;
707
        }
708
709
        return $productConcreteSkus;
710
    }
711
712
    /**
713
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
714
     *
715
     * @return array<\Generated\Shared\Transfer\PriceProductFilterTransfer>
716
     */
717
    protected function getPriceProductFilterTransfersBySku(array $priceProductFilterTransfers): array
718
    {
719
        $priceProductFilterTransfersBySku = [];
720
        foreach ($priceProductFilterTransfers as $priceProductFilterTransfer) {
721
            $priceProductFilterTransfersBySku[$priceProductFilterTransfer->getSku()] = $priceProductFilterTransfer;
722
        }
723
724
        return $priceProductFilterTransfersBySku;
725
    }
726
727
    /**
728
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
729
     *
730
     * @return \Generated\Shared\Transfer\PriceProductFilterTransfer
731
     */
732
    protected function getCommonPriceProductFilterTransfer(array $priceProductFilterTransfers): PriceProductFilterTransfer
733
    {
734
        /** @var \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer */
735
        $priceProductFilterTransfer = array_shift($priceProductFilterTransfers);
736
737
        return $priceProductFilterTransfer;
738
    }
739
740
    /**
741
     * @param array<\Generated\Shared\Transfer\PriceProductFilterTransfer> $priceProductFilterTransfers
742
     *
743
     * @return array<\Generated\Shared\Transfer\PriceProductFilterTransfer>
744
     */
745
    protected function filterPriceProductFilterTransfersWithoutExistingPriceType(array $priceProductFilterTransfers): array
746
    {
747
        $filteredPriceProductFilterTransfers = [];
748
        foreach ($priceProductFilterTransfers as $priceProductFilterTransfer) {
749
            $priceTypeName = $this->priceProductTypeReader->handleDefaultPriceType(
750
                $priceProductFilterTransfer->getPriceTypeName(),
751
            );
752
            if ($this->priceProductTypeReader->hasPriceType($priceTypeName)) {
753
                $filteredPriceProductFilterTransfers[] = $priceProductFilterTransfer;
754
            }
755
        }
756
757
        return $filteredPriceProductFilterTransfers;
758
    }
759
760
    /**
761
     * @param \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer
762
     *
763
     * @return string
764
     */
765
    protected function buildPriceProductFilterIdentifier(PriceProductFilterTransfer $priceProductFilterTransfer): string
766
    {
767
        $priceProductFilterIdentifierTransfer = (new PriceProductFilterIdentifierTransfer())->fromArray(
768
            $priceProductFilterTransfer->toArray(),
769
            true,
770
        );
771
        $priceProductFilterIdentifierTransfer->setQuantity((int)$priceProductFilterTransfer->getQuantity());
772
773
        return md5(json_encode($priceProductFilterIdentifierTransfer->toArray(), JSON_THROW_ON_ERROR));
774
    }
775
776
    /**
777
     * @param \Generated\Shared\Transfer\PriceProductFilterTransfer $priceProductFilterTransfer
778
     *
779
     * @return \Generated\Shared\Transfer\PriceProductFilterTransfer
780
     */
781
    protected function fillPriceProductFilterIdentifier(PriceProductFilterTransfer $priceProductFilterTransfer): PriceProductFilterTransfer
782
    {
783
        if ($priceProductFilterTransfer->getIdentifier() !== null) {
784
            return $priceProductFilterTransfer;
785
        }
786
787
        return $priceProductFilterTransfer->setIdentifier(
788
            $this->buildPriceProductFilterIdentifier($priceProductFilterTransfer),
789
        );
790
    }
791
792
    /**
793
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer $priceProductCriteriaTransfer
794
     *
795
     * @return array<int, array<\Generated\Shared\Transfer\PriceProductTransfer>>
796
     */
797
    public function findProductConcretePricesWithoutPriceExtractionByConcreteAbstractMap(PriceProductCriteriaTransfer $priceProductCriteriaTransfer): array
798
    {
799
        $productConcreteToProductAbstractIdMap = $priceProductCriteriaTransfer->getProductConcreteToAbstractIdMaps();
800
        $productAbstractToProductConcreteIdMap = $this->mapProductAbstractToProductConcreteId($productConcreteToProductAbstractIdMap);
801
        $priceProductTransfers = $this->getProductPriceTransfers($productConcreteToProductAbstractIdMap, $priceProductCriteriaTransfer);
802
        $priceProductConcreteProductAbstractPairs = $this->pairPriceProductsTransfersByConcreteToAbstractRelation(
803
            $priceProductTransfers,
804
            $productAbstractToProductConcreteIdMap,
805
        );
806
807
        $priceProductTransfersResult = [];
808
        foreach ($priceProductConcreteProductAbstractPairs as $idProductConcrete => $priceProductPair) {
809
            $priceProductTransfersResult[(int)$idProductConcrete] = $this->mergeConcreteAndAbstractPrices(
810
                $priceProductPair[static::FIELD_PRICE_PAIR_ABSTRACTS],
811
                $priceProductPair[static::FIELD_PRICE_PAIR_CONCRETES],
812
            );
813
        }
814
815
        return $priceProductTransfersResult;
816
    }
817
818
    /**
819
     * @param array<int, int> $productConcreteToProductAbstractIdMap
820
     * @param \Generated\Shared\Transfer\PriceProductCriteriaTransfer $priceProductCriteriaTransfer
821
     *
822
     * @return array<\Generated\Shared\Transfer\PriceProductTransfer>
823
     */
824
    protected function getProductPriceTransfers(
825
        array $productConcreteToProductAbstractIdMap,
826
        PriceProductCriteriaTransfer $priceProductCriteriaTransfer
827
    ): array {
828
        $productAbstractIds = array_unique(array_values($productConcreteToProductAbstractIdMap));
829
        $productConcreteIds = array_keys($productConcreteToProductAbstractIdMap);
830
831
        $priceProductTransfers = $this->priceProductRepository->findProductPricesByConcreteIdsOrAbstractIds(
832
            $productConcreteIds,
833
            $productAbstractIds,
834
            $priceProductCriteriaTransfer,
835
        );
836
837
        return $this->priceProductExpander->expandPriceProductTransfers($priceProductTransfers);
838
    }
839
840
    /**
841
     * @param array<int, int> $productConcreteToProductAbstractIdMap
842
     *
843
     * @return array<int, array<int>>
844
     */
845
    protected function mapProductAbstractToProductConcreteId(array $productConcreteToProductAbstractIdMap): array
846
    {
847
        $productAbstractToProductConcreteIdMap = [];
848
        foreach ($productConcreteToProductAbstractIdMap as $idProductConcrete => $idProductAbstract) {
849
            $productAbstractToProductConcreteIdMap[$idProductAbstract] = $productAbstractToProductConcreteIdMap[$idProductAbstract] ?? [];
850
            $productAbstractToProductConcreteIdMap[$idProductAbstract][] = $idProductConcrete;
851
        }
852
853
        return $productAbstractToProductConcreteIdMap;
854
    }
855
856
    /**
857
     * @param array<\Generated\Shared\Transfer\PriceProductTransfer> $priceProductTransfers
858
     * @param array<int, array<int>> $productAbstractToProductConcreteIdMap
859
     *
860
     * @return array<int|string, array<string, array<int, \Generated\Shared\Transfer\PriceProductTransfer>>>
861
     */
862
    protected function pairPriceProductsTransfersByConcreteToAbstractRelation(
863
        array $priceProductTransfers,
864
        array $productAbstractToProductConcreteIdMap
865
    ): array {
866
        $pairStructure = [
867
            static::FIELD_PRICE_PAIR_ABSTRACTS => [],
868
            static::FIELD_PRICE_PAIR_CONCRETES => [],
869
        ];
870
        $priceProductConcreteProductAbstractPairs = [];
871
872
        foreach ($priceProductTransfers as $priceProductTransfer) {
873
            $idProductAbstract = $priceProductTransfer->getIdProductAbstract();
874
            $idProductConcrete = $priceProductTransfer->getIdProduct();
875
876
            if (!$idProductAbstract || empty($productAbstractToProductConcreteIdMap[$idProductAbstract])) {
877
                $priceProductConcreteProductAbstractPairs[$idProductConcrete] ??= $pairStructure;
878
                $priceProductConcreteProductAbstractPairs[$idProductConcrete][static::FIELD_PRICE_PAIR_CONCRETES][] = $priceProductTransfer;
879
880
                continue;
881
            }
882
883
            foreach ($productAbstractToProductConcreteIdMap[$idProductAbstract] as $idProductConcreteRelated) {
884
                $priceProductConcreteProductAbstractPairs[$idProductConcreteRelated] ??= $pairStructure;
885
                $priceProductConcreteProductAbstractPairs[$idProductConcreteRelated][static::FIELD_PRICE_PAIR_ABSTRACTS][] = $priceProductTransfer;
886
            }
887
        }
888
889
        return $priceProductConcreteProductAbstractPairs;
890
    }
891
}
892