<?php
/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Customize\Controller\Admin;
use Carbon\Carbon;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query\ResultSetMapping;
use Eccube\Controller\AbstractController;
use Eccube\Entity\Master\CustomerStatus;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Entity\Master\ProductStatus;
use Eccube\Entity\ProductStock;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Eccube\Exception\PluginApiException;
use Eccube\Form\Type\Admin\ChangePasswordType;
use Eccube\Form\Type\Admin\LoginType;
use Eccube\Repository\CustomerRepository;
use Eccube\Repository\Master\OrderStatusRepository;
use Eccube\Repository\MemberRepository;
use Eccube\Repository\OrderRepository;
use Eccube\Repository\ProductRepository;
use Eccube\Service\PluginApiService;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class AdminController extends AbstractController
{
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
/**
* @var AuthenticationUtils
*/
protected $helper;
/**
* @var MemberRepository
*/
protected $memberRepository;
/**
* @var EncoderFactoryInterface
*/
protected $encoderFactory;
/**
* @var OrderRepository
*/
protected $orderRepository;
/**
* @var OrderStatusRepository
*/
protected $orderStatusRepository;
/**
* @var CustomerRepository
*/
protected $customerRepository;
/**
* @var ProductRepository
*/
protected $productRepository;
/** @var PluginApiService */
protected $pluginApiService;
/**
* @var array 売り上げ状況用受注状況
*/
private $excludes = [OrderStatus::CANCEL, OrderStatus::PENDING, OrderStatus::PROCESSING, OrderStatus::RETURNED];
/**
* AdminController constructor.
*
* @param AuthorizationCheckerInterface $authorizationChecker
* @param AuthenticationUtils $helper
* @param MemberRepository $memberRepository
* @param EncoderFactoryInterface $encoderFactory
* @param OrderRepository $orderRepository
* @param OrderStatusRepository $orderStatusRepository
* @param CustomerRepository $custmerRepository
* @param ProductRepository $productRepository
* @param PluginApiService $pluginApiService
*/
public function __construct(
AuthorizationCheckerInterface $authorizationChecker,
AuthenticationUtils $helper,
MemberRepository $memberRepository,
EncoderFactoryInterface $encoderFactory,
OrderRepository $orderRepository,
OrderStatusRepository $orderStatusRepository,
CustomerRepository $custmerRepository,
ProductRepository $productRepository,
PluginApiService $pluginApiService
) {
$this->authorizationChecker = $authorizationChecker;
$this->helper = $helper;
$this->memberRepository = $memberRepository;
$this->encoderFactory = $encoderFactory;
$this->orderRepository = $orderRepository;
$this->orderStatusRepository = $orderStatusRepository;
$this->customerRepository = $custmerRepository;
$this->productRepository = $productRepository;
$this->pluginApiService = $pluginApiService;
}
/**
* @Route("/%eccube_admin_route%/login", name="admin_login", methods={"GET", "POST"})
* @Template("@admin/login.twig")
*/
public function login(Request $request)
{
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
return $this->redirectToRoute('admin_auction');
}
/* @var $form \Symfony\Component\Form\FormInterface */
$builder = $this->formFactory->createNamedBuilder('', LoginType::class);
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_LOGIN_INITIALIZE);
$form = $builder->getForm();
return [
'error' => $this->helper->getLastAuthenticationError(),
'form' => $form->createView(),
];
}
/**
* 管理画面ホーム
*
* @param Request $request
*
* @return array
*
* @throws NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
*
* @Route("/%eccube_admin_route%/", name="admin_homepage", methods={"GET"})
* @Template("@admin/index.twig")
*/
public function index(Request $request)
{
return $this->redirectToRoute('admin_auction');
// $adminRoute = $this->eccubeConfig['eccube_admin_route'];
//
// $is_danger_admin_url = false;
// if ($adminRoute === 'admin') {
// $is_danger_admin_url = true;
// }
// /**
// * 受注状況.
// */
// $excludes = [];
// $excludes[] = OrderStatus::CANCEL;
// $excludes[] = OrderStatus::DELIVERED;
// $excludes[] = OrderStatus::PENDING;
// $excludes[] = OrderStatus::PROCESSING;
// $excludes[] = OrderStatus::RETURNED;
//
// $event = new EventArgs(
// [
// 'excludes' => $excludes,
// ],
// $request
// );
// $this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_INDEX_ORDER);
// $excludes = $event->getArgument('excludes');
//
// // 受注ステータスごとの受注件数.
// $Orders = $this->getOrderEachStatus($excludes);
//
// // 受注ステータスの一覧.
// $Criteria = new Criteria();
// $Criteria
// ->where($Criteria::expr()->notIn('id', $excludes))
// ->orderBy(['sort_no' => 'ASC']);
// $OrderStatuses = $this->orderStatusRepository->matching($Criteria);
//
// /**
// * 売り上げ状況
// */
// $event = new EventArgs(
// [
// 'excludes' => $this->excludes,
// ],
// $request
// );
// $this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_INDEX_SALES);
// $this->excludes = $event->getArgument('excludes');
//
// // 今日の売上/件数
// $salesToday = $this->getSalesByDay(new \DateTime());
// // 昨日の売上/件数
// $salesYesterday = $this->getSalesByDay(new \DateTime('-1 day'));
// // 今月の売上/件数
// $salesThisMonth = $this->getSalesByMonth(new \DateTime());
//
// /**
// * ショップ状況
// */
// // 在庫切れ商品数
// $countNonStockProducts = $this->countNonStockProducts();
//
// // 取り扱い商品数
// $countProducts = $this->countProducts();
//
// // 本会員数
// $countCustomers = $this->countCustomers();
//
// $event = new EventArgs(
// [
// 'Orders' => $Orders,
// 'OrderStatuses' => $OrderStatuses,
// 'salesThisMonth' => $salesThisMonth,
// 'salesToday' => $salesToday,
// 'salesYesterday' => $salesYesterday,
// 'countNonStockProducts' => $countNonStockProducts,
// 'countProducts' => $countProducts,
// 'countCustomers' => $countCustomers,
// ],
// $request
// );
// $this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_INDEX_COMPLETE);
//
// // 推奨プラグイン
// $recommendedPlugins = [];
// try {
// $recommendedPlugins = $this->pluginApiService->getRecommended();
// } catch (PluginApiException $ignore) {
// }
//
// return [
// 'Orders' => $Orders,
// 'OrderStatuses' => $OrderStatuses,
// 'salesThisMonth' => $salesThisMonth,
// 'salesToday' => $salesToday,
// 'salesYesterday' => $salesYesterday,
// 'countNonStockProducts' => $countNonStockProducts,
// 'countProducts' => $countProducts,
// 'countCustomers' => $countCustomers,
// 'recommendedPlugins' => $recommendedPlugins,
// 'is_danger_admin_url' => $is_danger_admin_url,
// ];
}
/**
* 売上状況の取得
*
* @param Request $request
*
* @Route("/%eccube_admin_route%/sale_chart", name="admin_homepage_sale", methods={"GET"})
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
*/
public function sale(Request $request)
{
if (!($request->isXmlHttpRequest() && $this->isTokenValid())) {
return $this->json(['status' => 'NG'], 400);
}
$event = new EventArgs(
[
'excludes' => $this->excludes,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_INDEX_SALES);
$this->excludes = $event->getArgument('excludes');
// 週間の売上金額
$toDate = Carbon::now();
$fromDate = Carbon::today()->subWeek();
$rawWeekly = $this->getData($fromDate, $toDate, 'Y/m/d');
// 月間の売上金額
$fromDate = Carbon::now()->startOfMonth();
$rawMonthly = $this->getData($fromDate, $toDate, 'Y/m/d');
// 年間の売上金額
$fromDate = Carbon::now()->subYear()->startOfMonth();
$rawYear = $this->getData($fromDate, $toDate, 'Y/m');
$datas = [$rawWeekly, $rawMonthly, $rawYear];
return $this->json($datas);
}
/**
* パスワード変更画面
*
* @Route("/%eccube_admin_route%/change_password", name="admin_change_password", methods={"GET", "POST"})
* @Template("@admin/change_password.twig")
*
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|array
*/
public function changePassword(Request $request)
{
$builder = $this->formFactory
->createBuilder(ChangePasswordType::class);
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIM_CHANGE_PASSWORD_INITIALIZE);
$form = $builder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$Member = $this->getUser();
$salt = $Member->getSalt();
$password = $form->get('change_password')->getData();
$encoder = $this->encoderFactory->getEncoder($Member);
// 2系からのデータ移行でsaltがセットされていない場合はsaltを生成.
if (empty($salt)) {
$salt = $encoder->createSalt();
}
$password = $encoder->encodePassword($password, $salt);
$Member
->setPassword($password)
->setSalt($salt);
$this->memberRepository->save($Member);
$event = new EventArgs(
[
'form' => $form,
'Member' => $Member,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_ADMIN_CHANGE_PASSWORD_COMPLETE);
$this->addSuccess('admin.change_password.password_changed', 'admin');
return $this->redirectToRoute('admin_change_password');
}
return [
'form' => $form->createView(),
];
}
/**
* 在庫なし商品の検索結果を表示する.
*
* @Route("/%eccube_admin_route%/search_nonstock", name="admin_homepage_nonstock", methods={"GET"})
*
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function searchNonStockProducts(Request $request)
{
// 在庫なし商品の検索条件をセッションに付与し, 商品マスタへリダイレクトする.
$searchData = [];
$searchData['stock'] = [ProductStock::OUT_OF_STOCK];
$session = $request->getSession();
$session->set('eccube.admin.product.search', $searchData);
return $this->redirectToRoute('admin_product_page', [
'page_no' => 1,
]);
}
/**
* 本会員の検索結果を表示する.
*
* @Route("/%eccube_admin_route%/search_customer", name="admin_homepage_customer", methods={"GET"})
*
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function searchCustomer(Request $request)
{
$searchData = [];
$searchData['customer_status'] = [CustomerStatus::REGULAR];
$session = $request->getSession();
$session->set('eccube.admin.customer.search', $searchData);
return $this->redirectToRoute('admin_customer_page', [
'page_no' => 1,
]);
}
/**
* @param \Doctrine\ORM\EntityManagerInterface $em
* @param array $excludes
*
* @return Request|null
*/
protected function getOrderEachStatus(array $excludes)
{
$sql = 'SELECT
t1.order_status_id as status,
COUNT(t1.id) as count
FROM
dtb_order t1
WHERE
t1.order_status_id NOT IN (:excludes)
GROUP BY
t1.order_status_id
ORDER BY
t1.order_status_id';
$rsm = new ResultSetMapping();
$rsm->addScalarResult('status', 'status');
$rsm->addScalarResult('count', 'count');
$query = $this->entityManager->createNativeQuery($sql, $rsm);
$query->setParameters([':excludes' => $excludes]);
$result = $query->getResult();
$orderArray = [];
foreach ($result as $row) {
$orderArray[$row['status']] = $row['count'];
}
return $orderArray;
}
/**
* @param \DateTime $dateTime
*
* @return array|mixed
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getSalesByDay($dateTime)
{
$dateTimeStart = clone $dateTime;
$dateTimeStart->setTime(0, 0, 0, 0);
$dateTimeEnd = clone $dateTimeStart;
$dateTimeEnd->modify('+1 days');
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total) AS order_amount,
COUNT(o) AS order_count')
->setParameter(':excludes', $this->excludes)
->setParameter(':targetDateStart', $dateTimeStart)
->setParameter(':targetDateEnd', $dateTimeEnd)
->andWhere(':targetDateStart <= o.order_date and o.order_date < :targetDateEnd')
->andWhere('o.OrderStatus NOT IN (:excludes)');
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す.
}
return $result;
}
/**
* @param \DateTime $dateTime
*
* @return array|mixed
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getSalesByMonth($dateTime)
{
$dateTimeStart = clone $dateTime;
$dateTimeStart->setTime(0, 0, 0, 0);
$dateTimeStart->modify('first day of this month');
$dateTimeEnd = clone $dateTime;
$dateTimeEnd->setTime(0, 0, 0, 0);
$dateTimeEnd->modify('first day of 1 month');
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total) AS order_amount,
COUNT(o) AS order_count')
->setParameter(':excludes', $this->excludes)
->setParameter(':targetDateStart', $dateTimeStart)
->setParameter(':targetDateEnd', $dateTimeEnd)
->andWhere(':targetDateStart <= o.order_date and o.order_date < :targetDateEnd')
->andWhere('o.OrderStatus NOT IN (:excludes)');
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す.
}
return $result;
}
/**
* 在庫切れ商品数を取得
*
* @return mixed
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function countNonStockProducts()
{
$qb = $this->productRepository->createQueryBuilder('p')
->select('count(DISTINCT p.id)')
->innerJoin('p.ProductClasses', 'pc')
->where('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
->andWhere('pc.visible = :visible')
->setParameter('StockUnlimited', false)
->setParameter('visible', true);
return $qb->getQuery()->getSingleScalarResult();
}
/**
* 商品数を取得
*
* @return mixed
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function countProducts()
{
$qb = $this->productRepository->createQueryBuilder('p')
->select('count(p.id)')
->where('p.Status in (:Status)')
->setParameter('Status', [ProductStatus::DISPLAY_SHOW, ProductStatus::DISPLAY_HIDE]);
return $qb->getQuery()->getSingleScalarResult();
}
/**
* 本会員数を取得
*
* @return mixed
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function countCustomers()
{
$qb = $this->customerRepository->createQueryBuilder('c')
->select('count(c.id)')
->where('c.Status = :Status')
->setParameter('Status', CustomerStatus::REGULAR);
return $qb->getQuery()->getSingleScalarResult();
}
/**
* 期間指定のデータを取得
*
* @param Carbon $fromDate
* @param Carbon $toDate
* @param $format
*
* @return array
*/
protected function getData(Carbon $fromDate, Carbon $toDate, $format)
{
$qb = $this->orderRepository->createQueryBuilder('o')
->andWhere('o.order_date >= :fromDate')
->andWhere('o.order_date <= :toDate')
->andWhere('o.OrderStatus NOT IN (:excludes)')
->setParameter(':excludes', $this->excludes)
->setParameter(':fromDate', $fromDate->copy())
->setParameter(':toDate', $toDate->copy())
->orderBy('o.order_date');
$result = $qb->getQuery()->getResult();
return $this->convert($result, $fromDate, $toDate, $format);
}
/**
* 期間毎にデータをまとめる
*
* @param $result
* @param Carbon $fromDate
* @param Carbon $toDate
* @param $format
*
* @return array
*/
protected function convert($result, Carbon $fromDate, Carbon $toDate, $format)
{
$raw = [];
for ($date = $fromDate; $date <= $toDate; $date = $date->addDay()) {
$raw[$date->format($format)]['price'] = 0;
$raw[$date->format($format)]['count'] = 0;
}
foreach ($result as $Order) {
$raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getPaymentTotal();
++$raw[$Order->getOrderDate()->format($format)]['count'];
}
return $raw;
}
}