Failed Conditions
Pull Request — 4.0 (#4276)
by Kiyotaka
04:32
created

OrderHelper::createOrderItemsFromCartItems()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
nc 1
nop 1
dl 0
loc 41
ccs 0
cts 0
cp 0
crap 30
rs 8.9528
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\Entity\TaxRule;
29
use Eccube\EventListener\SecurityListener;
30
use Eccube\Repository\DeliveryRepository;
31
use Eccube\Repository\Master\DeviceTypeRepository;
32
use Eccube\Repository\Master\OrderItemTypeRepository;
33
use Eccube\Repository\Master\OrderStatusRepository;
34
use Eccube\Repository\Master\PrefRepository;
35
use Eccube\Repository\OrderRepository;
36
use Eccube\Repository\PaymentRepository;
37
use Eccube\Repository\TaxRuleRepository;
38
use Eccube\Util\StringUtil;
39
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
40
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
41
use Symfony\Component\DependencyInjection\ContainerInterface;
42
use Symfony\Component\HttpFoundation\Session\SessionInterface;
43
44
class OrderHelper
45
{
46
    // FIXME 必要なメソッドのみ移植する
47
    use ControllerTrait;
48
49
    /**
50
     * @var ContainerInterface
51
     */
52
    protected $container;
53
54
    /**
55
     * @var string 非会員情報を保持するセッションのキー
56
     */
57
    const SESSION_NON_MEMBER = 'eccube.front.shopping.nonmember';
58
59
    /**
60
     * @var string 非会員の住所情報を保持するセッションのキー
61
     */
62
    const SESSION_NON_MEMBER_ADDRESSES = 'eccube.front.shopping.nonmember.customeraddress';
63
64
    /**
65
     * @var string 受注IDを保持するセッションのキー
66
     */
67
    const SESSION_ORDER_ID = 'eccube.front.shopping.order.id';
68
69
    /**
70
     * @var string カートが分割されているかどうかのフラグ. 購入フローからのログイン時にカートが分割された場合にtrueがセットされる.
71
     *
72
     * @see SecurityListener
73
     */
74
    const SESSION_CART_DIVIDE_FLAG = 'eccube.front.cart.divide';
75
76
    /**
77
     * @var SessionInterface
78
     */
79
    protected $session;
80
81
    /**
82
     * @var PrefRepository
83
     */
84
    protected $prefRepository;
85
86
    /**
87
     * @var OrderRepository
88
     */
89
    protected $orderRepository;
90
91
    /**
92
     * @var OrderItemTypeRepository
93
     */
94
    protected $orderItemTypeRepository;
95
96
    /**
97
     * @var OrderStatusRepository
98
     */
99
    protected $orderStatusRepository;
100
101
    /**
102
     * @var DeliveryRepository
103
     */
104
    protected $deliveryRepository;
105
106
    /**
107
     * @var PaymentRepository
108
     */
109
    protected $paymentRepository;
110
111
    /**
112
     * @var DeviceTypeRepository
113
     */
114
    protected $deviceTypeRepository;
115 175
116
    /**
117
     * @var EntityManagerInterface
118
     */
119
    protected $entityManager;
120
121
    /**
122
     * @var MobileDetector
123
     */
124
    protected $mobileDetector;
125
126
    /**
127
     * @var TaxRuleRepository
128 175
     */
129 175
    protected $taxRuleRepository;
130 175
131 175
    public function __construct(
132 175
        ContainerInterface $container,
133 175
        EntityManagerInterface $entityManager,
134 175
        OrderRepository $orderRepository,
135 175
        OrderItemTypeRepository $orderItemTypeRepository,
136 175
        OrderStatusRepository $orderStatusRepository,
137 175
        DeliveryRepository $deliveryRepository,
138 175
        PaymentRepository $paymentRepository,
139
        DeviceTypeRepository $deviceTypeRepository,
140
        PrefRepository $prefRepository,
141
        TaxRuleRepository $taxRuleRepository,
142
        MobileDetector $mobileDetector,
143
        SessionInterface $session
144
    ) {
145
        $this->container = $container;
146
        $this->orderRepository = $orderRepository;
147
        $this->orderStatusRepository = $orderStatusRepository;
148
        $this->orderItemTypeRepository = $orderItemTypeRepository;
149 51
        $this->deliveryRepository = $deliveryRepository;
150
        $this->paymentRepository = $paymentRepository;
151 51
        $this->deviceTypeRepository = $deviceTypeRepository;
152 51
        $this->entityManager = $entityManager;
153
        $this->prefRepository = $prefRepository;
154 51
        $this->taxRuleRepository = $taxRuleRepository;
155
        $this->mobileDetector = $mobileDetector;
156 51
        $this->session = $session;
157
    }
158
159
    /**
160 51
     * 購入処理中の受注を生成する.
161
     *
162 51
     * @param Customer $Customer
163 51
     * @param $CartItems
164
     *
165
     * @return Order
166 51
     */
167 51
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
168
    {
169 51
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
170 51
        $Order = new Order($OrderStatus);
0 ignored issues
show
Bug introduced by
It seems like $OrderStatus defined by $this->orderStatusReposi...rderStatus::PROCESSING) on line 169 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...
171
172 51
        $preOrderId = $this->createPreOrderId();
173 51
        $Order->setPreOrderId($preOrderId);
174
175 51
        // 顧客情報の設定
176 51
        $this->setCustomer($Order, $Customer);
177 51
178 51
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
179 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 178 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...
180 51
181 51
        // 明細情報の設定
182
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
183
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
184 51
            /* @var OrderItem $item */
185
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
186 51
            $result[$saleTypeId][] = $item;
187
188
            return $result;
189 51
        }, []);
190
191
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
192
            $Shipping = $this->createShippingFromCustomer($Customer);
193
            $Shipping->setOrder($Order);
194
            $this->addOrderItems($Order, $Shipping, $OrderItems);
195
            $this->setDefaultDelivery($Shipping);
196
            $this->entityManager->persist($Shipping);
197
            $Order->addShipping($Shipping);
198
        }
199 2
200
        $this->setDefaultPayment($Order);
201 2
202 2
        $this->entityManager->persist($Order);
203
204 2
        return $Order;
205 1
    }
206 1
207 1
    /**
208 1
     * @param Cart $Cart
209 1
     *
210 1
     * @return bool
211 1
     */
212
    public function verifyCart(Cart $Cart)
213
    {
214 2
        if (count($Cart->getCartItems()) > 0) {
215
            $divide = $this->session->get(self::SESSION_CART_DIVIDE_FLAG);
216
            if ($divide) {
217 51
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
218
219
                return false;
220
            }
221 51
222
            return true;
223 51
        }
224
225 51
        log_info('カートに商品が入っていません。');
226
227
        return false;
228
    }
229 51
230
    /**
231 51
     * 注文手続き画面でログインが必要かどうかの判定
232
     *
233
     * @return bool
234 51
     */
235
    public function isLoginRequired()
236 51
    {
237 34
        // フォームログイン済はログイン不要
238
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
239
            return false;
240 51
        }
241 51
242
        // Remember Meログイン済の場合はフォームからのログインが必要
243 51
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
244
            return true;
245
        }
246
247
        // 未ログインだがお客様情報を入力している場合はログイン不要
248
        if (!$this->getUser() && $this->getNonMember()) {
249
            return false;
250
        }
251
252
        return true;
253
    }
254
255
    /**
256 51
     * 購入処理中の受注を取得する.
257
     *
258 51
     * @param null|string $preOrderId
259
     *
260 51
     * @return null|Order
261
     */
262
    public function getPurchaseProcessingOrder($preOrderId = null)
263 51
    {
264
        if (null === $preOrderId) {
265 51
            return null;
266
        }
267 51
268
        return $this->orderRepository->findOneBy([
269 51
            'pre_order_id' => $preOrderId,
270 51
            'OrderStatus' => OrderStatus::PROCESSING,
271 51
        ]);
272 51
    }
273 51
274 51
    /**
275 51
     * セッションに保持されている非会員情報を取得する.
276
     * 非会員購入時に入力されたお客様情報を返す.
277 51
     *
278 51
     * @return Customer
279 51
     */
280 51
    public function getNonMember()
281
    {
282 51
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
283 51
        if ($NonMember && $NonMember->getPref()) {
284 42
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
285 42
            $NonMember->setPref($Pref);
286
        }
287
288 51
        return $NonMember;
289 51
    }
290
291
    /**
292 51
     * @param Cart $Cart
293
     * @param Customer $Customer
294 51
     *
295
     * @return Order|null
296 51
     */
297 51
    public function initializeOrder(Cart $Cart, Customer $Customer)
298 51
    {
299 51
        // 購入処理中の受注情報を取得
300 51
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
301 51
            return $Order;
302 51
        }
303 51
304 51
        // 受注情報を作成
305 51
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
306
        $Cart->setPreOrderId($Order->getPreOrderId());
307 51
308
        return $Order;
309
    }
310 51
311
    public function removeSession()
312
    {
313 51
        $this->session->remove(self::SESSION_ORDER_ID);
314 51
        $this->session->remove(self::SESSION_ORDER_ID);
315
        $this->session->remove(self::SESSION_NON_MEMBER);
316 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
317 51
    }
318 51
319 51
    /**
320
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
321
     *
322
     * @param Order $Order
323 51
     * @param Customer $Customer
324
     */
325
    public function updateCustomerInfo(Order $Order, Customer $Customer)
326 51
    {
327 51
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
328 51
            $this->setCustomer($Order, $Customer);
329
        }
330
    }
331 51
332
    public function createPreOrderId()
333 51
    {
334
        // ランダムなpre_order_idを作成
335
        do {
336 51
            $preOrderId = sha1(StringUtil::random(32));
337
338 51
            $Order = $this->orderRepository->findOneBy(
339 51
                [
340 51
                    'pre_order_id' => $preOrderId,
341
                ]
342
            );
343
        } while ($Order);
344 51
345 51
        return $preOrderId;
346
    }
347
348
    protected function setCustomer(Order $Order, Customer $Customer)
349 51
    {
350
        if ($Customer->getId()) {
351
            $Order->setCustomer($Customer);
352 51
        }
353
354
        $Order->copyProperties(
355 51
            $Customer,
356 51
            [
357 51
                'id',
358 51
                'create_date',
359
                'update_date',
360
                'del_flg',
361
            ]
362 51
        );
363
    }
364 51
365 51
    /**
366 51
     * @param Collection|ArrayCollection|CartItem[] $CartItems
367 51
     *
368 51
     * @return OrderItem[]
369
     */
370
    protected function createOrderItemsFromCartItems($CartItems)
371
    {
372
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
373
374
        return array_map(function ($item) use ($ProductItemType) {
375
            /* @var $item CartItem */
376
            /* @var $ProductClass \Eccube\Entity\ProductClass */
377
            $ProductClass = $item->getProductClass();
378
            /* @var $Product \Eccube\Entity\Product */
379
            $Product = $ProductClass->getProduct();
380
            /** @var TaxRule $TaxRule */
381
            $TaxRule = $this->taxRuleRepository->getByRule($Product, $ProductClass)
382
                ?: $this->taxRuleRepository->getByRule();
383
384
            $OrderItem = new OrderItem();
385
            $OrderItem
386
                ->setProduct($Product)
387
                ->setProductClass($ProductClass)
388
                ->setProductName($Product->getName())
389
                ->setProductCode($ProductClass->getCode())
390
                ->setPrice($ProductClass->getPrice02())
391
                ->setQuantity($item->getQuantity())
392
                ->setOrderItemType($ProductItemType)
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 372 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...
393
                ->setTaxRate($TaxRule->getTaxRate())
394
                ->setTaxAdjust($TaxRule->getTaxAdjust())
395
                ->setRoundingType($TaxRule->getRoundingType());
396
397
            $ClassCategory1 = $ProductClass->getClassCategory1();
398
            if (!is_null($ClassCategory1)) {
399
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
400
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
401
            }
402
            $ClassCategory2 = $ProductClass->getClassCategory2();
403
            if (!is_null($ClassCategory2)) {
404
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
405
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
406
            }
407
408
            return $OrderItem;
409
        }, $CartItems instanceof Collection ? $CartItems->toArray() : $CartItems);
410
    }
411
412
    /**
413
     * @param Customer $Customer
414
     *
415
     * @return Shipping
416
     */
417
    protected function createShippingFromCustomer(Customer $Customer)
418
    {
419
        $Shipping = new Shipping();
420
        $Shipping
421
            ->setName01($Customer->getName01())
422
            ->setName02($Customer->getName02())
423
            ->setKana01($Customer->getKana01())
424
            ->setKana02($Customer->getKana02())
425
            ->setCompanyName($Customer->getCompanyName())
426
            ->setPhoneNumber($Customer->getPhoneNumber())
427
            ->setPostalCode($Customer->getPostalCode())
428
            ->setPref($Customer->getPref())
429
            ->setAddr01($Customer->getAddr01())
430
            ->setAddr02($Customer->getAddr02());
431
432
        return $Shipping;
433
    }
434
435
    /**
436
     * @param Shipping $Shipping
437
     */
438
    protected function setDefaultDelivery(Shipping $Shipping)
439
    {
440
        // 配送商品に含まれる販売種別を抽出.
441
        $OrderItems = $Shipping->getOrderItems();
442
        $SaleTypes = [];
443
        /** @var OrderItem $OrderItem */
444 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
445
            $ProductClass = $OrderItem->getProductClass();
446
            $SaleType = $ProductClass->getSaleType();
447
            $SaleTypes[$SaleType->getId()] = $SaleType;
448
        }
449
450
        // 販売種別に紐づく配送業者を取得.
451
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
452
453
        // 初期の配送業者を設定
454
        $Delivery = current($Deliveries);
455
        $Shipping->setDelivery($Delivery);
456
        $Shipping->setShippingDeliveryName($Delivery->getName());
457
    }
458
459
    /**
460
     * @param Order $Order
461
     */
462
    protected function setDefaultPayment(Order $Order)
463
    {
464
        $OrderItems = $Order->getOrderItems();
465
466
        // 受注明細に含まれる販売種別を抽出.
467
        $SaleTypes = [];
468
        /** @var OrderItem $OrderItem */
469 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
470
            $ProductClass = $OrderItem->getProductClass();
471
            if (is_null($ProductClass)) {
472
                // 商品明細のみ対象とする. 送料明細等はスキップする.
473
                continue;
474
            }
475
            $SaleType = $ProductClass->getSaleType();
476
            $SaleTypes[$SaleType->getId()] = $SaleType;
477
        }
478
479
        // 販売種別に紐づく配送業者を抽出
480
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
481
482
        // 利用可能な支払い方法を抽出.
483
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
484
485
        // 初期の支払い方法を設定.
486
        $Payment = current($Payments);
487
        if ($Payment) {
488
            $Order->setPayment($Payment);
489
            $Order->setPaymentMethod($Payment->getMethod());
490
        }
491
    }
492
493
    /**
494
     * @param Order $Order
495
     * @param Shipping $Shipping
496
     * @param array $OrderItems
497
     */
498
    protected function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
499
    {
500
        foreach ($OrderItems as $OrderItem) {
501
            $Shipping->addOrderItem($OrderItem);
502
            $Order->addOrderItem($OrderItem);
503
            $OrderItem->setOrder($Order);
504
            $OrderItem->setShipping($Shipping);
505
        }
506
    }
507
}
508