<?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 Eccube\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;
use Plugin\EccubePaymentLite42\Entity\Config;
// use Plugin\EccubePaymentLite42\Repository\ConfigRepository;
// use Doctrine\ORM\EntityManagerInterface;
use Customize\Repository\CustomizeCustomerRepository; //代理店名の取得はカスタムリポジトリを使用
use Customize\Entity\Agency;
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];
/**
* @var array 売り上げ状況用受注状況
*/
private $targetStatus = [
//発送済み
OrderStatus::DELIVERED,
//対応中
OrderStatus::IN_PROGRESS,
//対応済
OrderStatus::ALREADY_SUPPORTED,
];
private $customizeCustomerRepository;
// private $entityManager;
// private $configRepository;
/**
* 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
* @param CustomizeCustomerRepository $customizeCustomerRepository
*/
public function __construct(
AuthorizationCheckerInterface $authorizationChecker,
AuthenticationUtils $helper,
MemberRepository $memberRepository,
EncoderFactoryInterface $encoderFactory,
OrderRepository $orderRepository,
OrderStatusRepository $orderStatusRepository,
CustomerRepository $custmerRepository,
ProductRepository $productRepository,
PluginApiService $pluginApiService,
CustomizeCustomerRepository $customizeCustomerRepository
// EntityManagerInterface $entityManager,
// ConfigRepository $configRepository
) {
$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;
$this->customizeCustomerRepository = $customizeCustomerRepository;
// $this->entityManager = $entityManager;
// $this->configRepository = $configRepository;
}
/**
* @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_homepage');
}
/* @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)
{
$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;
// 対象とする受注ステータスを定義
$targetStatus = [];
$targetStatus = [
//発送済み
OrderStatus::DELIVERED,
//対応中
OrderStatus::IN_PROGRESS,
//対応済
OrderStatus::ALREADY_SUPPORTED,
];
$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());
//---------------
//カスタマイズ 売上状況に新項目を追加
//---------------
//前月の売上金額 / 売上件数
$salesLastMonth = $this->getSalesByLastMonth();
// 前年同月の売上/件数の取得
$salesSameMonthLastYear = $this->getSalesBySameMonthLastYear();
// 累計の売上金額、平均単価、売上件数の取得
$totalSalesData = $this->getTotalSalesAndAveragePrice();
//今週の販売数量 / 売上件数
$salesThisWeek = $this->getSalesByWeek(new \DateTime());
// 今年の売上/件数の取得
$salesThisYear = $this->getSalesByYear();
/**
* ショップ状況
*/
// 在庫切れ商品数
$countNonStockProducts = $this->countNonStockProducts();
// 取り扱い商品数
$countProducts = $this->countProducts();
// 本会員数
$countCustomers = $this->countCustomers();
$event = new EventArgs(
[
'Orders' => $Orders,
'OrderStatuses' => $OrderStatuses,
'salesThisYear' => $salesThisYear,
'salesThisMonth' => $salesThisMonth,
'salesThisWeek' => $salesThisWeek,
'salesToday' => $salesToday,
'salesYesterday' => $salesYesterday,
'salesLastMonth' => $salesLastMonth,
'salesSameMonthLastYear' => $salesSameMonthLastYear,
'totalSalesData' => $totalSalesData,
'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) {
}
//定期受注締切日
//-------------
//テスト用 (任意の休日と祝日を統合)
//-------------
// $customHolidays =
// [
// '2024-03-04',
// '2024-03-03',
// '2024-03-05',
// '2024-03-06',
// '2024-03-07',
// '2024-03-08',
// '2024-03-09',
// '2024-03-10',
// '2024-09-09',
// ];
// $holidays = $this->fetchHolidays($customHolidays);
//本番用 祝日情報の取得
$holidays = $this->fetchApiHolidays();
//-----------
//配列確認用
//-----------
$holidaysText = "";
foreach ($holidays as $holiday) {
$holidaysText .= $holiday;
$holidaysText .= "___";
}
//今日の日付
$today = new \DateTime();
//テスト用特定の日付
// $today = new \DateTime('2024-4-8');
// ==============
//基準日の設定
//7日までは、8日時点の受注締切日の日数を設定
//20日までは、21日時点の受注締切日の日数を設定
//21日以降は、翌月8日時点の受注締切日の日数を設定
// ==============
//次回定期の基準日
$prevBaseDate = new \DateTime();
//次回定期の基準日
$nextBaseDate = new \DateTime();
//次次回定期の基準日
$nextNextBaseDate = new \DateTime();
if ($today->format('j') < 8) {
// 21日以降の場合、翌月の8日と21日を設定
$prevBaseDate = clone $today;
$prevBaseDate->modify('first day of -1 months')->setDate($prevBaseDate->format('Y'), $prevBaseDate->format('m'), 21);
$nextBaseDate->setDate($today->format('Y'), $today->format('m'), 8);
$nextNextBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
} elseif ($today->format('j') < 21) {
$prevBaseDate->setDate($today->format('Y'), $today->format('m'), 8);
$nextBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
// 21日以降の場合、翌月の8日と21日を設定
$nextNextBaseDate = clone $today;
$nextNextBaseDate->modify('first day of next month')->setDate($nextNextBaseDate->format('Y'), $nextNextBaseDate->format('m'), 8);
} else {
$prevBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
// 21日以降の場合、翌月の8日と21日を設定
$nextBaseDate = clone $today;
$nextBaseDate->modify('first day of next month')->setDate($nextBaseDate->format('Y'), $nextBaseDate->format('m'), 8);
$nextNextBaseDate = clone $nextBaseDate;
$nextNextBaseDate->setDate($nextNextBaseDate->format('Y'), $nextNextBaseDate->format('m'), 21);
}
// 初期設定の締切日数
$prevDeadline = 7;
$nextDeadline = 7;
$nextNextDeadline = 7;
//前回定期の計算
do {
//-----------------
//月:1〜日:7
//週末:6(土),7(日)
//-----------------
$dayOfWeek = $prevBaseDate->format('N');
//週末判定
$isWeekend = $dayOfWeek >= 6;
// 祝日の判定
$isHoliday = in_array($prevBaseDate->format('Y-m-d'), $holidays);
//土日祝の場合は、締切日を-1ずつしていく
if ($isWeekend || $isHoliday) {
$prevBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
$prevDeadline--; // 締切日を1日前倒し
}
} while ($isWeekend || $isHoliday);
//次回定期の計算
do {
//-----------------
//月:1〜日:7
//週末:6(土),7(日)
//-----------------
$dayOfWeek = $nextBaseDate->format('N');
//週末判定
$isWeekend = $dayOfWeek >= 6;
// 祝日の判定
$isHoliday = in_array($nextBaseDate->format('Y-m-d'), $holidays);
//土日祝の場合は、締切日を-1ずつしていく
if ($isWeekend || $isHoliday) {
$nextBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
$nextDeadline--; // 締切日を1日前倒し
}
} while ($isWeekend || $isHoliday);
//次々回定期の計算
do {
//-----------------
//月:1〜日:7
//週末:6(土),7(日)
//-----------------
$dayOfWeek = $nextNextBaseDate->format('N');
//週末判定
$isWeekend = $dayOfWeek >= 6;
// 祝日の判定
$isHoliday = in_array($nextNextBaseDate->format('Y-m-d'), $holidays);
//土日祝の場合は、締切日を-1ずつしていく
if ($isWeekend || $isHoliday) {
$nextNextBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
$nextNextDeadline--; // 締切日を1日前倒し
}
} while ($isWeekend || $isHoliday);
//=============================
//締切日が0以下にならないように調整
//最低値 = 1 配送日の前日
//=============================
$prevDeadline = max($prevDeadline, 1);
$nextDeadline = max($nextDeadline, 1);
$nextNextDeadline = max($nextNextDeadline, 1);
//現在の受注締切設定
$config = $this->entityManager->getRepository(Config::class)->find(1);
$currentDeadLine = $config->getRegularOrderDeadline();
//=================
//今月の受注締切日
//毎月15日、28日から適切な定期受注締切日を引いた値
//=================
// 今月の年と月を取得
$year = $today->format('Y');
$month = $today->format('m');
$day = $today->format('j');
//次回、次々回の締切との配送日
if ($day < 8) {
$prevDeadlineDate = (clone $today)->modify('first day of -1 months')->setDate($today->format('Y'), $today->format('m') - 1, 28);
$prevDeadlineDeliverDate = "28日配送";
$nextDeadlineDate = new \DateTime("$year-$month-15");
$nextDeadlineDeliverDate = "15日配送";
$nextNextDeadlineDate = new \DateTime("$year-$month-28");
$nextNextDeadlineDeliverDate = "28日配送";
} elseif ($day < 21) {
$prevDeadlineDate = new \DateTime("$year-$month-15");
$prevDeadlineDeliverDate = "15日配送";
$nextDeadlineDate = new \DateTime("$year-$month-28");
$nextDeadlineDeliverDate = "28日配送";
$nextNextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 1, 15);
$nextNextDeadlineDeliverDate = "15日配送";
} else {
$prevDeadlineDate = new \DateTime("$year-$month-28");
$prevDeadlineDeliverDate = "28日配送";
$nextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 1, 15);
$nextDeadlineDeliverDate = "15日配送";
$nextNextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 1, 28);
$nextNextDeadlineDeliverDate = "28日配送";
}
// 次に来る設定値の変更日(cron実行日)を設定
$nextModifyDate = new \DateTime();
if ($today->format('j') < 8) {
$nextModifyDate->setDate($today->format('Y'), $today->format('m'), 8);
} elseif ($today->format('j') < 21) {
$nextModifyDate->setDate($today->format('Y'), $today->format('m'), 21);
} else {
$nextModifyDate->setDate($today->format('Y'), $today->format('m') + 1, 8);
}
//次回、次々回の締切日
$prevDate = $prevDeadlineDate;
$nextDate = $nextDeadlineDate;
$nextNextDate = $nextNextDeadlineDate;
// 締切日数を考慮して日付を調整
$prevDeadlineDate->modify('-' . $prevDeadline . ' days');
$nextDeadlineDate->modify('-' . $nextDeadline . ' days');
$nextNextDeadlineDate->modify('-' . $nextNextDeadline . ' days');
// 現在のユーザーを取得(ログインユーザー)
$member = $this->getUser();
$authority = $member->getAuthority();
//代理店Idを取得
$agencyId = $this->customizeCustomerRepository->findAgencyIdByMemberLoginId($member->getUsername());
$agencyRepository = $this->getDoctrine()->getRepository(Agency::class);
$agency = $agencyRepository->findOneBy(['id' => $agencyId]);
return [
'authority' => $authority,
'agency' => $agency,
'agencyId' => $agencyId,
'Orders' => $Orders,
'OrderStatuses' => $OrderStatuses,
'salesThisYear' => $salesThisYear,
'salesThisMonth' => $salesThisMonth,
'salesThisWeek' => $salesThisWeek,
'salesToday' => $salesToday,
'salesYesterday' => $salesYesterday,
'salesLastMonth' => $salesLastMonth,
'salesSameMonthLastYear' => $salesSameMonthLastYear,
'totalSalesData' => $totalSalesData,
'countNonStockProducts' => $countNonStockProducts,
'countProducts' => $countProducts,
'countCustomers' => $countCustomers,
'recommendedPlugins' => $recommendedPlugins,
'is_danger_admin_url' => $is_danger_admin_url,
'currentDeadLine' => $currentDeadLine,
'nextDeadline' => $nextDeadline,
'nextNextDeadline' => $nextNextDeadline,
'nextModifyDate' => $nextModifyDate,
'prevDate' => $prevDate,
'nextDate' => $nextDate,
'nextNextDate' => $nextNextDate,
'today' => $today,
'prevDeadlineDate' => $prevDeadlineDate,
'nextDeadlineDate' => $nextDeadlineDate,
'nextNextDeadlineDate' => $nextNextDeadlineDate,
'prevDeadlineDeliverDate' => $prevDeadlineDeliverDate,
'nextDeadlineDeliverDate' => $nextDeadlineDeliverDate,
'nextNextDeadlineDeliverDate' => $nextNextDeadlineDeliverDate,
'nextNextDeadline' => $nextNextDeadline,
'prevDeadline' => $prevDeadline,
'nextDeadline' => $nextDeadline,
'prevBaseDate' => $prevBaseDate,
'holidaysText' => $holidaysText
];
}
//----------------
//祝日の取得(Google API)
//----------------
protected function fetchApiHolidays()
{
// 取得したAPIキー
$api_key = 'AIzaSyAijfWeKMkkG-ej5cdeGVLh5OF-hSslVqo';
// カレンダーID
$calendar_id = urlencode('japanese__ja@holiday.calendar.google.com'); // Googleの提供する日本の祝日カレンダー
// 過去1ヶ月の最初の日を取得
$startOfThreeMonthsAgo = (new \DateTime('first day of -1 months'))->format('Y-m-01\T00:00:00\Z');
// 未来12ヶ月の最後の日を取得
$endOfThreeMonthsAhead = (new \DateTime('last day of +12 months'))->format('Y-m-t\T23:59:59\Z');
$url = "https://www.googleapis.com/calendar/v3/calendars/" . $calendar_id . "/events?";
$query = [
'key' => $api_key,
'timeMin' => $startOfThreeMonthsAgo,
'timeMax' => $endOfThreeMonthsAhead,
'maxResults' => 50,
'orderBy' => 'startTime',
'singleEvents' => 'true'
];
$results = [];
if ($data = file_get_contents($url . http_build_query($query), true)) {
$data = json_decode($data);
foreach ($data->items as $row) {
// 日付が全日のイベントの場合はstart.dateを、そうでない場合はstart.dateTimeから日付部分のみを使用
$date = isset($row->start->date) ? $row->start->date : substr($row->start->dateTime, 0, 10);
// 日付をY-m-d H:i:s形式に変換してリストに追加、時間は15:00:00を静的に設定
$formattedDate = (new \DateTime($date))->format('Y-m-d');
// 祝日の名前ではなく、日付自体を値として格納
$results[] = $formattedDate;
}
}
return $results;
}
/**
* テスト用の任意の休日を含んだ祝日のリストを返す関数
*
* @param array $customHolidays テスト用の祝日の配列
* @return array 祝日のリスト
*/
protected function fetchHolidays($customHolidays = [])
{
$apiHolidays = $this->fetchApiHolidays(); // APIから取得した祝日を取得する仮想関数
// APIから取得した祝日とカスタム祝日を結合
$holidays = array_merge($apiHolidays, $customHolidays);
return $holidays;
}
/**
* 売上状況の取得
*
* @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');
//代理店手数料
$toDate = Carbon::now();
$fromDate = Carbon::today()->subWeek();
$agencyCommissionWeekly = $this->getAgencyCommissionDataByPeriod($fromDate, $toDate, 'Y/m/d');
// 月間の売上金額/代理店手数料
$fromDate = Carbon::now()->startOfMonth();
$rawMonthly = $this->getData($fromDate, $toDate, 'Y/m/d');
//代理店手数料
$fromDate = Carbon::now()->startOfMonth();
$agencyCommissionMonthly = $this->getAgencyCommissionDataByPeriod($fromDate, $toDate, 'Y/m/d');
// 年間の売上金額/代理店手数料
$fromDate = Carbon::now()->subYear()->startOfMonth();
$rawYear = $this->getData($fromDate, $toDate, 'Y/m');
//代理店手数料
$fromDate = Carbon::now()->subYear()->startOfMonth();
$agencyCommissionYearly = $this->getAgencyCommissionDataByPeriod($fromDate, $toDate, 'Y/m');
// 売上データと代理店手数料のデータを組み合わせる
$datas = [
'sales' => [$rawWeekly, $rawMonthly, $rawYear], // 既存の売上データ
'agencyCommission' => [$agencyCommissionWeekly, $agencyCommissionMonthly, $agencyCommissionYearly], // 追加した代理店手数料のデータ
];
// $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');
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(DISTINCT o.id) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->andWhere('o.OrderStatus IN (:targetStatus)')
->andWhere('s.shipping_date IS NOT NULL')
->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
->setParameter(':targetStatus', $this->targetStatus)
->setParameter(':targetDateStart', $dateTimeStart)
->setParameter(':targetDateEnd', $dateTimeEnd)
->orderBy('s.shipping_date');
$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');
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
// メインクエリ
$qb = $this->orderRepository->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->setParameter('targetStatus', $this->targetStatus)
->setParameter('targetDateStart', $dateTimeStart)
->setParameter('targetDateEnd', $dateTimeEnd)
->andWhere('s.shipping_date IS NOT NULL')
->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
->andWhere('o.OrderStatus IN (:targetStatus)');
$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 getSalesByWeek($dateTime)
{
$dateTimeStart = clone $dateTime;
$dateTimeStart->setTime(0, 0, 0, 0);
$dateTimeStart->modify('this week'); // 今週の開始日(通常は月曜日)
$dateTimeEnd = clone $dateTimeStart;
$dateTimeEnd->modify('+1 week'); // 次の週の開始日(今週の終了日の次の日)
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
// メインクエリ
$qb = $this->orderRepository->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->setParameter('targetStatus', $this->targetStatus)
->setParameter('targetDateStart', $dateTimeStart)
->setParameter('targetDateEnd', $dateTimeEnd)
->andWhere('s.shipping_date IS NOT NULL')
->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
->andWhere('o.OrderStatus IN (:targetStatus)');
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す
}
return $result;
}
/**
* カスタマイズ 新規作成
* 今年の売上とその件数を取得
*
* @return array|mixed
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getSalesByYear()
{
$now = new \DateTime();
$yearStart = Carbon::createFromDate($now->format('Y'), 1, 1)->startOfDay(); // 今年の1月1日
$yearEnd = Carbon::createFromDate($now->format('Y'), 12, 31)->endOfDay(); // 今年の12月31日
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
// メインクエリ
$qb = $this->orderRepository->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->andWhere('s.shipping_date IS NOT NULL')
->andWhere('s.shipping_date >= :yearStart AND s.shipping_date <= :yearEnd')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameter('yearStart', $yearStart)
->setParameter('yearEnd', $yearEnd)
->setParameter('targetStatus', $this->targetStatus);
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す
}
return $result;
}
/**
* カスタマイズ 新規作成
* 前月の売上金額と売上件数を取得
*
* @return array|mixed
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getSalesByLastMonth()
{
$lastMonthStart = new Carbon('first day of last month midnight'); // 前月の初日
$lastMonthEnd = new Carbon('last day of last month 23:59:59'); // 前月の最終日
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->andWhere('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
// メインクエリ
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->andWhere('s.shipping_date >= :lastMonthStart AND s.shipping_date <= :lastMonthEnd')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameters([
'lastMonthStart' => $lastMonthStart,
'lastMonthEnd' => $lastMonthEnd,
'targetStatus' => $this->targetStatus,
]);
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す
}
return $result;
}
/**
* カスタマイズ 新規作成
* 前年同月の売上金額と売上件数を取得
*
* @return array|mixed
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getSalesBySameMonthLastYear()
{
// 前年同月の初日
$sameMonthLastYearStart = new Carbon('first day of last year this month midnight');
// 前年同月の最終日
$sameMonthLastYearEnd = new Carbon('last day of last year this month 23:59:59');
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->andWhere('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
COUNT(o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->andWhere('s.shipping_date >= :sameMonthLastYearStart AND s.shipping_date <= :sameMonthLastYearEnd')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameters([
'sameMonthLastYearStart' => $sameMonthLastYearStart,
'sameMonthLastYearEnd' => $sameMonthLastYearEnd,
'targetStatus' => $this->targetStatus,
]);
$q = $qb->getQuery();
$result = [];
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す
}
return $result;
}
/**
* カスタマイズ 新規作成
* 累計の売上金額、平均単価、および売上件数を取得
*
* @return array
* @throws \Doctrine\ORM\NonUniqueResultException
*/
protected function getTotalSalesAndAveragePrice()
{
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.id)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
$qb = $this->orderRepository
->createQueryBuilder('o')
->select('
SUM(o.payment_total - o.delivery_fee_total) AS total_amount,
COUNT(o) AS total_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->andWhere('s.shipping_date IS NOT NULL')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameter('targetStatus', $this->targetStatus);
$q = $qb->getQuery();
try {
$result = $q->getSingleResult();
} catch (NoResultException $e) {
// 結果がない場合は空の配列を返す
return [
'total_amount' => 0,
'total_count' => 0,
'average_price' => 0,
];
}
// 平均単価を計算(売上件数が0の場合は平均単価も0)
$averagePrice = $result['total_count'] > 0 ? $result['total_amount'] / $result['total_count'] : 0;
return [
'total_amount' => $result['total_amount'],
'total_count' => $result['total_count'],
'average_price' => $averagePrice,
];
}
/**
* 在庫切れ商品数を取得
*
* @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)
{
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.shipping_date)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
$qb = $this->orderRepository->createQueryBuilder('o')
->select('DISTINCT o')
// ->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
// ->select('SUM(o.payment_total - o.delivery_fee_total) AS order_amount, COUNT(DISTINCT o) AS order_count')
->leftJoin('o.Shippings', 's', 'WITH', 's.shipping_date = (' . $subQueryDQL . ')')
// ->andWhere('o.order_date >= :fromDate')
// ->andWhere('o.order_date <= :toDate')
->andWhere('s.shipping_date >= :fromDate AND s.shipping_date <= :toDate')
->andWhere('s.shipping_date IS NOT NULL')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameter(':targetStatus', $this->targetStatus)
->setParameter(':fromDate', $fromDate->copy())
->setParameter(':toDate', $toDate->copy())
->groupBy('o.id');
$result = $qb->getQuery()->getResult();
return $this->convert($result, $fromDate, $toDate, $format);
}
/**
* 期間指定のデータを取得
*
* @param Carbon $fromDate
* @param Carbon $toDate
* @param $format
*
* @return array
*/
protected function getAgencyCommissionDataByPeriod(Carbon $fromDate, Carbon $toDate, $format)
{
// $qb = $this->orderRepository->createQueryBuilder('o')
// ->andWhere('o.order_date >= :fromDate')
// ->andWhere('o.order_date <= :toDate')
// ->andWhere('o.OrderStatus IN (:targetStatus)')
// ->setParameter(':targetStatus', $this->targetStatus)
// ->setParameter(':fromDate', $fromDate->copy())
// ->setParameter(':toDate', $toDate->copy())
// ->orderBy('o.order_date');
// サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
$subQb = $this->orderRepository->createQueryBuilder('o2')
->select('MAX(s2.shipping_date)')
->leftJoin('o2.Shippings', 's2')
->where('s2.shipping_date IS NOT NULL')
->andWhere('o2.id = o.id')
->orderBy('s2.shipping_date', 'DESC')
->setMaxResults(1);
$subQueryDQL = $subQb->getDQL();
$qb = $this->orderRepository->createQueryBuilder('o')
->select('DISTINCT o')
// ->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
->leftJoin('o.Shippings', 's', 'WITH', 's.shipping_date = (' . $subQueryDQL . ')')
// ->andWhere('o.order_date >= :fromDate')
// ->andWhere('o.order_date <= :toDate')
->andWhere('s.shipping_date >= :fromDate AND s.shipping_date <= :toDate')
->andWhere('s.shipping_date IS NOT NULL')
->andWhere('o.OrderStatus IN (:targetStatus)')
->setParameter(':targetStatus', $this->targetStatus)
->setParameter(':fromDate', $fromDate->copy())
->setParameter(':toDate', $toDate->copy())
->groupBy('o.id');
$result = $qb->getQuery()->getResult();
return $this->convertAgencyCommission($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) {
$shippings = $Order->getShippings()->toArray();
// shipping_dateがnullでないshippingsのみを抽出
$validShippings = array_filter($shippings, function ($shipping) {
return $shipping->getShippingDate() !== null;
});
// shipping_dateで降順にソート
usort($validShippings, function ($a, $b) {
return $b->getShippingDate() <=> $a->getShippingDate();
});
// 最初のshippingのshipping_dateを取得
$orderDate = reset($validShippings)->getShippingDate()->format($format);
if (!isset($raw[$orderDate]['price'])) {
continue; // もしくはエラー処理を行うなど適切な処理を行う
}
$raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getPaymentTotal();
++$raw[$Order->getOrderDate()->format($format)]['count'];
}
return $raw;
}
/**
* カスタマイズ 新規作成
* 期間毎に代理店手数料データをまとめるためのカスタマイズされたconvert関数
*
* @param $result
* @param Carbon $fromDate
* @param Carbon $toDate
* @param $format
*
* @return array
*/
protected function convertAgencyCommission($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) {
$shippings = $Order->getShippings()->toArray();
// shipping_dateがnullでないshippingsのみを抽出
$validShippings = array_filter($shippings, function ($shipping) {
return $shipping->getShippingDate() !== null;
});
// shipping_dateで降順にソート
usort($validShippings, function ($a, $b) {
return $b->getShippingDate() <=> $a->getShippingDate();
});
// 最初のshippingのshipping_dateを取得
$orderDate = reset($validShippings)->getShippingDate()->format($format);
// orderDateが期間内でない場合はスキップ
$targetFromDate = $fromDate->format($format);
$targetToDate = $toDate->format($format);
error_log($orderDate);
error_log($targetFromDate);
error_log($targetToDate);
if (!isset($raw[$orderDate]['price'])) {
continue; // もしくはエラー処理を行うなど適切な処理を行う
}
$raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getAgencyCommissionAmount();
++$raw[$Order->getOrderDate()->format($format)]['count'];
}
return $raw;
}
}