Failed Conditions
Push — dev/fixed_tax_rate ( c4b1a0 )
by Kiyotaka
06:00
created

PurchaseFlow::addItemHolderPostValidator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Service\PurchaseFlow;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Eccube\Entity\ItemHolderInterface;
18
use Eccube\Entity\ItemInterface;
19
use Eccube\Entity\Order;
20
use Eccube\Entity\OrderItem;
21
22
class PurchaseFlow
23
{
24
    /**
25
     * @var string
26
     */
27
    protected $flowType;
28
29
    /**
30
     * @var ArrayCollection|ItemPreprocessor[]
31
     */
32
    protected $itemPreprocessors;
33
34
    /**
35
     * @var ArrayCollection|ItemHolderPreprocessor[]
36
     */
37
    protected $itemHolderPreprocessors;
38
39
    /**
40
     * @var ArrayCollection|ItemValidator[]
41
     */
42
    protected $itemValidators;
43
44
    /**
45
     * @var ArrayCollection|ItemHolderValidator[]
46
     */
47
    protected $itemHolderValidators;
48
49
    /**
50
     * @var ArrayCollection|ItemHolderPostValidator[]
51
     */
52
    protected $itemHolderPostValidators;
53
54
    /**
55
     * @var ArrayCollection|DiscountProcessor[]
56
     */
57
    protected $discountProcessors;
58
59
    /**
60
     * @var ArrayCollection|PurchaseProcessor[]
61
     */
62
    protected $purchaseProcessors;
63
64
    public function __construct()
65
    {
66
        $this->purchaseProcessors = new ArrayCollection();
67
        $this->itemValidators = new ArrayCollection();
68
        $this->itemHolderValidators = new ArrayCollection();
69
        $this->itemPreprocessors = new ArrayCollection();
70
        $this->itemHolderPreprocessors = new ArrayCollection();
71
        $this->itemHolderPostValidators = new ArrayCollection();
72
        $this->discountProcessors = new ArrayCollection();
73
    }
74
75
    public function setFlowType($flowType)
76
    {
77
        $this->flowType = $flowType;
78
    }
79
80
    public function setPurchaseProcessors(ArrayCollection $processors)
81
    {
82
        $this->purchaseProcessors = $processors;
83
    }
84
85
    public function setItemValidators(ArrayCollection $itemValidators)
86
    {
87
        $this->itemValidators = $itemValidators;
88
    }
89
90
    public function setItemHolderValidators(ArrayCollection $itemHolderValidators)
91
    {
92
        $this->itemHolderValidators = $itemHolderValidators;
93
    }
94
95
    public function setItemPreprocessors(ArrayCollection $itemPreprocessors)
96
    {
97
        $this->itemPreprocessors = $itemPreprocessors;
98
    }
99
100
    public function setItemHolderPreprocessors(ArrayCollection $itemHolderPreprocessors)
101
    {
102
        $this->itemHolderPreprocessors = $itemHolderPreprocessors;
103
    }
104
105
    public function setItemHolderPostValidators(ArrayCollection $itemHolderPostValidators)
106
    {
107
        $this->itemHolderPostValidators = $itemHolderPostValidators;
108
    }
109
110
    public function setDiscountProcessors(ArrayCollection $discountProcessors)
111
    {
112
        $this->discountProcessors = $discountProcessors;
113
    }
114
115
    public function validate(ItemHolderInterface $itemHolder, PurchaseContext $context)
116
    {
117
        $context->setFlowType($this->flowType);
118
119
        $this->calculateAll($itemHolder);
120
121
        $flowResult = new PurchaseFlowResult($itemHolder);
122
123
        foreach ($itemHolder->getItems() as $item) {
124
            foreach ($this->itemValidators as $itemValidator) {
125
                $result = $itemValidator->execute($item, $context);
126
                $flowResult->addProcessResult($result);
127
            }
128
        }
129
130
        $this->calculateAll($itemHolder);
131
132
        foreach ($this->itemHolderValidators as $itemHolderValidator) {
133
            $result = $itemHolderValidator->execute($itemHolder, $context);
134
            $flowResult->addProcessResult($result);
135
        }
136
137
        $this->calculateAll($itemHolder);
138
139
        foreach ($itemHolder->getItems() as $item) {
140
            foreach ($this->itemPreprocessors as $itemPreprocessor) {
141
                $itemPreprocessor->process($item, $context);
142
            }
143
        }
144
145
        $this->calculateAll($itemHolder);
146
147
        foreach ($this->itemHolderPreprocessors as $holderPreprocessor) {
148
            $result = $holderPreprocessor->process($itemHolder, $context);
149
            if ($result) {
150
                $flowResult->addProcessResult($result);
151
            }
152
153
            $this->calculateAll($itemHolder);
154
        }
155
156
        foreach ($this->discountProcessors as $discountProcessor) {
157
            $discountProcessor->removeDiscountItem($itemHolder, $context);
158
        }
159
160
        $this->calculateAll($itemHolder);
161
162
        foreach ($this->discountProcessors as $discountProcessor) {
163
            $result = $discountProcessor->addDiscountItem($itemHolder, $context);
164
            if ($result) {
165
                $flowResult->addProcessResult($result);
166
            }
167
            $this->calculateAll($itemHolder);
168
        }
169
170
        foreach ($this->itemHolderPostValidators as $itemHolderPostValidator) {
171
            $result = $itemHolderPostValidator->execute($itemHolder, $context);
172
            $flowResult->addProcessResult($result);
173
174
            $this->calculateAll($itemHolder);
175
        }
176
177
        return $flowResult;
178
    }
179
180
    /**
181
     * 購入フロー仮確定処理.
182
     *
183
     * @param ItemHolderInterface $target
184
     * @param PurchaseContext $context
185
     *
186
     * @throws PurchaseException
187
     */
188
    public function prepare(ItemHolderInterface $target, PurchaseContext $context)
189
    {
190
        $context->setFlowType($this->flowType);
191
192
        foreach ($this->purchaseProcessors as $processor) {
193
            $processor->prepare($target, $context);
194
        }
195
    }
196
197
    /**
198
     * 購入フロー確定処理.
199
     *
200
     * @param ItemHolderInterface $target
201
     * @param PurchaseContext $context
202
     *
203
     * @throws PurchaseException
204
     */
205
    public function commit(ItemHolderInterface $target, PurchaseContext $context)
206
    {
207
        $context->setFlowType($this->flowType);
208
209
        foreach ($this->purchaseProcessors as $processor) {
210
            $processor->commit($target, $context);
211
        }
212
    }
213
214
    /**
215
     * 購入フロー仮確定取り消し処理.
216
     *
217
     * @param ItemHolderInterface $target
218
     * @param PurchaseContext $context
219
     */
220
    public function rollback(ItemHolderInterface $target, PurchaseContext $context)
221
    {
222
        $context->setFlowType($this->flowType);
223
224
        foreach ($this->purchaseProcessors as $processor) {
225
            $processor->rollback($target, $context);
226
        }
227
    }
228
229
    public function addPurchaseProcessor(PurchaseProcessor $processor)
230
    {
231
        $this->purchaseProcessors[] = $processor;
232
    }
233
234
    public function addItemHolderPreprocessor(ItemHolderPreprocessor $holderPreprocessor)
235
    {
236
        $this->itemHolderPreprocessors[] = $holderPreprocessor;
237
    }
238
239
    public function addItemPreprocessor(ItemPreprocessor $itemPreprocessor)
240
    {
241
        $this->itemPreprocessors[] = $itemPreprocessor;
242
    }
243
244
    public function addItemValidator(ItemValidator $itemValidator)
245
    {
246
        $this->itemValidators[] = $itemValidator;
247
    }
248
249
    public function addItemHolderValidator(ItemHolderValidator $itemHolderValidator)
250
    {
251
        $this->itemHolderValidators[] = $itemHolderValidator;
252
    }
253
254
    public function addItemHolderPostValidator(ItemHolderPostValidator $itemHolderValidator)
255
    {
256
        $this->itemHolderPostValidators[] = $itemHolderValidator;
257
    }
258
259
    public function addDiscountProcessor(DiscountProcessor $discountProcessor)
260
    {
261
        $this->discountProcessors[] = $discountProcessor;
262
    }
263
264
    /**
265
     * @param ItemHolderInterface $itemHolder
266
     */
267
    protected function calculateTotal(ItemHolderInterface $itemHolder)
268
    {
269
        $total = array_reduce($itemHolder->getItems()->toArray(), function ($sum, ItemInterface $item) {
0 ignored issues
show
Bug introduced by
The method toArray cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
270
            $sum += $item->getPriceIncTax() * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
271
272
            return $sum;
273
        }, 0);
274
        $itemHolder->setTotal($total);
275
        // TODO
276
        if ($itemHolder instanceof Order) {
277
            // Order には PaymentTotal もセットする
278
            $itemHolder->setPaymentTotal($total);
279
        }
280
    }
281
282 View Code Duplication
    protected function calculateSubTotal(ItemHolderInterface $itemHolder)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283
    {
284
        $total = $itemHolder->getItems()
0 ignored issues
show
Bug introduced by
The method getProductClasses cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
285
            ->getProductClasses()
286
            ->reduce(function ($sum, ItemInterface $item) {
287
                $sum += $item->getPriceIncTax() * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
288
289
                return $sum;
290
            }, 0);
291
        // TODO
292
        if ($itemHolder instanceof Order) {
293
            // Order の場合は SubTotal をセットする
294
            $itemHolder->setSubTotal($total);
295
        }
296
    }
297
298
    /**
299
     * @param ItemHolderInterface $itemHolder
300
     */
301 View Code Duplication
    protected function calculateDeliveryFeeTotal(ItemHolderInterface $itemHolder)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
302
    {
303
        $total = $itemHolder->getItems()
0 ignored issues
show
Bug introduced by
The method getDeliveryFees cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
304
            ->getDeliveryFees()
305
            ->reduce(function ($sum, ItemInterface $item) {
306
                $sum += $item->getPriceIncTax() * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
307
308
                return $sum;
309
            }, 0);
310
        $itemHolder->setDeliveryFeeTotal($total);
311
    }
312
313
    /**
314
     * @param ItemHolderInterface $itemHolder
315
     */
316 View Code Duplication
    protected function calculateDiscount(ItemHolderInterface $itemHolder)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
    {
318
        $total = $itemHolder->getItems()
0 ignored issues
show
Bug introduced by
The method getDiscounts cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
319
            ->getDiscounts()
320
            ->reduce(function ($sum, ItemInterface $item) {
321
                $sum += $item->getPriceIncTax() * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
322
323
                return $sum;
324
            }, 0);
325
        // TODO 後方互換のため discount には正の整数を代入する
326
        $itemHolder->setDiscount($total * -1);
327
    }
328
329
    /**
330
     * @param ItemHolderInterface $itemHolder
331
     */
332 View Code Duplication
    protected function calculateCharge(ItemHolderInterface $itemHolder)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
333
    {
334
        $total = $itemHolder->getItems()
0 ignored issues
show
Bug introduced by
The method getCharges cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
335
            ->getCharges()
336
            ->reduce(function ($sum, ItemInterface $item) {
337
                $sum += $item->getPriceIncTax() * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
338
339
                return $sum;
340
            }, 0);
341
        $itemHolder->setCharge($total);
342
    }
343
344
    /**
345
     * @param ItemHolderInterface $itemHolder
346
     */
347
    protected function calculateTax(ItemHolderInterface $itemHolder)
348
    {
349
        $total = $itemHolder->getItems()
0 ignored issues
show
Bug introduced by
The method reduce cannot be called on $itemHolder->getItems() (of type array<integer,object<Ecc...\Entity\ItemInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
350
            ->reduce(function ($sum, ItemInterface $item) {
351
                if ($item instanceof OrderItem) {
352
                    $sum += $item->getTax() * $item->getQuantity();
353
                } else {
354
                    $sum += ($item->getPriceIncTax() - $item->getPrice()) * $item->getQuantity();
0 ignored issues
show
Bug introduced by
The method getPriceIncTax() does not exist on Eccube\Entity\ItemInterface. Did you maybe mean getPrice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
355
                }
356
357
                return $sum;
358
            }, 0);
359
        $itemHolder->setTax($total);
0 ignored issues
show
Deprecated Code introduced by
The method Eccube\Entity\ItemHolderInterface::setTax() has been deprecated with message: 明細ごとに集計した税額と差異が発生する場合があるため非推奨

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
360
    }
361
362
    /**
363
     * @param ItemHolderInterface $itemHolder
364
     */
365
    protected function calculateAll(ItemHolderInterface $itemHolder)
366
    {
367
        $this->calculateDeliveryFeeTotal($itemHolder);
368
        $this->calculateCharge($itemHolder);
369
        $this->calculateDiscount($itemHolder);
370
        $this->calculateSubTotal($itemHolder); // Order の場合のみ
371
        $this->calculateTax($itemHolder);
372
        $this->calculateTotal($itemHolder);
373
    }
374
375
    /**
376
     * PurchaseFlow をツリー表示します.
377
     *
378
     * @return string
379
     */
380
    public function dump()
381
    {
382
        $callback = function ($processor) {
383
            return get_class($processor);
384
        };
385
        $flows = [
386
            0 => $this->flowType.' flow',
387
            'ItemValidator' => $this->itemValidators->map($callback)->toArray(),
388
            'ItemHolderValidator' => $this->itemHolderValidators->map($callback)->toArray(),
389
            'ItemPreprocessor' => $this->itemPreprocessors->map($callback)->toArray(),
390
            'ItemHolderPreprocessor' => $this->itemHolderPreprocessors->map($callback)->toArray(),
391
            'DiscountProcessor' => $this->discountProcessors->map($callback)->toArray(),
392
            'ItemHolderPostValidator' => $this->itemHolderPostValidators->map($callback)->toArray()
393
        ];
394
        $tree  = new \RecursiveTreeIterator(new \RecursiveArrayIterator($flows));
395
        $tree->setPrefixPart(\RecursiveTreeIterator::PREFIX_RIGHT, ' ');
396
        $tree->setPrefixPart(\RecursiveTreeIterator::PREFIX_MID_LAST, ' ');
397
        $tree->setPrefixPart(\RecursiveTreeIterator::PREFIX_MID_HAS_NEXT, '│');
398
        $tree->setPrefixPart(\RecursiveTreeIterator::PREFIX_END_HAS_NEXT, '├');
399
        $tree->setPrefixPart(\RecursiveTreeIterator::PREFIX_END_LAST, '└');
400
        $out = '';
401
        foreach ($tree as $key => $value) {
402
            if (is_numeric($key)) {
403
                $out .= $value.PHP_EOL;
404
            } else {
405
                $out .= $key.PHP_EOL;
406
            }
407
        }
408
        return $out;
409
    }
410
411
    /**
412
     * @return string
413
     */
414
    public function __toString()
415
    {
416
        return $this->dump();
417
    }
418
}
419