app/Customize/Controller/Admin/AdminController.php line 342

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Customize\Controller\Admin;
  13. use Carbon\Carbon;
  14. use Doctrine\Common\Collections\Criteria;
  15. use Doctrine\ORM\NoResultException;
  16. use Doctrine\ORM\Query\ResultSetMapping;
  17. use Eccube\Controller\AbstractController;
  18. use Eccube\Entity\Master\CustomerStatus;
  19. use Eccube\Entity\Master\OrderStatus;
  20. use Eccube\Entity\Master\ProductStatus;
  21. use Eccube\Entity\ProductStock;
  22. use Eccube\Event\EccubeEvents;
  23. use Eccube\Event\EventArgs;
  24. use Eccube\Exception\PluginApiException;
  25. use Eccube\Form\Type\Admin\ChangePasswordType;
  26. use Eccube\Form\Type\Admin\LoginType;
  27. use Eccube\Repository\CustomerRepository;
  28. use Eccube\Repository\Master\OrderStatusRepository;
  29. use Eccube\Repository\MemberRepository;
  30. use Eccube\Repository\OrderRepository;
  31. use Eccube\Repository\ProductRepository;
  32. use Eccube\Service\PluginApiService;
  33. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  34. use Symfony\Component\HttpFoundation\Request;
  35. use Symfony\Component\Routing\Annotation\Route;
  36. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  37. use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
  38. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  39. class AdminController extends AbstractController
  40. {
  41.     /**
  42.      * @var AuthorizationCheckerInterface
  43.      */
  44.     protected $authorizationChecker;
  45.     /**
  46.      * @var AuthenticationUtils
  47.      */
  48.     protected $helper;
  49.     /**
  50.      * @var MemberRepository
  51.      */
  52.     protected $memberRepository;
  53.     /**
  54.      * @var EncoderFactoryInterface
  55.      */
  56.     protected $encoderFactory;
  57.     /**
  58.      * @var OrderRepository
  59.      */
  60.     protected $orderRepository;
  61.     /**
  62.      * @var OrderStatusRepository
  63.      */
  64.     protected $orderStatusRepository;
  65.     /**
  66.      * @var CustomerRepository
  67.      */
  68.     protected $customerRepository;
  69.     /**
  70.      * @var ProductRepository
  71.      */
  72.     protected $productRepository;
  73.     /** @var PluginApiService */
  74.     protected $pluginApiService;
  75.     /**
  76.      * @var array 売り上げ状況用受注状況
  77.      */
  78.     private $excludes = [OrderStatus::CANCELOrderStatus::PENDINGOrderStatus::PROCESSINGOrderStatus::RETURNED];
  79.     /**
  80.      * AdminController constructor.
  81.      *
  82.      * @param AuthorizationCheckerInterface $authorizationChecker
  83.      * @param AuthenticationUtils $helper
  84.      * @param MemberRepository $memberRepository
  85.      * @param EncoderFactoryInterface $encoderFactory
  86.      * @param OrderRepository $orderRepository
  87.      * @param OrderStatusRepository $orderStatusRepository
  88.      * @param CustomerRepository $custmerRepository
  89.      * @param ProductRepository $productRepository
  90.      * @param PluginApiService $pluginApiService
  91.      */
  92.     public function __construct(
  93.         AuthorizationCheckerInterface $authorizationChecker,
  94.         AuthenticationUtils $helper,
  95.         MemberRepository $memberRepository,
  96.         EncoderFactoryInterface $encoderFactory,
  97.         OrderRepository $orderRepository,
  98.         OrderStatusRepository $orderStatusRepository,
  99.         CustomerRepository $custmerRepository,
  100.         ProductRepository $productRepository,
  101.         PluginApiService $pluginApiService
  102.     ) {
  103.         $this->authorizationChecker $authorizationChecker;
  104.         $this->helper $helper;
  105.         $this->memberRepository $memberRepository;
  106.         $this->encoderFactory $encoderFactory;
  107.         $this->orderRepository $orderRepository;
  108.         $this->orderStatusRepository $orderStatusRepository;
  109.         $this->customerRepository $custmerRepository;
  110.         $this->productRepository $productRepository;
  111.         $this->pluginApiService $pluginApiService;
  112.     }
  113.     /**
  114.      * @Route("/%eccube_admin_route%/login", name="admin_login", methods={"GET", "POST"})
  115.      * @Template("@admin/login.twig")
  116.      */
  117.     public function login(Request $request)
  118.     {
  119.         if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
  120.             return $this->redirectToRoute('admin_homepage');
  121.         }
  122.         /* @var $form \Symfony\Component\Form\FormInterface */
  123.         $builder $this->formFactory->createNamedBuilder(''LoginType::class);
  124.         $event = new EventArgs(
  125.             [
  126.                 'builder' => $builder,
  127.             ],
  128.             $request
  129.         );
  130.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_LOGIN_INITIALIZE);
  131.         $form $builder->getForm();
  132.         return [
  133.             'error' => $this->helper->getLastAuthenticationError(),
  134.             'form' => $form->createView(),
  135.         ];
  136.     }
  137.     /**
  138.      * 管理画面ホーム
  139.      *
  140.      * @param Request $request
  141.      *
  142.      * @return array
  143.      *
  144.      * @throws NoResultException
  145.      * @throws \Doctrine\ORM\NonUniqueResultException
  146.      *
  147.      * @Route("/%eccube_admin_route%/", name="admin_homepage", methods={"GET"})
  148.      * @Template("@admin/index.twig")
  149.      */
  150.     public function index(Request $request)
  151.     {
  152.         return $this->redirectToRoute('admin_order');
  153.         $adminRoute $this->eccubeConfig['eccube_admin_route'];
  154.         $is_danger_admin_url false;
  155.         if ($adminRoute === 'admin') {
  156.             $is_danger_admin_url true;
  157.         }
  158.         /**
  159.          * 受注状況.
  160.          */
  161.         $excludes = [];
  162.         $excludes[] = OrderStatus::CANCEL;
  163.         $excludes[] = OrderStatus::DELIVERED;
  164.         $excludes[] = OrderStatus::PENDING;
  165.         $excludes[] = OrderStatus::PROCESSING;
  166.         $excludes[] = OrderStatus::RETURNED;
  167.         $event = new EventArgs(
  168.             [
  169.                 'excludes' => $excludes,
  170.             ],
  171.             $request
  172.         );
  173.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_ORDER);
  174.         $excludes $event->getArgument('excludes');
  175.         // 受注ステータスごとの受注件数.
  176.         $Orders $this->getOrderEachStatus($excludes);
  177.         // 受注ステータスの一覧.
  178.         $Criteria = new Criteria();
  179.         $Criteria
  180.             ->where($Criteria::expr()->notIn('id'$excludes))
  181.             ->orderBy(['sort_no' => 'ASC']);
  182.         $OrderStatuses $this->orderStatusRepository->matching($Criteria);
  183.         /**
  184.          * 売り上げ状況
  185.          */
  186.         $event = new EventArgs(
  187.             [
  188.                 'excludes' => $this->excludes,
  189.             ],
  190.             $request
  191.         );
  192.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_SALES);
  193.         $this->excludes $event->getArgument('excludes');
  194.         // 今日の売上/件数
  195.         $salesToday $this->getSalesByDay(new \DateTime());
  196.         // 昨日の売上/件数
  197.         $salesYesterday $this->getSalesByDay(new \DateTime('-1 day'));
  198.         // 今月の売上/件数
  199.         $salesThisMonth $this->getSalesByMonth(new \DateTime());
  200.         /**
  201.          * ショップ状況
  202.          */
  203.         // 在庫切れ商品数
  204.         $countNonStockProducts $this->countNonStockProducts();
  205.         // 取り扱い商品数
  206.         $countProducts $this->countProducts();
  207.         // 本会員数
  208.         $countCustomers $this->countCustomers();
  209.         $event = new EventArgs(
  210.             [
  211.                 'Orders' => $Orders,
  212.                 'OrderStatuses' => $OrderStatuses,
  213.                 'salesThisMonth' => $salesThisMonth,
  214.                 'salesToday' => $salesToday,
  215.                 'salesYesterday' => $salesYesterday,
  216.                 'countNonStockProducts' => $countNonStockProducts,
  217.                 'countProducts' => $countProducts,
  218.                 'countCustomers' => $countCustomers,
  219.             ],
  220.             $request
  221.         );
  222.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_COMPLETE);
  223.         // 推奨プラグイン
  224.         $recommendedPlugins = [];
  225.         try {
  226.             $recommendedPlugins $this->pluginApiService->getRecommended();
  227.         } catch (PluginApiException $ignore) {
  228.         }
  229.         return [
  230.             'Orders' => $Orders,
  231.             'OrderStatuses' => $OrderStatuses,
  232.             'salesThisMonth' => $salesThisMonth,
  233.             'salesToday' => $salesToday,
  234.             'salesYesterday' => $salesYesterday,
  235.             'countNonStockProducts' => $countNonStockProducts,
  236.             'countProducts' => $countProducts,
  237.             'countCustomers' => $countCustomers,
  238.             'recommendedPlugins' => $recommendedPlugins,
  239.             'is_danger_admin_url' => $is_danger_admin_url,
  240.         ];
  241.     }
  242.     /**
  243.      * 売上状況の取得
  244.      *
  245.      * @param Request $request
  246.      *
  247.      * @Route("/%eccube_admin_route%/sale_chart", name="admin_homepage_sale", methods={"GET"})
  248.      *
  249.      * @return \Symfony\Component\HttpFoundation\JsonResponse
  250.      */
  251.     public function sale(Request $request)
  252.     {
  253.         if (!($request->isXmlHttpRequest() && $this->isTokenValid())) {
  254.             return $this->json(['status' => 'NG'], 400);
  255.         }
  256.         $event = new EventArgs(
  257.             [
  258.                 'excludes' => $this->excludes,
  259.             ],
  260.             $request
  261.         );
  262.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_SALES);
  263.         $this->excludes $event->getArgument('excludes');
  264.         // 週間の売上金額
  265.         $toDate Carbon::now();
  266.         $fromDate Carbon::today()->subWeek();
  267.         $rawWeekly $this->getData($fromDate$toDate'Y/m/d');
  268.         // 月間の売上金額
  269.         $fromDate Carbon::now()->startOfMonth();
  270.         $rawMonthly $this->getData($fromDate$toDate'Y/m/d');
  271.         // 年間の売上金額
  272.         $fromDate Carbon::now()->subYear()->startOfMonth();
  273.         $rawYear $this->getData($fromDate$toDate'Y/m');
  274.         $datas = [$rawWeekly$rawMonthly$rawYear];
  275.         return $this->json($datas);
  276.     }
  277.     /**
  278.      * メニュー変更
  279.      *
  280.      * @param Request $request
  281.      *
  282.      * @Route("/%eccube_admin_route%/pushmenu", name="admin_pushmenu", methods={"GET"})
  283.      *
  284.      * @return \Symfony\Component\HttpFoundation\JsonResponse
  285.      */
  286.     public function pushmenu(Request $request)
  287.     {
  288.         $this->session->set('pushmenu'$_GET['pushmenu']);
  289.         return $this->json(['status' => 'OK'], 200);
  290.     }
  291.     /**
  292.      * フルスクリーン変更
  293.      *
  294.      * @param Request $request
  295.      *
  296.      * @Route("/%eccube_admin_route%/fullscreen", name="admin_fullscreen", methods={"GET"})
  297.      *
  298.      * @return \Symfony\Component\HttpFoundation\JsonResponse
  299.      */
  300.     public function fullscreen(Request $request)
  301.     {
  302.         
  303.         $this->session->set('fullscreen'$_GET['fullscreen']);
  304.         return $this->json(['status' => 'OK'], 200);
  305.     }
  306.     /**
  307.      * パスワード変更画面
  308.      *
  309.      * @Route("/%eccube_admin_route%/change_password", name="admin_change_password", methods={"GET", "POST"})
  310.      * @Template("@admin/change_password.twig")
  311.      *
  312.      * @param Request $request
  313.      *
  314.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|array
  315.      */
  316.     public function changePassword(Request $request)
  317.     {
  318.         $builder $this->formFactory
  319.             ->createBuilder(ChangePasswordType::class);
  320.         $event = new EventArgs(
  321.             [
  322.                 'builder' => $builder,
  323.             ],
  324.             $request
  325.         );
  326.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_CHANGE_PASSWORD_INITIALIZE);
  327.         $form $builder->getForm();
  328.         $form->handleRequest($request);
  329.         if ($form->isSubmitted() && $form->isValid()) {
  330.             $Member $this->getUser();
  331.             $salt $Member->getSalt();
  332.             $password $form->get('change_password')->getData();
  333.             $encoder $this->encoderFactory->getEncoder($Member);
  334.             // 2系からのデータ移行でsaltがセットされていない場合はsaltを生成.
  335.             if (empty($salt)) {
  336.                 $salt $encoder->createSalt();
  337.             }
  338.             $password $encoder->encodePassword($password$salt);
  339.             $Member
  340.                 ->setPassword($password)
  341.                 ->setSalt($salt);
  342.             $this->memberRepository->save($Member);
  343.             $event = new EventArgs(
  344.                 [
  345.                     'form' => $form,
  346.                     'Member' => $Member,
  347.                 ],
  348.                 $request
  349.             );
  350.             $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIN_CHANGE_PASSWORD_COMPLETE);
  351.             $this->addSuccess('admin.change_password.password_changed''admin');
  352.             return $this->redirectToRoute('admin_change_password');
  353.         }
  354.         return [
  355.             'form' => $form->createView(),
  356.         ];
  357.     }
  358.     /**
  359.      * 在庫なし商品の検索結果を表示する.
  360.      *
  361.      * @Route("/%eccube_admin_route%/search_nonstock", name="admin_homepage_nonstock", methods={"GET"})
  362.      *
  363.      * @param Request $request
  364.      *
  365.      * @return \Symfony\Component\HttpFoundation\Response
  366.      */
  367.     public function searchNonStockProducts(Request $request)
  368.     {
  369.         // 在庫なし商品の検索条件をセッションに付与し, 商品マスタへリダイレクトする.
  370.         $searchData = [];
  371.         $searchData['stock'] = [ProductStock::OUT_OF_STOCK];
  372.         $session $request->getSession();
  373.         $session->set('eccube.admin.product.search'$searchData);
  374.         return $this->redirectToRoute('admin_product_page', [
  375.             'page_no' => 1,
  376.         ]);
  377.     }
  378.     /**
  379.      * 本会員の検索結果を表示する.
  380.      *
  381.      * @Route("/%eccube_admin_route%/search_customer", name="admin_homepage_customer", methods={"GET"})
  382.      *
  383.      * @param Request $request
  384.      *
  385.      * @return \Symfony\Component\HttpFoundation\Response
  386.      */
  387.     public function searchCustomer(Request $request)
  388.     {
  389.         $searchData = [];
  390.         $searchData['customer_status'] = [CustomerStatus::REGULAR];
  391.         $session $request->getSession();
  392.         $session->set('eccube.admin.customer.search'$searchData);
  393.         return $this->redirectToRoute('admin_customer_page', [
  394.             'page_no' => 1,
  395.         ]);
  396.     }
  397.     /**
  398.      * @param \Doctrine\ORM\EntityManagerInterface $em
  399.      * @param array $excludes
  400.      *
  401.      * @return Request|null
  402.      */
  403.     protected function getOrderEachStatus(array $excludes)
  404.     {
  405.         $sql 'SELECT
  406.                     t1.order_status_id as status,
  407.                     COUNT(t1.id) as count
  408.                 FROM
  409.                     dtb_order t1
  410.                 WHERE
  411.                     t1.order_status_id NOT IN (:excludes)
  412.                 GROUP BY
  413.                     t1.order_status_id
  414.                 ORDER BY
  415.                     t1.order_status_id';
  416.         $rsm = new ResultSetMapping();
  417.         $rsm->addScalarResult('status''status');
  418.         $rsm->addScalarResult('count''count');
  419.         $query $this->entityManager->createNativeQuery($sql$rsm);
  420.         $query->setParameters([':excludes' => $excludes]);
  421.         $result $query->getResult();
  422.         $orderArray = [];
  423.         foreach ($result as $row) {
  424.             $orderArray[$row['status']] = $row['count'];
  425.         }
  426.         return $orderArray;
  427.     }
  428.     /**
  429.      * @param \DateTime $dateTime
  430.      *
  431.      * @return array|mixed
  432.      *
  433.      * @throws \Doctrine\ORM\NonUniqueResultException
  434.      */
  435.     protected function getSalesByDay($dateTime)
  436.     {
  437.         $dateTimeStart = clone $dateTime;
  438.         $dateTimeStart->setTime(0000);
  439.         $dateTimeEnd = clone $dateTimeStart;
  440.         $dateTimeEnd->modify('+1 days');
  441.         $qb $this->orderRepository
  442.             ->createQueryBuilder('o')
  443.             ->select('
  444.             SUM(o.payment_total) AS order_amount,
  445.             COUNT(o) AS order_count')
  446.             ->setParameter(':excludes'$this->excludes)
  447.             ->setParameter(':targetDateStart'$dateTimeStart)
  448.             ->setParameter(':targetDateEnd'$dateTimeEnd)
  449.             ->andWhere(':targetDateStart <= o.order_date and o.order_date < :targetDateEnd')
  450.             ->andWhere('o.OrderStatus NOT IN (:excludes)');
  451.         $q $qb->getQuery();
  452.         $result = [];
  453.         try {
  454.             $result $q->getSingleResult();
  455.         } catch (NoResultException $e) {
  456.             // 結果がない場合は空の配列を返す.
  457.         }
  458.         return $result;
  459.     }
  460.     /**
  461.      * @param \DateTime $dateTime
  462.      *
  463.      * @return array|mixed
  464.      *
  465.      * @throws \Doctrine\ORM\NonUniqueResultException
  466.      */
  467.     protected function getSalesByMonth($dateTime)
  468.     {
  469.         $dateTimeStart = clone $dateTime;
  470.         $dateTimeStart->setTime(0000);
  471.         $dateTimeStart->modify('first day of this month');
  472.         $dateTimeEnd = clone $dateTime;
  473.         $dateTimeEnd->setTime(0000);
  474.         $dateTimeEnd->modify('first day of 1 month');
  475.         $qb $this->orderRepository
  476.             ->createQueryBuilder('o')
  477.             ->select('
  478.             SUM(o.payment_total) AS order_amount,
  479.             COUNT(o) AS order_count')
  480.             ->setParameter(':excludes'$this->excludes)
  481.             ->setParameter(':targetDateStart'$dateTimeStart)
  482.             ->setParameter(':targetDateEnd'$dateTimeEnd)
  483.             ->andWhere(':targetDateStart <= o.order_date and o.order_date < :targetDateEnd')
  484.             ->andWhere('o.OrderStatus NOT IN (:excludes)');
  485.         $q $qb->getQuery();
  486.         $result = [];
  487.         try {
  488.             $result $q->getSingleResult();
  489.         } catch (NoResultException $e) {
  490.             // 結果がない場合は空の配列を返す.
  491.         }
  492.         return $result;
  493.     }
  494.     /**
  495.      * 在庫切れ商品数を取得
  496.      *
  497.      * @return mixed
  498.      *
  499.      * @throws \Doctrine\ORM\NonUniqueResultException
  500.      */
  501.     protected function countNonStockProducts()
  502.     {
  503.         $qb $this->productRepository->createQueryBuilder('p')
  504.             ->select('count(DISTINCT p.id)')
  505.             ->innerJoin('p.ProductClasses''pc')
  506.             ->where('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
  507.             ->andWhere('pc.visible = :visible')
  508.             ->setParameter('StockUnlimited'false)
  509.             ->setParameter('visible'true);
  510.         return $qb->getQuery()->getSingleScalarResult();
  511.     }
  512.     /**
  513.      * 商品数を取得
  514.      *
  515.      * @return mixed
  516.      *
  517.      * @throws \Doctrine\ORM\NonUniqueResultException
  518.      */
  519.     protected function countProducts()
  520.     {
  521.         $qb $this->productRepository->createQueryBuilder('p')
  522.             ->select('count(p.id)')
  523.             ->where('p.Status in (:Status)')
  524.             ->setParameter('Status', [ProductStatus::DISPLAY_SHOWProductStatus::DISPLAY_HIDE]);
  525.         return $qb->getQuery()->getSingleScalarResult();
  526.     }
  527.     /**
  528.      * 本会員数を取得
  529.      *
  530.      * @return mixed
  531.      *
  532.      * @throws \Doctrine\ORM\NonUniqueResultException
  533.      */
  534.     protected function countCustomers()
  535.     {
  536.         $qb $this->customerRepository->createQueryBuilder('c')
  537.             ->select('count(c.id)')
  538.             ->where('c.Status = :Status')
  539.             ->setParameter('Status'CustomerStatus::REGULAR);
  540.         return $qb->getQuery()->getSingleScalarResult();
  541.     }
  542.     /**
  543.      * 期間指定のデータを取得
  544.      *
  545.      * @param Carbon $fromDate
  546.      * @param Carbon $toDate
  547.      * @param $format
  548.      *
  549.      * @return array
  550.      */
  551.     protected function getData(Carbon $fromDateCarbon $toDate$format)
  552.     {
  553.         $qb $this->orderRepository->createQueryBuilder('o')
  554.             ->andWhere('o.order_date >= :fromDate')
  555.             ->andWhere('o.order_date <= :toDate')
  556.             ->andWhere('o.OrderStatus NOT IN (:excludes)')
  557.             ->setParameter(':excludes'$this->excludes)
  558.             ->setParameter(':fromDate'$fromDate->copy())
  559.             ->setParameter(':toDate'$toDate->copy())
  560.             ->orderBy('o.order_date');
  561.         $result $qb->getQuery()->getResult();
  562.         return $this->convert($result$fromDate$toDate$format);
  563.     }
  564.     /**
  565.      * 期間毎にデータをまとめる
  566.      *
  567.      * @param $result
  568.      * @param Carbon $fromDate
  569.      * @param Carbon $toDate
  570.      * @param $format
  571.      *
  572.      * @return array
  573.      */
  574.     protected function convert($resultCarbon $fromDateCarbon $toDate$format)
  575.     {
  576.         $raw = [];
  577.         for ($date $fromDate$date <= $toDate$date $date->addDay()) {
  578.             $raw[$date->format($format)]['price'] = 0;
  579.             $raw[$date->format($format)]['count'] = 0;
  580.         }
  581.         foreach ($result as $Order) {
  582.             $raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getPaymentTotal();
  583.             ++$raw[$Order->getOrderDate()->format($format)]['count'];
  584.         }
  585.         return $raw;
  586.     }
  587. }