Completed
Push — dev/4269 ( 0f73fd )
by Kiyotaka
04:20
created

OrderHelper::createOrderItemsFromCartItems()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 1
nop 1
dl 0
loc 41
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
116
    /**
117
     * @var EntityManagerInterface
118
     */
119
    protected $entityManager;
120
121
    /**
122
     * @var MobileDetector
123
     */
124
    protected $mobileDetector;
125
126
    /**
127
     * @var TaxRuleRepository
128
     */
129
    protected $taxRuleRepository;
130
131
    public function __construct(
132
        ContainerInterface $container,
133
        EntityManagerInterface $entityManager,
134
        OrderRepository $orderRepository,
135
        OrderItemTypeRepository $orderItemTypeRepository,
136
        OrderStatusRepository $orderStatusRepository,
137
        DeliveryRepository $deliveryRepository,
138
        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
        $this->deliveryRepository = $deliveryRepository;
150
        $this->paymentRepository = $paymentRepository;
151
        $this->deviceTypeRepository = $deviceTypeRepository;
152
        $this->entityManager = $entityManager;
153
        $this->prefRepository = $prefRepository;
154
        $this->taxRuleRepository = $taxRuleRepository;
155
        $this->mobileDetector = $mobileDetector;
156
        $this->session = $session;
157
    }
158
159
    /**
160
     * 購入処理中の受注を生成する.
161
     *
162
     * @param Customer $Customer
163
     * @param $CartItems
164
     *
165
     * @return Order
166
     */
167
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
168
    {
169
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
170
        $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
        $preOrderId = $this->createPreOrderId();
173
        $Order->setPreOrderId($preOrderId);
174
175
        // 顧客情報の設定
176
        $this->setCustomer($Order, $Customer);
177
178
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
179
        $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
181
        // 明細情報の設定
182
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
183
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
184
            /* @var OrderItem $item */
185
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
186
            $result[$saleTypeId][] = $item;
187
188
            return $result;
189
        }, []);
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
200
        $this->setDefaultPayment($Order);
201
202
        $this->entityManager->persist($Order);
203
204
        return $Order;
205
    }
206
207
    /**
208
     * @param Cart $Cart
209
     *
210
     * @return bool
211
     */
212
    public function verifyCart(Cart $Cart)
213
    {
214
        if (count($Cart->getCartItems()) > 0) {
215
            $divide = $this->session->get(self::SESSION_CART_DIVIDE_FLAG);
216
            if ($divide) {
217
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
218
219
                return false;
220
            }
221
222
            return true;
223
        }
224
225
        log_info('カートに商品が入っていません。');
226
227
        return false;
228
    }
229
230
    /**
231
     * 注文手続き画面でログインが必要かどうかの判定
232
     *
233
     * @return bool
234
     */
235
    public function isLoginRequired()
236
    {
237
        // フォームログイン済はログイン不要
238
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
239
            return false;
240
        }
241
242
        // Remember Meログイン済の場合はフォームからのログインが必要
243
        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
     * 購入処理中の受注を取得する.
257
     *
258
     * @param null|string $preOrderId
259
     *
260
     * @return null|Order
261
     */
262
    public function getPurchaseProcessingOrder($preOrderId = null)
263
    {
264
        if (null === $preOrderId) {
265
            return null;
266
        }
267
268
        return $this->orderRepository->findOneBy([
269
            'pre_order_id' => $preOrderId,
270
            'OrderStatus' => OrderStatus::PROCESSING,
271
        ]);
272
    }
273
274
    /**
275
     * セッションに保持されている非会員情報を取得する.
276
     * 非会員購入時に入力されたお客様情報を返す.
277
     *
278
     * @return Customer
279
     */
280
    public function getNonMember()
281
    {
282
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
283
        if ($NonMember && $NonMember->getPref()) {
284
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
285
            $NonMember->setPref($Pref);
286
        }
287
288
        return $NonMember;
289
    }
290
291
    /**
292
     * @param Cart $Cart
293
     * @param Customer $Customer
294
     *
295
     * @return Order|null
296
     */
297
    public function initializeOrder(Cart $Cart, Customer $Customer)
298
    {
299
        // 購入処理中の受注情報を取得
300
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
301
            return $Order;
302
        }
303
304
        // 受注情報を作成
305
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
306
        $Cart->setPreOrderId($Order->getPreOrderId());
307
308
        return $Order;
309
    }
310
311
    public function removeSession()
312
    {
313
        $this->session->remove(self::SESSION_ORDER_ID);
314
        $this->session->remove(self::SESSION_ORDER_ID);
315
        $this->session->remove(self::SESSION_NON_MEMBER);
316
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
317
    }
318
319
    /**
320
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
321
     *
322
     * @param Order $Order
323
     * @param Customer $Customer
324
     */
325
    public function updateCustomerInfo(Order $Order, Customer $Customer)
326
    {
327
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
328
            $this->setCustomer($Order, $Customer);
329
        }
330
    }
331
332
    public function createPreOrderId()
333
    {
334
        // ランダムなpre_order_idを作成
335
        do {
336
            $preOrderId = sha1(StringUtil::random(32));
337
338
            $Order = $this->orderRepository->findOneBy(
339
                [
340
                    'pre_order_id' => $preOrderId,
341
                ]
342
            );
343
        } while ($Order);
344
345
        return $preOrderId;
346
    }
347
348
    protected function setCustomer(Order $Order, Customer $Customer)
349
    {
350
        if ($Customer->getId()) {
351
            $Order->setCustomer($Customer);
352
        }
353
354
        $Order->copyProperties(
355
            $Customer,
356
            [
357
                'id',
358
                'create_date',
359
                'update_date',
360
                'del_flg',
361
            ]
362
        );
363
    }
364
365
    /**
366
     * @param Collection|ArrayCollection|CartItem[] $CartItems
367
     *
368
     * @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
                ->setTax($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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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