Failed Conditions
Pull Request — 4.0 (#4276)
by Kiyotaka
05:16
created

OrderHelper::createPreOrderId()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0116

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 0
dl 0
loc 15
ccs 6
cts 7
cp 0.8571
crap 2.0116
rs 9.7666
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
        /** @var OrderStatus $OrderStatus */
170 51
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
171
        $Order = new Order($OrderStatus);
172 51
173 51
        $preOrderId = $this->createPreOrderId();
174
        $Order->setPreOrderId($preOrderId);
175 51
176 51
        // 顧客情報の設定
177 51
        $this->setCustomer($Order, $Customer);
178 51
179 51
        /** @var DeviceType $DeviceType */
180 51
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
181 51
        $Order->setDeviceType($DeviceType);
182
183
        // 明細情報の設定
184 51
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
185
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
186 51
            /* @var OrderItem $item */
187
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
188
            $result[$saleTypeId][] = $item;
189 51
190
            return $result;
191
        }, []);
192
193
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
194
            $Shipping = $this->createShippingFromCustomer($Customer);
195
            $Shipping->setOrder($Order);
196
            $this->addOrderItems($Order, $Shipping, $OrderItems);
197
            $this->setDefaultDelivery($Shipping);
198
            $this->entityManager->persist($Shipping);
199 2
            $Order->addShipping($Shipping);
200
        }
201 2
202 2
        $this->setDefaultPayment($Order);
203
204 2
        $this->entityManager->persist($Order);
205 1
206 1
        return $Order;
207 1
    }
208 1
209 1
    /**
210 1
     * @param Cart $Cart
211 1
     *
212
     * @return bool
213
     */
214 2
    public function verifyCart(Cart $Cart)
215
    {
216
        if (count($Cart->getCartItems()) > 0) {
217 51
            $divide = $this->session->get(self::SESSION_CART_DIVIDE_FLAG);
218
            if ($divide) {
219
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
220
221 51
                return false;
222
            }
223 51
224
            return true;
225 51
        }
226
227
        log_info('カートに商品が入っていません。');
228
229 51
        return false;
230
    }
231 51
232
    /**
233
     * 注文手続き画面でログインが必要かどうかの判定
234 51
     *
235
     * @return bool
236 51
     */
237 34
    public function isLoginRequired()
238
    {
239
        // フォームログイン済はログイン不要
240 51
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
241 51
            return false;
242
        }
243 51
244
        // Remember Meログイン済の場合はフォームからのログインが必要
245
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
246
            return true;
247
        }
248
249
        // 未ログインだがお客様情報を入力している場合はログイン不要
250
        if (!$this->getUser() && $this->getNonMember()) {
251
            return false;
252
        }
253
254
        return true;
255
    }
256 51
257
    /**
258 51
     * 購入処理中の受注を取得する.
259
     *
260 51
     * @param null|string $preOrderId
261
     *
262
     * @return null|Order
263 51
     */
264
    public function getPurchaseProcessingOrder($preOrderId = null)
265 51
    {
266
        if (null === $preOrderId) {
267 51
            return null;
268
        }
269 51
270 51
        return $this->orderRepository->findOneBy([
271 51
            'pre_order_id' => $preOrderId,
272 51
            'OrderStatus' => OrderStatus::PROCESSING,
273 51
        ]);
274 51
    }
275 51
276
    /**
277 51
     * セッションに保持されている非会員情報を取得する.
278 51
     * 非会員購入時に入力されたお客様情報を返す.
279 51
     *
280 51
     * @return Customer
281
     */
282 51
    public function getNonMember()
283 51
    {
284 42
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
285 42
        if ($NonMember && $NonMember->getPref()) {
286
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
287
            $NonMember->setPref($Pref);
288 51
        }
289 51
290
        return $NonMember;
291
    }
292 51
293
    /**
294 51
     * @param Cart $Cart
295
     * @param Customer $Customer
296 51
     *
297 51
     * @return Order|null
298 51
     */
299 51
    public function initializeOrder(Cart $Cart, Customer $Customer)
300 51
    {
301 51
        // 購入処理中の受注情報を取得
302 51
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
303 51
            return $Order;
304 51
        }
305 51
306
        // 受注情報を作成
307 51
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
308
        $Cart->setPreOrderId($Order->getPreOrderId());
309
310 51
        return $Order;
311
    }
312
313 51
    public function removeSession()
314 51
    {
315
        $this->session->remove(self::SESSION_ORDER_ID);
316 51
        $this->session->remove(self::SESSION_ORDER_ID);
317 51
        $this->session->remove(self::SESSION_NON_MEMBER);
318 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
319 51
    }
320
321
    /**
322
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
323 51
     *
324
     * @param Order $Order
325
     * @param Customer $Customer
326 51
     */
327 51
    public function updateCustomerInfo(Order $Order, Customer $Customer)
328 51
    {
329
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
330
            $this->setCustomer($Order, $Customer);
331 51
        }
332
    }
333 51
334
    public function createPreOrderId()
335
    {
336 51
        // ランダムなpre_order_idを作成
337
        do {
338 51
            $preOrderId = sha1(StringUtil::random(32));
339 51
340 51
            $Order = $this->orderRepository->findOneBy(
341
                [
342
                    'pre_order_id' => $preOrderId,
343
                ]
344 51
            );
345 51
        } while ($Order);
346
347
        return $preOrderId;
348
    }
349 51
350
    protected function setCustomer(Order $Order, Customer $Customer)
351
    {
352 51
        if ($Customer->getId()) {
353
            $Order->setCustomer($Customer);
354
        }
355 51
356 51
        $Order->copyProperties(
357 51
            $Customer,
358 51
            [
359
                'id',
360
                'create_date',
361
                'update_date',
362 51
                'del_flg',
363
            ]
364 51
        );
365 51
    }
366 51
367 51
    /**
368 51
     * @param Collection|ArrayCollection|CartItem[] $CartItems
369
     *
370
     * @return OrderItem[]
371
     */
372
    protected function createOrderItemsFromCartItems($CartItems)
373
    {
374
        /** @var OrderItemType $ProductItemType */
375
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
376
377
        return array_map(function ($item) use ($ProductItemType) {
378
            /* @var $item CartItem */
379
            /* @var $ProductClass \Eccube\Entity\ProductClass */
380
            $ProductClass = $item->getProductClass();
381
            /* @var $Product \Eccube\Entity\Product */
382
            $Product = $ProductClass->getProduct();
383
            /** @var TaxRule $TaxRule */
384
            $TaxRule = $this->taxRuleRepository->getByRule($Product, $ProductClass)
385
                ?: $this->taxRuleRepository->getByRule();
386
387
            $OrderItem = new OrderItem();
388
            $OrderItem
389
                ->setProduct($Product)
390
                ->setProductClass($ProductClass)
391
                ->setProductName($Product->getName())
392
                ->setProductCode($ProductClass->getCode())
393
                ->setPrice($ProductClass->getPrice02())
394
                ->setQuantity($item->getQuantity())
395
                ->setOrderItemType($ProductItemType)
396
                ->setTaxRate($TaxRule->getTaxRate())
397
                ->setTaxAdjust($TaxRule->getTaxAdjust())
398
                ->setRoundingType($TaxRule->getRoundingType());
399
400
            $ClassCategory1 = $ProductClass->getClassCategory1();
401
            if (!is_null($ClassCategory1)) {
402
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
403
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
404
            }
405
            $ClassCategory2 = $ProductClass->getClassCategory2();
406
            if (!is_null($ClassCategory2)) {
407
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
408
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
409
            }
410
411
            return $OrderItem;
412
        }, $CartItems instanceof Collection ? $CartItems->toArray() : $CartItems);
413
    }
414
415
    /**
416
     * @param Customer $Customer
417
     *
418
     * @return Shipping
419
     */
420
    protected function createShippingFromCustomer(Customer $Customer)
421
    {
422
        $Shipping = new Shipping();
423
        $Shipping
424
            ->setName01($Customer->getName01())
425
            ->setName02($Customer->getName02())
426
            ->setKana01($Customer->getKana01())
427
            ->setKana02($Customer->getKana02())
428
            ->setCompanyName($Customer->getCompanyName())
429
            ->setPhoneNumber($Customer->getPhoneNumber())
430
            ->setPostalCode($Customer->getPostalCode())
431
            ->setPref($Customer->getPref())
432
            ->setAddr01($Customer->getAddr01())
433
            ->setAddr02($Customer->getAddr02());
434
435
        return $Shipping;
436
    }
437
438
    /**
439
     * @param Shipping $Shipping
440
     */
441
    protected function setDefaultDelivery(Shipping $Shipping)
442
    {
443
        // 配送商品に含まれる販売種別を抽出.
444
        $OrderItems = $Shipping->getOrderItems();
445
        $SaleTypes = [];
446
        /** @var OrderItem $OrderItem */
447 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
448
            $ProductClass = $OrderItem->getProductClass();
449
            $SaleType = $ProductClass->getSaleType();
450
            $SaleTypes[$SaleType->getId()] = $SaleType;
451
        }
452
453
        // 販売種別に紐づく配送業者を取得.
454
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
455
456
        // 初期の配送業者を設定
457
        $Delivery = current($Deliveries);
458
        $Shipping->setDelivery($Delivery);
459
        $Shipping->setShippingDeliveryName($Delivery->getName());
460
    }
461
462
    /**
463
     * @param Order $Order
464
     */
465
    protected function setDefaultPayment(Order $Order)
466
    {
467
        $OrderItems = $Order->getOrderItems();
468
469
        // 受注明細に含まれる販売種別を抽出.
470
        $SaleTypes = [];
471
        /** @var OrderItem $OrderItem */
472 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
473
            $ProductClass = $OrderItem->getProductClass();
474
            if (is_null($ProductClass)) {
475
                // 商品明細のみ対象とする. 送料明細等はスキップする.
476
                continue;
477
            }
478
            $SaleType = $ProductClass->getSaleType();
479
            $SaleTypes[$SaleType->getId()] = $SaleType;
480
        }
481
482
        // 販売種別に紐づく配送業者を抽出
483
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
484
485
        // 利用可能な支払い方法を抽出.
486
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
487
488
        // 初期の支払い方法を設定.
489
        $Payment = current($Payments);
490
        if ($Payment) {
491
            $Order->setPayment($Payment);
492
            $Order->setPaymentMethod($Payment->getMethod());
493
        }
494
    }
495
496
    /**
497
     * @param Order $Order
498
     * @param Shipping $Shipping
499
     * @param array $OrderItems
500
     */
501
    protected function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
502
    {
503
        foreach ($OrderItems as $OrderItem) {
504
            $Shipping->addOrderItem($OrderItem);
505
            $Order->addOrderItem($OrderItem);
506
            $OrderItem->setOrder($Order);
507
            $OrderItem->setShipping($Shipping);
508
        }
509
    }
510
}
511