Completed
Pull Request — experimental/sf (#3412)
by Kentaro
14:51 queued 07:29
created

PointProcessor   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 241
Duplicated Lines 8.3 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 94.81%

Importance

Changes 0
Metric Value
dl 20
loc 241
ccs 73
cts 77
cp 0.9481
rs 10
c 0
b 0
f 0
wmc 26
lcom 1
cbo 2

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A process() 0 18 3
A validate() 0 22 4
A prepare() 10 10 2
A commit() 0 4 1
A rollback() 10 10 2
A supports() 0 16 4
A calculateAddPoint() 0 24 4
A addPointDiscountItem() 0 21 1
A removePointDiscountItem() 0 9 3
A pointToPrice() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.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\Processor;
15
16
use Doctrine\ORM\EntityManagerInterface;
17
use Eccube\Entity\BaseInfo;
18
use Eccube\Entity\ItemHolderInterface;
19
use Eccube\Entity\ItemInterface;
20
use Eccube\Entity\Master\OrderItemType;
21
use Eccube\Entity\Master\TaxDisplayType;
22
use Eccube\Entity\Master\TaxType;
23
use Eccube\Entity\Order;
24
use Eccube\Entity\OrderItem;
25
use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor;
26
use Eccube\Service\PurchaseFlow\ItemHolderValidator;
27
use Eccube\Service\PurchaseFlow\PurchaseContext;
28
use Eccube\Service\PurchaseFlow\PurchaseProcessor;
29
30
/**
31
 * 購入フローにおけるポイント処理.
32
 */
33
class PointProcessor extends ItemHolderValidator implements ItemHolderPreprocessor, PurchaseProcessor
34
{
35
    /**
36
     * @var EntityManagerInterface
37
     */
38
    protected $entityManager;
39
40
    /**
41
     * @var BaseInfo
42
     */
43
    protected $BaseInfo;
44
45
    /**
46
     * AddPointProcessor constructor.
47
     *
48
     * @param EntityManagerInterface $entityManager
49
     * @param BaseInfo $BaseInfo
0 ignored issues
show
introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
50
     */
51 787
    public function __construct(EntityManagerInterface $entityManager, BaseInfo $BaseInfo)
52
    {
53 787
        $this->entityManager = $entityManager;
54 787
        $this->BaseInfo = $BaseInfo;
55
    }
56
57
    /*
58
     * ItemHolderPreprocessor
59
     */
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 230
    public function process(ItemHolderInterface $itemHolder, PurchaseContext $context)
65
    {
66 230
        if (!$this->supports($itemHolder)) {
67 17
            return;
68
        }
69
70 213
        $this->removePointDiscountItem($itemHolder);
71
72
        // 利用ポイントがある場合は割引明細を追加
73 213
        if ($itemHolder->getUsePoint() > 0) {
74 17
            $discount = $this->pointToPrice($itemHolder->getUsePoint());
75 17
            $this->addPointDiscountItem($itemHolder, $discount);
76
        }
77
78
        // 付与ポイントを計算
79 213
        $addPoint = $this->calculateAddPoint($itemHolder);
80 213
        $itemHolder->setAddPoint($addPoint);
81
    }
82
83
    /*
84
     * ItemHolderValidator
85
     */
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 61
    protected function validate(ItemHolderInterface $itemHolder, PurchaseContext $context)
91
    {
92 61
        if (!$this->supports($itemHolder)) {
93 17
            return;
94
        }
95
96
        // 所有ポイント < 利用ポイント
97 44
        $Customer = $itemHolder->getCustomer();
98 44
        if ($Customer->getPoint() < $itemHolder->getUsePoint()) {
99
            // 利用ポイントが所有ポイントを上回っていた場合は所有ポイントで上書き
100 2
            $itemHolder->setUsePoint($Customer->getPoint());
101 2
            $this->throwInvalidItemException('利用ポイントが所有ポイントを上回っています.');
102
        }
103
104
        // 支払い金額 < 利用ポイント
105 42
        if ($itemHolder->getTotal() < 0) {
106
            // 利用ポイントが支払い金額を上回っていた場合は支払い金額が0円以上となるようにポイントを調整
107 1
            $overPoint = floor($itemHolder->getTotal() / $this->BaseInfo->getPointConversionRate());
108 1
            $itemHolder->setUsePoint($itemHolder->getUsePoint() + $overPoint);
109 1
            $this->throwInvalidItemException('利用ポイントがお支払い金額を上回っています.');
110
        }
111
    }
112
113
    /*
114
     * PurchaseProcessor
115
     */
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 12 View Code Duplication
    public function prepare(ItemHolderInterface $itemHolder, PurchaseContext $context)
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...
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
121
    {
122 12
        if (!$this->supports($itemHolder)) {
123 3
            return;
124
        }
125
126
        // ユーザの保有ポイントを減算
127 9
        $Customer = $itemHolder->getCustomer();
128 9
        $Customer->setPoint($Customer->getPoint() - $itemHolder->getUsePoint());
129
    }
130
131
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$target" missing
Loading history...
introduced by
Doc comment for parameter "$context" missing
Loading history...
132
     * {@inheritdoc
133
     */
134
    public function commit(ItemHolderInterface $target, PurchaseContext $context)
135
    {
136
        // 何もしない
137
    }
138
139
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$itemHolder" missing
Loading history...
introduced by
Doc comment for parameter "$context" missing
Loading history...
140
     * {@inheritdoc
141
     */
142 2 View Code Duplication
    public function rollback(ItemHolderInterface $itemHolder, PurchaseContext $context)
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...
143
    {
144
        // 利用したポイントをユーザに戻す.
145 2
        if (!$this->supports($itemHolder)) {
146
            return;
147
        }
148
149 2
        $Customer = $itemHolder->getCustomer();
150 2
        $Customer->setPoint($Customer->getPoint() + $itemHolder->getUsePoint());
151
    }
152
153
    /*
154
     * Helper methods
155
     */
156
157
    /**
158
     * Processorが実行出来るかどうかを返す.
159
     *
160
     * 以下を満たす場合に実行できる.
161
     *
162
     * - ポイント設定が有効であること.
163
     * - $itemHolderがOrderエンティティであること.
164
     * - 会員のOrderであること.
165
     *
166
     * @param ItemHolderInterface $itemHolder
167
     *
168
     * @return bool
169
     */
170 237
    private function supports(ItemHolderInterface $itemHolder)
171
    {
172 237
        if (!$this->BaseInfo->isOptionPoint()) {
173
            return false;
174
        }
175
176 237
        if (!$itemHolder instanceof Order) {
177
            return false;
178
        }
179
180 237
        if (!$itemHolder->getCustomer()) {
181 17
            return false;
182
        }
183
184 220
        return true;
185
    }
186
187
    /**
188
     * 付与ポイントを計算.
189
     *
190
     * @param ItemHolderInterface $itemHolder
191
     *
192
     * @return int
193
     */
194 213
    private function calculateAddPoint(ItemHolderInterface $itemHolder)
195
    {
196 213
        $basicPointRate = $this->BaseInfo->getBasicPointRate();
197
198
        // 明細ごとのポイントを集計
199 213
        $totalPoint = array_reduce($itemHolder->getItems()->toArray(), function ($carry, ItemInterface $item) use ($basicPointRate) {
200 213
            $pointRate = $item->getPointRate();
201 213
            if ($pointRate === null) {
202 213
                $pointRate = $basicPointRate;
203
            }
204
205
            // TODO: ポイントは税抜き分しか割引されない、ポイント明細は税抜きのままでいいのか?
206 213
            if ($item->isPoint()) {
207 17
                $point = round($item->getPrice() * ($pointRate / 100)) * $item->getQuantity();
208
            } else {
209
                // ポイント = 単価 * ポイント付与率 * 数量
210 212
                $point = round($item->getPriceIncTax() * ($pointRate / 100)) * $item->getQuantity();
211
            }
212
213 213
            return $carry + $point;
214 213
        }, 0);
215
216 213
        return $totalPoint < 0 ? 0 : $totalPoint;
217
    }
218
219
    /**
220
     * 明細追加処理.
221
     *
222
     * @param ItemHolderInterface $itemHolder
223
     * @param $discount
224
     */
225 17
    private function addPointDiscountItem(ItemHolderInterface $itemHolder, $discount)
226
    {
227 17
        $DiscountType = $this->entityManager->find(OrderItemType::class, OrderItemType::POINT);
228 17
        $TaxInclude = $this->entityManager->find(TaxDisplayType::class, TaxDisplayType::INCLUDED);
229 17
        $Taxation = $this->entityManager->find(TaxType::class, TaxType::NON_TAXABLE);
230
231
        // TODO TaxProcessorが先行して実行されるため, 税額等の値は個別にセットする.
232 17
        $OrderItem = new OrderItem();
233 17
        $OrderItem->setProductName($DiscountType->getName())
234 17
            ->setPrice($discount)
235 17
            ->setQuantity(1)
236 17
            ->setTax(0)
237 17
            ->setTaxRate(0)
238 17
            ->setTaxRuleId(null)
239 17
            ->setRoundingType(null)
240 17
            ->setOrderItemType($DiscountType)
241 17
            ->setTaxDisplayType($TaxInclude)
242 17
            ->setTaxType($Taxation)
243 17
            ->setOrder($itemHolder);
244 17
        $itemHolder->addItem($OrderItem);
245
    }
246
247
    /**
248
     * 既存のポイント明細を削除する.
249
     *
250
     * @param ItemHolderInterface $itemHolder
251
     */
252 213
    private function removePointDiscountItem(ItemHolderInterface $itemHolder)
253
    {
254 213
        foreach ($itemHolder->getItems() as $item) {
255 212
            if ($item->isPoint()) {
256
                $itemHolder->removeOrderItem($item);
257 212
                $this->entityManager->remove($item);
258
            }
259
        }
260
    }
261
262
    /**
263
     * ポイントを金額に変換する.
264
     *
265
     * @param $point int ポイント
266
     *
267
     * @return int 金額
268
     */
269 17
    private function pointToPrice($point)
270
    {
271 17
        return intval($point * $this->BaseInfo->getPointConversionRate()) * -1;
272
    }
273
}
274