Completed
Pull Request — 4.0 (#4932)
by Kiyotaka
04:49
created

OrderHelper::setDefaultPayment()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 30

Duplication

Lines 9
Ratio 30 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
nc 6
nop 1
dl 9
loc 30
ccs 0
cts 0
cp 0
crap 20
rs 9.44
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;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Doctrine\ORM\EntityManagerInterface;
19
use Eccube\Entity\Cart;
20
use Eccube\Entity\CartItem;
21
use Eccube\Entity\Customer;
22
use Eccube\Entity\Master\DeviceType;
23
use Eccube\Entity\Master\OrderItemType;
24
use Eccube\Entity\Master\OrderStatus;
25
use Eccube\Entity\Order;
26
use Eccube\Entity\OrderItem;
27
use Eccube\Entity\Shipping;
28
use Eccube\EventListener\SecurityListener;
29
use Eccube\Repository\DeliveryRepository;
30
use Eccube\Repository\Master\DeviceTypeRepository;
31
use Eccube\Repository\Master\OrderItemTypeRepository;
32
use Eccube\Repository\Master\OrderStatusRepository;
33
use Eccube\Repository\Master\PrefRepository;
34
use Eccube\Repository\OrderRepository;
35
use Eccube\Repository\PaymentRepository;
36
use Eccube\Util\StringUtil;
37
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
38
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
39
use Symfony\Component\DependencyInjection\ContainerInterface;
40
use Symfony\Component\HttpFoundation\Session\SessionInterface;
41
42
class OrderHelper
43
{
44
    // FIXME 必要なメソッドのみ移植する
45
    use ControllerTrait;
46
47
    /**
48
     * @var ContainerInterface
49
     */
50
    protected $container;
51
52
    /**
53
     * @var string 非会員情報を保持するセッションのキー
54
     */
55
    const SESSION_NON_MEMBER = 'eccube.front.shopping.nonmember';
56
57
    /**
58
     * @var string 非会員の住所情報を保持するセッションのキー
59
     */
60
    const SESSION_NON_MEMBER_ADDRESSES = 'eccube.front.shopping.nonmember.customeraddress';
61
62
    /**
63
     * @var string 受注IDを保持するセッションのキー
64
     */
65
    const SESSION_ORDER_ID = 'eccube.front.shopping.order.id';
66
67
    /**
68
     * @var string カートが分割されているかどうかのフラグ. 購入フローからのログイン時にカートが分割された場合にtrueがセットされる.
69
     *
70
     * @see SecurityListener
71
     */
72
    const SESSION_CART_DIVIDE_FLAG = 'eccube.front.cart.divide';
73
74
    /**
75
     * @var SessionInterface
76
     */
77
    protected $session;
78
79
    /**
80
     * @var PrefRepository
81
     */
82
    protected $prefRepository;
83
84
    /**
85
     * @var OrderRepository
86
     */
87
    protected $orderRepository;
88
89
    /**
90
     * @var OrderItemTypeRepository
91
     */
92
    protected $orderItemTypeRepository;
93
94
    /**
95
     * @var OrderStatusRepository
96
     */
97
    protected $orderStatusRepository;
98
99
    /**
100
     * @var DeliveryRepository
101
     */
102
    protected $deliveryRepository;
103
104
    /**
105
     * @var PaymentRepository
106
     */
107
    protected $paymentRepository;
108
109
    /**
110
     * @var DeviceTypeRepository
111
     */
112
    protected $deviceTypeRepository;
113
114
    /**
115 175
     * @var MobileDetector
116
     */
117
    protected $mobileDetector;
118
119
    /**
120
     * @var EntityManagerInterface
121
     */
122
    protected $entityManager;
123
124
    public function __construct(
125
        ContainerInterface $container,
126
        EntityManagerInterface $entityManager,
127
        OrderRepository $orderRepository,
128 175
        OrderItemTypeRepository $orderItemTypeRepository,
129 175
        OrderStatusRepository $orderStatusRepository,
130 175
        DeliveryRepository $deliveryRepository,
131 175
        PaymentRepository $paymentRepository,
132 175
        DeviceTypeRepository $deviceTypeRepository,
133 175
        PrefRepository $prefRepository,
134 175
        MobileDetector $mobileDetector,
135 175
        SessionInterface $session
136 175
    ) {
137 175
        $this->container = $container;
138 175
        $this->orderRepository = $orderRepository;
139
        $this->orderStatusRepository = $orderStatusRepository;
140
        $this->orderItemTypeRepository = $orderItemTypeRepository;
141
        $this->deliveryRepository = $deliveryRepository;
142
        $this->paymentRepository = $paymentRepository;
143
        $this->deviceTypeRepository = $deviceTypeRepository;
144
        $this->entityManager = $entityManager;
145
        $this->prefRepository = $prefRepository;
146
        $this->mobileDetector = $mobileDetector;
147
        $this->session = $session;
148
    }
149 51
150
    /**
151 51
     * 購入処理中の受注を生成する.
152 51
     *
153
     * @param Customer $Customer
154 51
     * @param $CartItems
155
     *
156 51
     * @return Order
157
     */
158
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
159
    {
160 51
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
161
        $Order = new Order($OrderStatus);
0 ignored issues
show
Bug introduced by
It seems like $OrderStatus defined by $this->orderStatusReposi...rderStatus::PROCESSING) on line 160 can also be of type object; however, Eccube\Entity\Order::__construct() does only seem to accept null|object<Eccube\Entity\Master\OrderStatus>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
162 51
163 51
        $preOrderId = $this->createPreOrderId();
164
        $Order->setPreOrderId($preOrderId);
165
166 51
        // 顧客情報の設定
167 51
        $this->setCustomer($Order, $Customer);
168
169 51
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
170 51
        $Order->setDeviceType($DeviceType);
0 ignored issues
show
Bug introduced by
It seems like $DeviceType defined by $this->deviceTypeReposit...ceType::DEVICE_TYPE_PC) on line 169 can also be of type object; however, Eccube\Entity\Order::setDeviceType() does only seem to accept null|object<Eccube\Entity\Master\DeviceType>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
171
172 51
        // 明細情報の設定
173 51
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
174
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
175 51
            /* @var OrderItem $item */
176 51
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
177 51
            $result[$saleTypeId][] = $item;
178 51
179 51
            return $result;
180 51
        }, []);
181 51
182
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
183
            $Shipping = $this->createShippingFromCustomer($Customer);
184 51
            $Shipping->setOrder($Order);
185
            $this->addOrderItems($Order, $Shipping, $OrderItems);
186 51
            $this->setDefaultDelivery($Shipping);
187
            $this->entityManager->persist($Shipping);
188
            $Order->addShipping($Shipping);
189 51
        }
190
191
        $this->setDefaultPayment($Order);
192
193
        $this->entityManager->persist($Order);
194
195
        return $Order;
196
    }
197
198
    /**
199 2
     * @param Cart $Cart
200
     *
201 2
     * @return bool
202 2
     */
203
    public function verifyCart(Cart $Cart)
204 2
    {
205 1
        if (count($Cart->getCartItems()) > 0) {
206 1
            $divide = $this->session->get(self::SESSION_CART_DIVIDE_FLAG);
207 1
            if ($divide) {
208 1
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
209 1
210 1
                return false;
211 1
            }
212
213
            return true;
214 2
        }
215
216
        log_info('カートに商品が入っていません。');
217 51
218
        return false;
219
    }
220
221 51
    /**
222
     * 注文手続き画面でログインが必要かどうかの判定
223 51
     *
224
     * @return bool
225 51
     */
226
    public function isLoginRequired()
227
    {
228
        // フォームログイン済はログイン不要
229 51
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
230
            return false;
231 51
        }
232
233
        // Remember Meログイン済の場合はフォームからのログインが必要
234 51
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
235
            return true;
236 51
        }
237 34
238
        // 未ログインだがお客様情報を入力している場合はログイン不要
239
        if (!$this->getUser() && $this->getNonMember()) {
240 51
            return false;
241 51
        }
242
243 51
        return true;
244
    }
245
246
    /**
247
     * 購入処理中の受注を取得する.
248
     *
249
     * @param null|string $preOrderId
250
     *
251
     * @return null|Order
252
     */
253
    public function getPurchaseProcessingOrder($preOrderId = null)
254
    {
255
        if (null === $preOrderId) {
256 51
            return null;
257
        }
258 51
259
        return $this->orderRepository->findOneBy([
260 51
            'pre_order_id' => $preOrderId,
261
            'OrderStatus' => OrderStatus::PROCESSING,
262
        ]);
263 51
    }
264
265 51
    /**
266
     * セッションに保持されている非会員情報を取得する.
267 51
     * 非会員購入時に入力されたお客様情報を返す.
268
     *
269 51
     * @return Customer
270 51
     */
271 51
    public function getNonMember()
272 51
    {
273 51
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
274 51
        if ($NonMember && $NonMember->getPref()) {
275 51
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
276
            $NonMember->setPref($Pref);
277 51
        }
278 51
279 51
        return $NonMember;
280 51
    }
281
282 51
    /**
283 51
     * @param Cart $Cart
284 42
     * @param Customer $Customer
285 42
     *
286
     * @return Order|null
287
     */
288 51
    public function initializeOrder(Cart $Cart, Customer $Customer)
289 51
    {
290
        // 購入処理中の受注情報を取得
291
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
292 51
            return $Order;
293
        }
294 51
295
        // 受注情報を作成
296 51
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
297 51
        $Cart->setPreOrderId($Order->getPreOrderId());
298 51
299 51
        return $Order;
300 51
    }
301 51
302 51
    public function removeSession()
303 51
    {
304 51
        $this->session->remove(self::SESSION_ORDER_ID);
305 51
        $this->session->remove(self::SESSION_ORDER_ID);
306
        $this->session->remove(self::SESSION_NON_MEMBER);
307 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
308
    }
309
310 51
    /**
311
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
312
     *
313 51
     * @param Order $Order
314 51
     * @param Customer $Customer
315
     */
316 51
    public function updateCustomerInfo(Order $Order, Customer $Customer)
317 51
    {
318 51
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
319 51
            $this->setCustomer($Order, $Customer);
320
        }
321
    }
322
323 51
    public function createPreOrderId()
324
    {
325
        // ランダムなpre_order_idを作成
326 51
        do {
327 51
            $preOrderId = sha1(StringUtil::random(32));
328 51
329
            $Order = $this->orderRepository->findOneBy(
330
                [
331 51
                    'pre_order_id' => $preOrderId,
332
                ]
333 51
            );
334
        } while ($Order);
335
336 51
        return $preOrderId;
337
    }
338 51
339 51
    protected function setCustomer(Order $Order, Customer $Customer)
340 51
    {
341
        if ($Customer->getId()) {
342
            $Order->setCustomer($Customer);
343
        }
344 51
345 51
        $Order->copyProperties(
346
            $Customer,
347
            [
348
                'id',
349 51
                'create_date',
350
                'update_date',
351
                'del_flg',
352 51
            ]
353
        );
354
    }
355 51
356 51
    /**
357 51
     * @param Collection|ArrayCollection|CartItem[] $CartItems
358 51
     *
359
     * @return OrderItem[]
360
     */
361
    protected function createOrderItemsFromCartItems($CartItems)
362 51
    {
363
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
364 51
365 51
        return array_map(function ($item) use ($ProductItemType) {
366 51
            /* @var $item CartItem */
367 51
            /* @var $ProductClass \Eccube\Entity\ProductClass */
368 51
            $ProductClass = $item->getProductClass();
369
            /* @var $Product \Eccube\Entity\Product */
370
            $Product = $ProductClass->getProduct();
371
372
            $OrderItem = new OrderItem();
373
            $OrderItem
374
                ->setProduct($Product)
375
                ->setProductClass($ProductClass)
376
                ->setProductName($Product->getName())
377
                ->setProductCode($ProductClass->getCode())
378
                ->setPrice($ProductClass->getPrice02())
379
                ->setQuantity($item->getQuantity())
380
                ->setOrderItemType($ProductItemType);
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 363 can also be of type object; however, Eccube\Entity\OrderItem::setOrderItemType() does only seem to accept null|object<Eccube\Entity\Master\OrderItemType>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
381
382
            $ClassCategory1 = $ProductClass->getClassCategory1();
383
            if (!is_null($ClassCategory1)) {
384
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
385
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
386
            }
387
            $ClassCategory2 = $ProductClass->getClassCategory2();
388
            if (!is_null($ClassCategory2)) {
389
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
390
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
391
            }
392
393
            return $OrderItem;
394
        }, $CartItems instanceof Collection ? $CartItems->toArray() : $CartItems);
395
    }
396
397
    /**
398
     * @param Customer $Customer
399
     *
400
     * @return Shipping
401
     */
402
    protected function createShippingFromCustomer(Customer $Customer)
403
    {
404
        $Shipping = new Shipping();
405
        $Shipping
406
            ->setName01($Customer->getName01())
407
            ->setName02($Customer->getName02())
408
            ->setKana01($Customer->getKana01())
409
            ->setKana02($Customer->getKana02())
410
            ->setCompanyName($Customer->getCompanyName())
411
            ->setPhoneNumber($Customer->getPhoneNumber())
412
            ->setPostalCode($Customer->getPostalCode())
413
            ->setPref($Customer->getPref())
414
            ->setAddr01($Customer->getAddr01())
415
            ->setAddr02($Customer->getAddr02());
416
417
        return $Shipping;
418
    }
419
420
    /**
421
     * @param Shipping $Shipping
422
     */
423
    protected function setDefaultDelivery(Shipping $Shipping)
424
    {
425
        // 配送商品に含まれる販売種別を抽出.
426
        $OrderItems = $Shipping->getOrderItems();
427
        $SaleTypes = [];
428
        /** @var OrderItem $OrderItem */
429 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
430
            $ProductClass = $OrderItem->getProductClass();
431
            $SaleType = $ProductClass->getSaleType();
432
            $SaleTypes[$SaleType->getId()] = $SaleType;
433
        }
434
435
        // 販売種別に紐づく配送業者を取得.
436
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
437
438
        // 初期の配送業者を設定
439
        $Delivery = current($Deliveries);
440
        $Shipping->setDelivery($Delivery);
441
        $Shipping->setShippingDeliveryName($Delivery->getName());
442
    }
443
444
    /**
445
     * @param Order $Order
446
     */
447
    protected function setDefaultPayment(Order $Order)
448
    {
449
        $OrderItems = $Order->getOrderItems();
450
451
        // 受注明細に含まれる販売種別を抽出.
452
        $SaleTypes = [];
453
        /** @var OrderItem $OrderItem */
454 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
455
            $ProductClass = $OrderItem->getProductClass();
456
            if (is_null($ProductClass)) {
457
                // 商品明細のみ対象とする. 送料明細等はスキップする.
458
                continue;
459
            }
460
            $SaleType = $ProductClass->getSaleType();
461
            $SaleTypes[$SaleType->getId()] = $SaleType;
462
        }
463
464
        // 販売種別に紐づく配送業者を抽出
465
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
466
467
        // 利用可能な支払い方法を抽出.
468
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
469
470
        // 初期の支払い方法を設定.
471
        $Payment = current($Payments);
472
        if ($Payment) {
473
            $Order->setPayment($Payment);
474
            $Order->setPaymentMethod($Payment->getMethod());
475
        }
476
    }
477
478
    /**
479
     * @param Order $Order
480
     * @param Shipping $Shipping
481
     * @param array $OrderItems
482
     */
483
    protected function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
484
    {
485
        foreach ($OrderItems as $OrderItem) {
486
            $Shipping->addOrderItem($OrderItem);
487
            $Order->addOrderItem($OrderItem);
488
            $OrderItem->setOrder($Order);
489
            $OrderItem->setShipping($Shipping);
490
        }
491
    }
492
}
493