Failed Conditions
Push — experimental/sf ( 069d05...1f2df0 )
by chihiro
157:56 queued 150:35
created

Eccube/Controller/Admin/Order/OrderController.php (6 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Controller\Admin\Order;
15
16
use Eccube\Common\Constant;
17
use Eccube\Controller\AbstractController;
18
use Eccube\Entity\Csv;
19
use Eccube\Entity\ExportCsvRow;
20
use Eccube\Entity\Master\CsvType;
21
use Eccube\Entity\OrderItem;
22
use Eccube\Event\EccubeEvents;
23
use Eccube\Event\EventArgs;
24
use Eccube\Form\Type\Admin\SearchOrderType;
25
use Eccube\Repository\CustomerRepository;
26
use Eccube\Repository\Master\OrderStatusRepository;
27
use Eccube\Repository\Master\PageMaxRepository;
28
use Eccube\Repository\Master\ProductStatusRepository;
29
use Eccube\Repository\Master\SexRepository;
30
use Eccube\Repository\OrderRepository;
31
use Eccube\Repository\PaymentRepository;
32
use Eccube\Service\CsvExportService;
33
use Eccube\Service\MailService;
34
use Eccube\Service\OrderStateMachine;
35
use Eccube\Util\FormUtil;
36
use Knp\Component\Pager\PaginatorInterface;
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
38
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
39
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
40
use Symfony\Component\HttpFoundation\Response;
41
use Symfony\Component\HttpFoundation\Request;
42
use Symfony\Component\HttpFoundation\StreamedResponse;
43
use Eccube\Entity\Master\OrderStatus;
44
use Symfony\Component\HttpFoundation\RedirectResponse;
45
use Eccube\Entity\Order;
46
use Eccube\Entity\Shipping;
47
use Eccube\Service\PurchaseFlow\PurchaseFlow;
48
use Symfony\Component\Validator\Constraints as Assert;
49
use Symfony\Component\Validator\Validator\ValidatorInterface;
50
51
class OrderController extends AbstractController
52
{
53
    /**
54
     * @var PurchaseFlow
55
     */
56
    protected $purchaseFlow;
57
58
    /**
59
     * @var CsvExportService
60
     */
61
    protected $csvExportService;
62
63
    /**
64
     * @var CustomerRepository
65
     */
66
    protected $customerRepository;
67
68
    /**
69
     * @var PaymentRepository
70
     */
71
    protected $paymentRepository;
72
73
    /**
74
     * @var SexRepository
75
     */
76
    protected $sexRepository;
77
78
    /**
79
     * @var OrderStatusRepository
80
     */
81
    protected $orderStatusRepository;
82
83
    /**
84
     * @var PageMaxRepository
85
     */
86
    protected $pageMaxRepository;
87
88
    /**
89
     * @var ProductStatusRepository
90
     */
91
    protected $productStatusRepository;
92
93
    /**
94
     * @var OrderRepository
95
     */
96
    protected $orderRepository;
97
98
    /**
99
     * @var ValidatorInterface
100
     */
101
    protected $validator;
102
103
    /**
104
     * @var OrderStateMachine
105
     */
106
    protected $orderStateMachine;
107
108
    /**
109
     * @var MailService
110
     */
111
    protected $mailService;
112
113
    /**
114
     * OrderController constructor.
115
     *
116
     * @param PurchaseFlow $orderPurchaseFlow
117
     * @param CsvExportService $csvExportService
118
     * @param CustomerRepository $customerRepository
119
     * @param PaymentRepository $paymentRepository
120
     * @param SexRepository $sexRepository
121
     * @param OrderStatusRepository $orderStatusRepository
122
     * @param PageMaxRepository $pageMaxRepository
123
     * @param ProductStatusRepository $productStatusRepository
124
     * @param OrderRepository $orderRepository
125
     * @param OrderStateMachine $orderStateMachine
0 ignored issues
show
Doc comment for parameter $orderStateMachine does not match actual variable name $validator
Loading history...
126
     * @param MailService $mailService
0 ignored issues
show
Expected 13 spaces after parameter type; 1 found
Loading history...
Doc comment for parameter $mailService does not match actual variable name $orderStateMachine
Loading history...
127
     */
128 16
    public function __construct(
129
        PurchaseFlow $orderPurchaseFlow,
130
        CsvExportService $csvExportService,
131
        CustomerRepository $customerRepository,
132
        PaymentRepository $paymentRepository,
133
        SexRepository $sexRepository,
134
        OrderStatusRepository $orderStatusRepository,
135
        PageMaxRepository $pageMaxRepository,
136
        ProductStatusRepository $productStatusRepository,
137
        OrderRepository $orderRepository,
138
        ValidatorInterface $validator,
139
        OrderStateMachine $orderStateMachine,
140
        MailService $mailService
141
    ) {
142 16
        $this->purchaseFlow = $orderPurchaseFlow;
143 16
        $this->csvExportService = $csvExportService;
144 16
        $this->customerRepository = $customerRepository;
145 16
        $this->paymentRepository = $paymentRepository;
146 16
        $this->sexRepository = $sexRepository;
147 16
        $this->orderStatusRepository = $orderStatusRepository;
148 16
        $this->pageMaxRepository = $pageMaxRepository;
149 16
        $this->productStatusRepository = $productStatusRepository;
150 16
        $this->orderRepository = $orderRepository;
151 16
        $this->validator = $validator;
152 16
        $this->orderStateMachine = $orderStateMachine;
153 16
        $this->mailService = $mailService;
154
    }
155
156
    /**
0 ignored issues
show
Doc comment for parameter "$request" missing
Loading history...
Doc comment for parameter "$page_no" missing
Loading history...
Doc comment for parameter "$paginator" missing
Loading history...
157
     * 受注一覧画面.
158
     *
159
     * - 検索条件, ページ番号, 表示件数はセッションに保持されます.
160
     * - クエリパラメータでresume=1が指定された場合、検索条件, ページ番号, 表示件数をセッションから復旧します.
161
     * - 各データの, セッションに保持するアクションは以下の通りです.
162
     *   - 検索ボタン押下時
163
     *      - 検索条件をセッションに保存します
164
     *      - ページ番号は1で初期化し、セッションに保存します。
165
     *   - 表示件数変更時
166
     *      - クエリパラメータpage_countをセッションに保存します。
167
     *      - ただし, mtb_page_maxと一致しない場合, eccube_default_page_countが保存されます.
168
     *   - ページング時
169
     *      - URLパラメータpage_noをセッションに保存します.
170
     *   - 初期表示
171
     *      - 検索条件は空配列, ページ番号は1で初期化し, セッションに保存します.
172
     *
173
     * @Route("/%eccube_admin_route%/order", name="admin_order")
174
     * @Route("/%eccube_admin_route%/order/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_order_page")
175
     * @Template("@admin/Order/index.twig")
176
     */
177 8
    public function index(Request $request, $page_no = null, PaginatorInterface $paginator)
178
    {
179 8
        $builder = $this->formFactory
180 8
            ->createBuilder(SearchOrderType::class);
181
182 8
        $event = new EventArgs(
183
            [
184 8
                'builder' => $builder,
185
            ],
186 8
            $request
187
        );
188 8
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_ORDER_INDEX_INITIALIZE, $event);
189
190 8
        $searchForm = $builder->getForm();
191
192
        /**
193
         * ページの表示件数は, 以下の順に優先される.
194
         * - リクエストパラメータ
195
         * - セッション
196
         * - デフォルト値
197
         * また, セッションに保存する際は mtb_page_maxと照合し, 一致した場合のみ保存する.
198
         **/
199 8
        $page_count = $this->session->get('eccube.admin.order.search.page_count',
200 8
                $this->eccubeConfig->get('eccube_default_page_count'));
201
202 8
        $page_count_param = (int) $request->get('page_count');
203 8
        $pageMaxis = $this->pageMaxRepository->findAll();
204
205 8
        if ($page_count_param) {
206 1
            foreach ($pageMaxis as $pageMax) {
207 1
                if ($page_count_param == $pageMax->getName()) {
208
                    $page_count = $pageMax->getName();
209
                    $this->session->set('eccube.admin.order.search.page_count', $page_count);
210 1
                    break;
211
                }
212
            }
213
        }
214
215 8
        if ('POST' === $request->getMethod()) {
216 6
            $searchForm->handleRequest($request);
217
218 6
            if ($searchForm->isValid()) {
219
                /**
220
                 * 検索が実行された場合は, セッションに検索条件を保存する.
221
                 * ページ番号は最初のページ番号に初期化する.
222
                 */
223 5
                $page_no = 1;
224 5
                $searchData = $searchForm->getData();
225
226
                // 検索条件, ページ番号をセッションに保持.
227 5
                $this->session->set('eccube.admin.order.search', FormUtil::getViewData($searchForm));
228 5
                $this->session->set('eccube.admin.order.search.page_no', $page_no);
229
            } else {
230
                // 検索エラーの際は, 詳細検索枠を開いてエラー表示する.
231
                return [
232 6
                    'searchForm' => $searchForm->createView(),
233
                    'pagination' => [],
234 1
                    'pageMaxis' => $pageMaxis,
235 1
                    'page_no' => $page_no,
236 1
                    'page_count' => $page_count,
237
                    'has_errors' => true,
238
                ];
239
            }
240
        } else {
241 3
            if (null !== $page_no || $request->get('resume')) {
242
                /*
243
                 * ページ送りの場合または、他画面から戻ってきた場合は, セッションから検索条件を復旧する.
244
                 */
245 1
                if ($page_no) {
246
                    // ページ送りで遷移した場合.
247 1
                    $this->session->set('eccube.admin.order.search.page_no', (int) $page_no);
248
                } else {
249
                    // 他画面から遷移した場合.
250
                    $page_no = $this->session->get('eccube.admin.order.search.page_no', 1);
251
                }
252 1
                $viewData = $this->session->get('eccube.admin.order.search', []);
253 1
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
254
            } else {
255
                /**
256
                 * 初期表示の場合.
257
                 */
258 2
                $page_no = 1;
259 2
                $viewData = [];
260
261 2
                if ($statusId = (int) $request->get('order_status_id')) {
262
                    $viewData = ['status' => $statusId];
263
                }
264
265 2
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
266
267
                // セッション中の検索条件, ページ番号を初期化.
268 2
                $this->session->set('eccube.admin.order.search', $viewData);
269 2
                $this->session->set('eccube.admin.order.search.page_no', $page_no);
270
            }
271
        }
272
273 8
        $qb = $this->orderRepository->getQueryBuilderBySearchDataForAdmin($searchData);
274
275 8
        $event = new EventArgs(
276
            [
277 8
                'qb' => $qb,
278 8
                'searchData' => $searchData,
279
            ],
280 8
            $request
281
        );
282
283 8
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_ORDER_INDEX_SEARCH, $event);
284
285 8
        $pagination = $paginator->paginate(
286 8
            $qb,
287 8
            $page_no,
288 8
            $page_count
289
        );
290
291
        return [
292 8
            'searchForm' => $searchForm->createView(),
293 8
            'pagination' => $pagination,
294 8
            'pageMaxis' => $pageMaxis,
295 8
            'page_no' => $page_no,
296 8
            'page_count' => $page_count,
297
            'has_errors' => false,
298 8
            'OrderStatuses' => $this->orderStatusRepository->findBy([], ['sort_no' => 'ASC']),
299
        ];
300
    }
301
302
    /**
303
     * @Method("POST")
304
     * @Route("/%eccube_admin_route%/order/bulk_delete", name="admin_order_bulk_delete")
305
     */
306 1
    public function bulkDelete(Request $request)
307
    {
308 1
        $this->isTokenValid();
309 1
        $ids = $request->get('ids');
310 1
        foreach ($ids as $order_id) {
311 1
            $Order = $this->orderRepository
312 1
                ->find($order_id);
313 1
            if ($Order) {
314 1
                $this->entityManager->remove($Order);
315 1
                log_info('受注削除', [$Order->getId()]);
316
            }
317
        }
318
319 1
        $this->entityManager->flush();
320
321 1
        $this->addSuccess('admin.order.delete.complete', 'admin');
322
323 1
        return $this->redirect($this->generateUrl('admin_order', ['resume' => Constant::ENABLED]));
324
    }
325
326
    /**
327
     * 受注CSVの出力.
328
     *
329
     * @Route("/%eccube_admin_route%/order/export/order", name="admin_order_export_order")
330
     *
331
     * @param Request $request
332
     *
333
     * @return StreamedResponse
334
     */
335 1 View Code Duplication
    public function exportOrder(Request $request)
336
    {
337 1
        $filename = 'order_'.(new \DateTime())->format('YmdHis').'.csv';
338 1
        $response = $this->exportCsv($request, CsvType::CSV_TYPE_ORDER, $filename);
339 1
        log_info('受注CSV出力ファイル名', [$filename]);
340
341 1
        return $response;
342
    }
343
344
    /**
345
     * 配送CSVの出力.
346
     *
347
     * @Route("/%eccube_admin_route%/order/export/shipping", name="admin_order_export_shipping")
348
     *
349
     * @param Request $request
350
     *
351
     * @return StreamedResponse
352
     */
353 View Code Duplication
    public function exportShipping(Request $request)
354
    {
355
        $filename = 'shipping_'.(new \DateTime())->format('YmdHis').'.csv';
356
        $response = $this->exportCsv($request, CsvType::CSV_TYPE_SHIPPING, $filename);
357
        log_info('配送CSV出力ファイル名', [$filename]);
358
359
        return $response;
360
    }
361
362
    /**
363
     * @param Request $request
364
     * @param $csvTypeId
365
     * @param $fileName
366
     *
367
     * @return StreamedResponse
368
     */
369 1
    private function exportCsv(Request $request, $csvTypeId, $fileName)
370
    {
371
        // タイムアウトを無効にする.
372 1
        set_time_limit(0);
373
374
        // sql loggerを無効にする.
375 1
        $em = $this->entityManager;
376 1
        $em->getConfiguration()->setSQLLogger(null);
377
378 1
        $response = new StreamedResponse();
379 1
        $response->setCallback(function () use ($request, $csvTypeId) {
380
            // CSV種別を元に初期化.
381 1
            $this->csvExportService->initCsvType($csvTypeId);
382
383
            // ヘッダ行の出力.
384 1
            $this->csvExportService->exportHeader();
385
386
            // 受注データ検索用のクエリビルダを取得.
387 1
            $qb = $this->csvExportService
388 1
                ->getOrderQueryBuilder($request);
389
390
            // データ行の出力.
391 1
            $this->csvExportService->setExportQueryBuilder($qb);
392 1
            $this->csvExportService->exportData(function ($entity, $csvService) use ($request) {
393 1
                $Csvs = $csvService->getCsvs();
394
395 1
                $Order = $entity;
396 1
                $OrderItems = $Order->getOrderItems();
397
398 1
                foreach ($OrderItems as $OrderItem) {
399 1
                    $ExportCsvRow = new ExportCsvRow();
400
401
                    // CSV出力項目と合致するデータを取得.
402 1
                    foreach ($Csvs as $Csv) {
403
                        // 受注データを検索.
404 1
                        $ExportCsvRow->setData($csvService->getData($Csv, $Order));
405 1
                        if ($ExportCsvRow->isDataNull()) {
406
                            // 受注データにない場合は, 受注明細を検索.
407 1
                            $ExportCsvRow->setData($csvService->getData($Csv, $OrderItem));
408
                        }
409 1
                        if ($ExportCsvRow->isDataNull() && $Shipping = $OrderItem->getShipping()) {
410
                            // 受注明細データにない場合は, 出荷を検索.
411 1
                            $ExportCsvRow->setData($csvService->getData($Csv, $Shipping));
412
                        }
413
414 1
                        $event = new EventArgs(
415
                            [
416 1
                                'csvService' => $csvService,
417 1
                                'Csv' => $Csv,
418 1
                                'OrderItem' => $OrderItem,
419 1
                                'ExportCsvRow' => $ExportCsvRow,
420
                            ],
421 1
                            $request
422
                        );
423 1
                        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_ORDER_CSV_EXPORT_ORDER, $event);
424
425 1
                        $ExportCsvRow->pushData();
426
                    }
427
428
                    //$row[] = number_format(memory_get_usage(true));
429
                    // 出力.
430 1
                    $csvService->fputcsv($ExportCsvRow->getRow());
431
                }
432 1
            });
433 1
        });
434
435 1
        $response->headers->set('Content-Type', 'application/octet-stream');
436 1
        $response->headers->set('Content-Disposition', 'attachment; filename='.$fileName);
437 1
        $response->send();
438
439 1
        return $response;
440
    }
441
442
    /**
443
     * Update to order status
444
     *
445
     * @Method("PUT")
446
     * @Route("/%eccube_admin_route%/shipping/{id}/order_status", requirements={"id" = "\d+"}, name="admin_shipping_update_order_status")
447
     *
448
     * @param Request $request
449
     * @param Shipping $shipping
450
     *
451
     * @return RedirectResponse
452
     */
453 4
    public function updateOrderStatus(Request $request, Shipping $Shipping)
454
    {
455 4
        if (!($request->isXmlHttpRequest() && $this->isTokenValid())) {
456
            return $this->json(['status' => 'NG'], 400);
457
        }
458
459 4
        $Order = $Shipping->getOrder();
460 4
        $OrderStatus = $this->entityManager->find(OrderStatus::class, $request->get('order_status'));
461
462 4
        if (!$OrderStatus) {
463 1
            return $this->json(['status' => 'NG'], 400);
464
        }
465
466 3
        $result = [];
467
        try {
468
            // 発送済みに変更された場合は、関連する出荷がすべて出荷済みになったら OrderStatus を変更する
469 3
            if (OrderStatus::DELIVERED == $OrderStatus->getId()) {
470 2
                if (!$Shipping->getShippingDate()) {
471 2
                    $Shipping->setShippingDate(new \DateTime());
472
473 2
                    if ($request->get('notificationMail')) { // for SimpleStatusUpdate
474 1
                        $this->mailService->sendShippingNotifyMail($Shipping);
475 1
                        $Shipping->setMailSendDate(new \DateTime());
476 1
                        $result['mail'] = true;
477
                    } else {
478 1
                        $result['mail'] = false;
479
                    }
480 2
                    $this->entityManager->flush($Shipping);
481
                }
482
483 2
                $RelateShippings = $Order->getShippings();
484 2
                $allShipped = true;
485 2
                foreach ($RelateShippings as $RelateShipping) {
486 2
                    if (!$RelateShipping->getShippingDate()) {
487
                        $allShipped = false;
488 2
                        break;
489
                    }
490
                }
491 2 View Code Duplication
                if ($allShipped) {
492 2
                    if ($this->orderStateMachine->can($Order, $OrderStatus)) {
493 2
                        $this->orderStateMachine->apply($Order, $OrderStatus);
494
                    } else {
495
                        $from = $Order->getOrderStatus()->getName();
496
                        $to = $OrderStatus->getName();
497 2
                        $result = ['message' => sprintf('%s: %s から %s へのステータス変更はできません', $Shipping->getId(), $from, $to)];
498
                    }
499
                }
500 View Code Duplication
            } else {
501 1
                if ($this->orderStateMachine->can($Order, $OrderStatus)) {
502 1
                    $this->orderStateMachine->apply($Order, $OrderStatus);
503
                } else {
504
                    $from = $Order->getOrderStatus()->getName();
505
                    $to = $OrderStatus->getName();
506
                    $result = ['message' => sprintf('%s: %s から %s へのステータス変更はできません', $Shipping->getId(), $from, $to)];
507
                }
508
            }
509 3
            $this->entityManager->flush($Order);
510
511
            // 会員の場合、購入回数、購入金額などを更新
512 3
            if ($Customer = $Order->getCustomer()) {
513 3
                $this->orderRepository->updateOrderSummary($Customer);
514 3
                $this->entityManager->flush($Customer);
515
            }
516 3
            log_info('対応状況一括変更処理完了', [$Order->getId()]);
517
        } catch (\Exception $e) {
518
            log_error('予期しないエラーです', [$e->getMessage()]);
519
520
            return $this->json(['status' => 'NG'], 500);
521
        }
522
523 3
        return $this->json(array_merge(['status' => 'OK'], $result));
524
    }
525
526
    /**
527
     * Update to Tracking number.
528
     *
529
     * @Method("PUT")
530
     * @Route("/%eccube_admin_route%/shipping/{id}/tracking_number", requirements={"id" = "\d+"}, name="admin_shipping_update_tracking_number")
531
     *
532
     * @param Request $request
533
     * @param Shipping $shipping
534
     *
535
     * @return Response
536
     */
537 2
    public function updateTrackingNumber(Request $request, Shipping $shipping)
538
    {
539 2
        if (!($request->isXmlHttpRequest() && $this->isTokenValid())) {
540
            return $this->json(['status' => 'NG'], 400);
541
        }
542
543 2
        $trackingNumber = mb_convert_kana($request->get('tracking_number'), 'a', 'utf-8');
544
        /** @var \Symfony\Component\Validator\ConstraintViolationListInterface $errors */
545 2
        $errors = $this->validator->validate(
546 2
            $trackingNumber,
547
            [
548 2
                new Assert\Length(['max' => $this->eccubeConfig['eccube_stext_len']]),
549 2
                new Assert\Regex(
550 2
                    ['pattern' => '/^[0-9a-zA-Z-]+$/u', 'message' => trans('form.type.admin.nottrackingnumberstyle')]
551
                ),
552
            ]
553
        );
554
555 2
        if ($errors->count() != 0) {
556 1
            log_info('送り状番号入力チェックエラー');
557 1
            $messages = [];
558
            /** @var \Symfony\Component\Validator\ConstraintViolationInterface $error */
559 1
            foreach ($errors as $error) {
560 1
                $messages[] = $error->getMessage();
561
            }
562
563 1
            return $this->json(['status' => 'NG', 'messages' => $messages], 400);
564
        }
565
566
        try {
567 1
            $shipping->setTrackingNumber($trackingNumber);
568 1
            $this->entityManager->flush($shipping);
569 1
            log_info('送り状番号変更処理完了', [$shipping->getId()]);
570 1
            $message = ['status' => 'OK', 'shipping_id' => $shipping->getId(), 'tracking_number' => $trackingNumber];
571
572 1
            return $this->json($message);
573
        } catch (\Exception $e) {
574
            log_error('予期しないエラー', [$e->getMessage()]);
575
576
            return $this->json(['status' => 'NG'], 500);
577
        }
578
    }
579
}
580