src/Eccube/Controller/Admin/AdminController.php line 168

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 Eccube\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. use Plugin\EccubePaymentLite42\Entity\Config;
  40. // use Plugin\EccubePaymentLite42\Repository\ConfigRepository;
  41. // use Doctrine\ORM\EntityManagerInterface;
  42. use Customize\Repository\CustomizeCustomerRepository//代理店名の取得はカスタムリポジトリを使用
  43. use Customize\Entity\Agency;
  44. class AdminController extends AbstractController
  45. {
  46.     /**
  47.      * @var AuthorizationCheckerInterface
  48.      */
  49.     protected $authorizationChecker;
  50.     /**
  51.      * @var AuthenticationUtils
  52.      */
  53.     protected $helper;
  54.     /**
  55.      * @var MemberRepository
  56.      */
  57.     protected $memberRepository;
  58.     /**
  59.      * @var EncoderFactoryInterface
  60.      */
  61.     protected $encoderFactory;
  62.     /**
  63.      * @var OrderRepository
  64.      */
  65.     protected $orderRepository;
  66.     /**
  67.      * @var OrderStatusRepository
  68.      */
  69.     protected $orderStatusRepository;
  70.     /**
  71.      * @var CustomerRepository
  72.      */
  73.     protected $customerRepository;
  74.     /**
  75.      * @var ProductRepository
  76.      */
  77.     protected $productRepository;
  78.     /** @var PluginApiService */
  79.     protected $pluginApiService;
  80.     /**
  81.      * @var array 売り上げ状況用受注状況
  82.      */
  83.     private $excludes = [OrderStatus::CANCELOrderStatus::PENDINGOrderStatus::PROCESSINGOrderStatus::RETURNED];
  84.     /**
  85.      * @var array 売り上げ状況用受注状況
  86.      */
  87.     private $targetStatus = [
  88.         //発送済み
  89.         OrderStatus::DELIVERED,
  90.         //対応中
  91.         OrderStatus::IN_PROGRESS,
  92.         //対応済
  93.         OrderStatus::ALREADY_SUPPORTED,
  94.     ];
  95.     private $customizeCustomerRepository;
  96.     // private $entityManager;
  97.     // private $configRepository;
  98.     /**
  99.      * AdminController constructor.
  100.      *
  101.      * @param AuthorizationCheckerInterface $authorizationChecker
  102.      * @param AuthenticationUtils $helper
  103.      * @param MemberRepository $memberRepository
  104.      * @param EncoderFactoryInterface $encoderFactory
  105.      * @param OrderRepository $orderRepository
  106.      * @param OrderStatusRepository $orderStatusRepository
  107.      * @param CustomerRepository $custmerRepository
  108.      * @param ProductRepository $productRepository
  109.      * @param PluginApiService $pluginApiService
  110.      * @param CustomizeCustomerRepository $customizeCustomerRepository
  111.      */
  112.     public function __construct(
  113.         AuthorizationCheckerInterface $authorizationChecker,
  114.         AuthenticationUtils $helper,
  115.         MemberRepository $memberRepository,
  116.         EncoderFactoryInterface $encoderFactory,
  117.         OrderRepository $orderRepository,
  118.         OrderStatusRepository $orderStatusRepository,
  119.         CustomerRepository $custmerRepository,
  120.         ProductRepository $productRepository,
  121.         PluginApiService $pluginApiService,
  122.         CustomizeCustomerRepository $customizeCustomerRepository
  123.         // EntityManagerInterface $entityManager,
  124.         // ConfigRepository $configRepository
  125.     ) {
  126.         $this->authorizationChecker $authorizationChecker;
  127.         $this->helper $helper;
  128.         $this->memberRepository $memberRepository;
  129.         $this->encoderFactory $encoderFactory;
  130.         $this->orderRepository $orderRepository;
  131.         $this->orderStatusRepository $orderStatusRepository;
  132.         $this->customerRepository $custmerRepository;
  133.         $this->productRepository $productRepository;
  134.         $this->pluginApiService $pluginApiService;
  135.         $this->customizeCustomerRepository $customizeCustomerRepository;
  136.         // $this->entityManager = $entityManager;
  137.         // $this->configRepository = $configRepository;
  138.     }
  139.     /**
  140.      * @Route("/%eccube_admin_route%/login", name="admin_login", methods={"GET", "POST"})
  141.      * @Template("@admin/login.twig")
  142.      */
  143.     public function login(Request $request)
  144.     {
  145.         if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
  146.             return $this->redirectToRoute('admin_homepage');
  147.         }
  148.         /* @var $form \Symfony\Component\Form\FormInterface */
  149.         $builder $this->formFactory->createNamedBuilder(''LoginType::class);
  150.         $event = new EventArgs(
  151.             [
  152.                 'builder' => $builder,
  153.             ],
  154.             $request
  155.         );
  156.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_LOGIN_INITIALIZE);
  157.         $form $builder->getForm();
  158.         return [
  159.             'error' => $this->helper->getLastAuthenticationError(),
  160.             'form' => $form->createView(),
  161.         ];
  162.     }
  163.     /**
  164.      * 管理画面ホーム
  165.      *
  166.      * @param Request $request
  167.      *
  168.      * @return array
  169.      *
  170.      * @throws NoResultException
  171.      * @throws \Doctrine\ORM\NonUniqueResultException
  172.      *
  173.      * @Route("/%eccube_admin_route%/", name="admin_homepage", methods={"GET"})
  174.      * @Template("@admin/index.twig")
  175.      */
  176.     public function index(Request $request)
  177.     {
  178.         $adminRoute $this->eccubeConfig['eccube_admin_route'];
  179.         $is_danger_admin_url false;
  180.         if ($adminRoute === 'admin') {
  181.             $is_danger_admin_url true;
  182.         }
  183.         /**
  184.          * 受注状況.
  185.          */
  186.         $excludes = [];
  187.         $excludes[] = OrderStatus::CANCEL;
  188.         $excludes[] = OrderStatus::DELIVERED;
  189.         $excludes[] = OrderStatus::PENDING;
  190.         $excludes[] = OrderStatus::PROCESSING;
  191.         $excludes[] = OrderStatus::RETURNED;
  192.         // 対象とする受注ステータスを定義
  193.         $targetStatus = [];
  194.         $targetStatus = [
  195.             //発送済み
  196.             OrderStatus::DELIVERED,
  197.             //対応中
  198.             OrderStatus::IN_PROGRESS,
  199.             //対応済
  200.             OrderStatus::ALREADY_SUPPORTED,
  201.         ];
  202.         $event = new EventArgs(
  203.             [
  204.                 // 'excludes' => $excludes,
  205.             ],
  206.             $request
  207.         );
  208.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_ORDER);
  209.         // $excludes = $event->getArgument('excludes');
  210.         // 受注ステータスごとの受注件数.
  211.         $Orders $this->getOrderEachStatus($excludes);
  212.         // 受注ステータスの一覧.
  213.         $Criteria = new Criteria();
  214.         $Criteria
  215.             ->where($Criteria::expr()->notIn('id'$excludes))
  216.             ->orderBy(['sort_no' => 'ASC']);
  217.         $OrderStatuses $this->orderStatusRepository->matching($Criteria);
  218.         /**
  219.          * 売り上げ状況
  220.          */
  221.         $event = new EventArgs(
  222.             [
  223.                 // 'excludes' => $this->excludes,
  224.             ],
  225.             $request
  226.         );
  227.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_SALES);
  228.         // $this->excludes = $event->getArgument('excludes');
  229.         // 今日の売上/件数
  230.         $salesToday $this->getSalesByDay(new \DateTime());
  231.         // 昨日の売上/件数
  232.         $salesYesterday $this->getSalesByDay(new \DateTime('-1 day'));
  233.         // 今月の売上/件数
  234.         $salesThisMonth $this->getSalesByMonth(new \DateTime());
  235.         //---------------
  236.         //カスタマイズ 売上状況に新項目を追加
  237.         //---------------
  238.         //前月の売上金額 / 売上件数
  239.         $salesLastMonth $this->getSalesByLastMonth();
  240.         // 前年同月の売上/件数の取得
  241.         $salesSameMonthLastYear $this->getSalesBySameMonthLastYear();
  242.         // 累計の売上金額、平均単価、売上件数の取得
  243.         $totalSalesData $this->getTotalSalesAndAveragePrice();
  244.         //今週の販売数量 / 売上件数
  245.         $salesThisWeek $this->getSalesByWeek(new \DateTime());
  246.         // 今年の売上/件数の取得
  247.         $salesThisYear $this->getSalesByYear();
  248.         /**
  249.          * ショップ状況
  250.          */
  251.         // 在庫切れ商品数
  252.         $countNonStockProducts $this->countNonStockProducts();
  253.         // 取り扱い商品数
  254.         $countProducts $this->countProducts();
  255.         // 本会員数
  256.         $countCustomers $this->countCustomers();
  257.         $event = new EventArgs(
  258.             [
  259.                 'Orders' => $Orders,
  260.                 'OrderStatuses' => $OrderStatuses,
  261.                 'salesThisYear' => $salesThisYear,
  262.                 'salesThisMonth' => $salesThisMonth,
  263.                 'salesThisWeek' => $salesThisWeek,
  264.                 'salesToday' => $salesToday,
  265.                 'salesYesterday' => $salesYesterday,
  266.                 'salesLastMonth' => $salesLastMonth,
  267.                 'salesSameMonthLastYear' => $salesSameMonthLastYear,
  268.                 'totalSalesData' => $totalSalesData,
  269.                 'countNonStockProducts' => $countNonStockProducts,
  270.                 'countProducts' => $countProducts,
  271.                 'countCustomers' => $countCustomers,
  272.             ],
  273.             $request
  274.         );
  275.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_COMPLETE);
  276.         // 推奨プラグイン
  277.         $recommendedPlugins = [];
  278.         try {
  279.             $recommendedPlugins $this->pluginApiService->getRecommended();
  280.         } catch (PluginApiException $ignore) {
  281.         }
  282.         //定期受注締切日
  283.         //-------------
  284.         //テスト用 (任意の休日と祝日を統合)
  285.         //-------------
  286.         // $customHolidays =
  287.         //     [
  288.         //         '2024-03-04',
  289.         //         '2024-03-03',
  290.         //         '2024-03-05',
  291.         //         '2024-03-06',
  292.         //         '2024-03-07',
  293.         //         '2024-03-08',
  294.         //         '2024-03-09',
  295.         //         '2024-03-10',
  296.         //         '2024-09-09',
  297.         //     ];
  298.         // $holidays = $this->fetchHolidays($customHolidays);
  299.         //本番用 祝日情報の取得
  300.         $holidays $this->fetchApiHolidays();
  301.         //-----------
  302.         //配列確認用
  303.         //-----------
  304.         $holidaysText "";
  305.         foreach ($holidays as $holiday) {
  306.             $holidaysText .= $holiday;
  307.             $holidaysText .= "___";
  308.         }
  309.         //今日の日付
  310.         $today = new \DateTime();
  311.         //テスト用特定の日付
  312.         // $today = new \DateTime('2024-4-8');
  313.         // ==============
  314.         //基準日の設定
  315.         //7日までは、8日時点の受注締切日の日数を設定
  316.         //20日までは、21日時点の受注締切日の日数を設定
  317.         //21日以降は、翌月8日時点の受注締切日の日数を設定
  318.         // ==============
  319.         //次回定期の基準日
  320.         $prevBaseDate = new \DateTime();
  321.         //次回定期の基準日
  322.         $nextBaseDate = new \DateTime();
  323.         //次次回定期の基準日
  324.         $nextNextBaseDate = new \DateTime();
  325.         if ($today->format('j') < 8) {
  326.             // 21日以降の場合、翌月の8日と21日を設定
  327.             $prevBaseDate = clone $today;
  328.             $prevBaseDate->modify('first day of -1 months')->setDate($prevBaseDate->format('Y'), $prevBaseDate->format('m'), 21);
  329.             $nextBaseDate->setDate($today->format('Y'), $today->format('m'), 8);
  330.             $nextNextBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
  331.         } elseif ($today->format('j') < 21) {
  332.             $prevBaseDate->setDate($today->format('Y'), $today->format('m'), 8);
  333.             $nextBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
  334.             // 21日以降の場合、翌月の8日と21日を設定
  335.             $nextNextBaseDate = clone $today;
  336.             $nextNextBaseDate->modify('first day of next month')->setDate($nextNextBaseDate->format('Y'), $nextNextBaseDate->format('m'), 8);
  337.         } else {
  338.             $prevBaseDate->setDate($today->format('Y'), $today->format('m'), 21);
  339.             // 21日以降の場合、翌月の8日と21日を設定
  340.             $nextBaseDate = clone $today;
  341.             $nextBaseDate->modify('first day of next month')->setDate($nextBaseDate->format('Y'), $nextBaseDate->format('m'), 8);
  342.             $nextNextBaseDate = clone $nextBaseDate;
  343.             $nextNextBaseDate->setDate($nextNextBaseDate->format('Y'), $nextNextBaseDate->format('m'), 21);
  344.         }
  345.         // 初期設定の締切日数
  346.         $prevDeadline 7;
  347.         $nextDeadline 7;
  348.         $nextNextDeadline 7;
  349.         //前回定期の計算
  350.         do {
  351.             //-----------------
  352.             //月:1〜日:7
  353.             //週末:6(土),7(日)
  354.             //-----------------
  355.             $dayOfWeek $prevBaseDate->format('N');
  356.             //週末判定
  357.             $isWeekend $dayOfWeek >= 6;
  358.             // 祝日の判定
  359.             $isHoliday in_array($prevBaseDate->format('Y-m-d'), $holidays);
  360.             //土日祝の場合は、締切日を-1ずつしていく
  361.             if ($isWeekend || $isHoliday) {
  362.                 $prevBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
  363.                 $prevDeadline--; // 締切日を1日前倒し
  364.             }
  365.         } while ($isWeekend || $isHoliday);
  366.         //次回定期の計算
  367.         do {
  368.             //-----------------
  369.             //月:1〜日:7
  370.             //週末:6(土),7(日)
  371.             //-----------------
  372.             $dayOfWeek $nextBaseDate->format('N');
  373.             //週末判定
  374.             $isWeekend $dayOfWeek >= 6;
  375.             // 祝日の判定
  376.             $isHoliday in_array($nextBaseDate->format('Y-m-d'), $holidays);
  377.             //土日祝の場合は、締切日を-1ずつしていく
  378.             if ($isWeekend || $isHoliday) {
  379.                 $nextBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
  380.                 $nextDeadline--; // 締切日を1日前倒し
  381.             }
  382.         } while ($isWeekend || $isHoliday);
  383.         //次々回定期の計算
  384.         do {
  385.             //-----------------
  386.             //月:1〜日:7
  387.             //週末:6(土),7(日)
  388.             //-----------------
  389.             $dayOfWeek $nextNextBaseDate->format('N');
  390.             //週末判定
  391.             $isWeekend $dayOfWeek >= 6;
  392.             // 祝日の判定
  393.             $isHoliday in_array($nextNextBaseDate->format('Y-m-d'), $holidays);
  394.             //土日祝の場合は、締切日を-1ずつしていく
  395.             if ($isWeekend || $isHoliday) {
  396.                 $nextNextBaseDate->modify('+1 day'); // 休日または週末なら日付を1日進める
  397.                 $nextNextDeadline--; // 締切日を1日前倒し
  398.             }
  399.         } while ($isWeekend || $isHoliday);
  400.         //=============================
  401.         //締切日が0以下にならないように調整
  402.         //最低値 = 1 配送日の前日
  403.         //=============================
  404.         $prevDeadline max($prevDeadline1);
  405.         $nextDeadline max($nextDeadline1);
  406.         $nextNextDeadline max($nextNextDeadline1);
  407.         //現在の受注締切設定
  408.         $config $this->entityManager->getRepository(Config::class)->find(1);
  409.         $currentDeadLine $config->getRegularOrderDeadline();
  410.         //=================
  411.         //今月の受注締切日
  412.         //毎月15日、28日から適切な定期受注締切日を引いた値
  413.         //=================
  414.         // 今月の年と月を取得
  415.         $year $today->format('Y');
  416.         $month $today->format('m');
  417.         $day $today->format('j');
  418.         //次回、次々回の締切との配送日
  419.         if ($day 8) {
  420.             $prevDeadlineDate = (clone $today)->modify('first day of -1 months')->setDate($today->format('Y'), $today->format('m') - 128);
  421.             $prevDeadlineDeliverDate "28日配送";
  422.             $nextDeadlineDate = new \DateTime("$year-$month-15");
  423.             $nextDeadlineDeliverDate "15日配送";
  424.             $nextNextDeadlineDate = new \DateTime("$year-$month-28");
  425.             $nextNextDeadlineDeliverDate "28日配送";
  426.         } elseif ($day 21) {
  427.             $prevDeadlineDate = new \DateTime("$year-$month-15");
  428.             $prevDeadlineDeliverDate "15日配送";
  429.             $nextDeadlineDate = new \DateTime("$year-$month-28");
  430.             $nextDeadlineDeliverDate "28日配送";
  431.             $nextNextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 115);
  432.             $nextNextDeadlineDeliverDate "15日配送";
  433.         } else {
  434.             $prevDeadlineDate = new \DateTime("$year-$month-28");
  435.             $prevDeadlineDeliverDate "28日配送";
  436.             $nextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 115);
  437.             $nextDeadlineDeliverDate "15日配送";
  438.             $nextNextDeadlineDate = (clone $today)->modify('first day of next month')->setDate($today->format('Y'), $today->format('m') + 128);
  439.             $nextNextDeadlineDeliverDate "28日配送";
  440.         }
  441.         // 次に来る設定値の変更日(cron実行日)を設定
  442.         $nextModifyDate = new \DateTime();
  443.         if ($today->format('j') < 8) {
  444.             $nextModifyDate->setDate($today->format('Y'), $today->format('m'), 8);
  445.         } elseif ($today->format('j') < 21) {
  446.             $nextModifyDate->setDate($today->format('Y'), $today->format('m'), 21);
  447.         } else {
  448.             $nextModifyDate->setDate($today->format('Y'), $today->format('m') + 18);
  449.         }
  450.         //次回、次々回の締切日
  451.         $prevDate $prevDeadlineDate;
  452.         $nextDate $nextDeadlineDate;
  453.         $nextNextDate $nextNextDeadlineDate;
  454.         // 締切日数を考慮して日付を調整
  455.         $prevDeadlineDate->modify('-' $prevDeadline ' days');
  456.         $nextDeadlineDate->modify('-' $nextDeadline ' days');
  457.         $nextNextDeadlineDate->modify('-' $nextNextDeadline ' days');
  458.         // 現在のユーザーを取得(ログインユーザー)
  459.         $member $this->getUser();
  460.         $authority $member->getAuthority();
  461.         //代理店Idを取得
  462.         $agencyId $this->customizeCustomerRepository->findAgencyIdByMemberLoginId($member->getUsername());
  463.         $agencyRepository $this->getDoctrine()->getRepository(Agency::class);
  464.         $agency $agencyRepository->findOneBy(['id' => $agencyId]);
  465.         return [
  466.             'authority' => $authority,
  467.             'agency' => $agency,
  468.             'agencyId' => $agencyId,
  469.             'Orders' => $Orders,
  470.             'OrderStatuses' => $OrderStatuses,
  471.             'salesThisYear' => $salesThisYear,
  472.             'salesThisMonth' => $salesThisMonth,
  473.             'salesThisWeek' => $salesThisWeek,
  474.             'salesToday' => $salesToday,
  475.             'salesYesterday' => $salesYesterday,
  476.             'salesLastMonth' => $salesLastMonth,
  477.             'salesSameMonthLastYear' => $salesSameMonthLastYear,
  478.             'totalSalesData' => $totalSalesData,
  479.             'countNonStockProducts' => $countNonStockProducts,
  480.             'countProducts' => $countProducts,
  481.             'countCustomers' => $countCustomers,
  482.             'recommendedPlugins' => $recommendedPlugins,
  483.             'is_danger_admin_url' => $is_danger_admin_url,
  484.             'currentDeadLine' => $currentDeadLine,
  485.             'nextDeadline' => $nextDeadline,
  486.             'nextNextDeadline' => $nextNextDeadline,
  487.             'nextModifyDate' => $nextModifyDate,
  488.             'prevDate' => $prevDate,
  489.             'nextDate' => $nextDate,
  490.             'nextNextDate' => $nextNextDate,
  491.             'today' => $today,
  492.             'prevDeadlineDate' => $prevDeadlineDate,
  493.             'nextDeadlineDate' => $nextDeadlineDate,
  494.             'nextNextDeadlineDate' => $nextNextDeadlineDate,
  495.             'prevDeadlineDeliverDate' => $prevDeadlineDeliverDate,
  496.             'nextDeadlineDeliverDate' => $nextDeadlineDeliverDate,
  497.             'nextNextDeadlineDeliverDate' => $nextNextDeadlineDeliverDate,
  498.             'nextNextDeadline' => $nextNextDeadline,
  499.             'prevDeadline' => $prevDeadline,
  500.             'nextDeadline' => $nextDeadline,
  501.             'prevBaseDate' => $prevBaseDate,
  502.             'holidaysText' => $holidaysText
  503.         ];
  504.     }
  505.     //----------------
  506.     //祝日の取得(Google API)
  507.     //----------------
  508.     protected function fetchApiHolidays()
  509.     {
  510.         // 取得したAPIキー
  511.         $api_key 'AIzaSyAijfWeKMkkG-ej5cdeGVLh5OF-hSslVqo';
  512.         // カレンダーID
  513.         $calendar_id urlencode('japanese__ja@holiday.calendar.google.com');  // Googleの提供する日本の祝日カレンダー
  514.         // 過去1ヶ月の最初の日を取得
  515.         $startOfThreeMonthsAgo = (new \DateTime('first day of -1 months'))->format('Y-m-01\T00:00:00\Z');
  516.         // 未来12ヶ月の最後の日を取得
  517.         $endOfThreeMonthsAhead = (new \DateTime('last day of +12 months'))->format('Y-m-t\T23:59:59\Z');
  518.         $url "https://www.googleapis.com/calendar/v3/calendars/" $calendar_id "/events?";
  519.         $query = [
  520.             'key' => $api_key,
  521.             'timeMin' => $startOfThreeMonthsAgo,
  522.             'timeMax' => $endOfThreeMonthsAhead,
  523.             'maxResults' => 50,
  524.             'orderBy' => 'startTime',
  525.             'singleEvents' => 'true'
  526.         ];
  527.         $results = [];
  528.         if ($data file_get_contents($url http_build_query($query), true)) {
  529.             $data json_decode($data);
  530.             foreach ($data->items as $row) {
  531.                 // 日付が全日のイベントの場合はstart.dateを、そうでない場合はstart.dateTimeから日付部分のみを使用
  532.                 $date = isset($row->start->date) ? $row->start->date substr($row->start->dateTime010);
  533.                 // 日付をY-m-d H:i:s形式に変換してリストに追加、時間は15:00:00を静的に設定
  534.                 $formattedDate = (new \DateTime($date))->format('Y-m-d');
  535.                 // 祝日の名前ではなく、日付自体を値として格納
  536.                 $results[] = $formattedDate;
  537.             }
  538.         }
  539.         return $results;
  540.     }
  541.     /**
  542.      * テスト用の任意の休日を含んだ祝日のリストを返す関数
  543.      *
  544.      * @param array $customHolidays テスト用の祝日の配列
  545.      * @return array 祝日のリスト
  546.      */
  547.     protected function fetchHolidays($customHolidays = [])
  548.     {
  549.         $apiHolidays $this->fetchApiHolidays(); // APIから取得した祝日を取得する仮想関数
  550.         // APIから取得した祝日とカスタム祝日を結合
  551.         $holidays array_merge($apiHolidays$customHolidays);
  552.         return $holidays;
  553.     }
  554.     /**
  555.      * 売上状況の取得
  556.      *
  557.      * @param Request $request
  558.      *
  559.      * @Route("/%eccube_admin_route%/sale_chart", name="admin_homepage_sale", methods={"GET"})
  560.      *
  561.      * @return \Symfony\Component\HttpFoundation\JsonResponse
  562.      */
  563.     public function sale(Request $request)
  564.     {
  565.         if (!($request->isXmlHttpRequest() && $this->isTokenValid())) {
  566.             return $this->json(['status' => 'NG'], 400);
  567.         }
  568.         $event = new EventArgs(
  569.             [
  570.                 'excludes' => $this->excludes,
  571.             ],
  572.             $request
  573.         );
  574.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_INDEX_SALES);
  575.         $this->excludes $event->getArgument('excludes');
  576.         //------カスタマイズ---------
  577.         //代理店手数料金額も取得
  578.         //---------------
  579.         // 代理店手数料のデータ取得ロジックをここに追加
  580.         // 週間の売上金額/代理店手数料
  581.         $toDate Carbon::now();
  582.         $fromDate Carbon::today()->subWeek();
  583.         $rawWeekly $this->getData($fromDate$toDate'Y/m/d');
  584.         //代理店手数料
  585.         $toDate Carbon::now();
  586.         $fromDate Carbon::today()->subWeek();
  587.         $agencyCommissionWeekly $this->getAgencyCommissionDataByPeriod($fromDate$toDate'Y/m/d');
  588.         // 月間の売上金額/代理店手数料
  589.         $fromDate Carbon::now()->startOfMonth();
  590.         $rawMonthly $this->getData($fromDate$toDate'Y/m/d');
  591.         //代理店手数料
  592.         $fromDate Carbon::now()->startOfMonth();
  593.         $agencyCommissionMonthly $this->getAgencyCommissionDataByPeriod($fromDate$toDate'Y/m/d');
  594.         // 年間の売上金額/代理店手数料
  595.         $fromDate Carbon::now()->subYear()->startOfMonth();
  596.         $rawYear $this->getData($fromDate$toDate'Y/m');
  597.         //代理店手数料
  598.         $fromDate Carbon::now()->subYear()->startOfMonth();
  599.         $agencyCommissionYearly $this->getAgencyCommissionDataByPeriod($fromDate$toDate'Y/m');
  600.         // 売上データと代理店手数料のデータを組み合わせる
  601.         $datas = [
  602.             'sales' => [$rawWeekly$rawMonthly$rawYear], // 既存の売上データ
  603.             'agencyCommission' => [$agencyCommissionWeekly$agencyCommissionMonthly$agencyCommissionYearly], // 追加した代理店手数料のデータ
  604.         ];
  605.         // $datas = [$rawWeekly, $rawMonthly, $rawYear];
  606.         return $this->json($datas);
  607.     }
  608.     /**
  609.      * パスワード変更画面
  610.      *
  611.      * @Route("/%eccube_admin_route%/change_password", name="admin_change_password", methods={"GET", "POST"})
  612.      * @Template("@admin/change_password.twig")
  613.      *
  614.      * @param Request $request
  615.      *
  616.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|array
  617.      */
  618.     public function changePassword(Request $request)
  619.     {
  620.         $builder $this->formFactory
  621.             ->createBuilder(ChangePasswordType::class);
  622.         $event = new EventArgs(
  623.             [
  624.                 'builder' => $builder,
  625.             ],
  626.             $request
  627.         );
  628.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIM_CHANGE_PASSWORD_INITIALIZE);
  629.         $form $builder->getForm();
  630.         $form->handleRequest($request);
  631.         if ($form->isSubmitted() && $form->isValid()) {
  632.             $Member $this->getUser();
  633.             $salt $Member->getSalt();
  634.             $password $form->get('change_password')->getData();
  635.             $encoder $this->encoderFactory->getEncoder($Member);
  636.             // 2系からのデータ移行でsaltがセットされていない場合はsaltを生成.
  637.             if (empty($salt)) {
  638.                 $salt $encoder->createSalt();
  639.             }
  640.             $password $encoder->encodePassword($password$salt);
  641.             $Member
  642.                 ->setPassword($password)
  643.                 ->setSalt($salt);
  644.             $this->memberRepository->save($Member);
  645.             $event = new EventArgs(
  646.                 [
  647.                     'form' => $form,
  648.                     'Member' => $Member,
  649.                 ],
  650.                 $request
  651.             );
  652.             $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_ADMIN_CHANGE_PASSWORD_COMPLETE);
  653.             $this->addSuccess('admin.change_password.password_changed''admin');
  654.             return $this->redirectToRoute('admin_change_password');
  655.         }
  656.         return [
  657.             'form' => $form->createView(),
  658.         ];
  659.     }
  660.     /**
  661.      * 在庫なし商品の検索結果を表示する.
  662.      *
  663.      * @Route("/%eccube_admin_route%/search_nonstock", name="admin_homepage_nonstock", methods={"GET"})
  664.      *
  665.      * @param Request $request
  666.      *
  667.      * @return \Symfony\Component\HttpFoundation\Response
  668.      */
  669.     public function searchNonStockProducts(Request $request)
  670.     {
  671.         // 在庫なし商品の検索条件をセッションに付与し, 商品マスタへリダイレクトする.
  672.         $searchData = [];
  673.         $searchData['stock'] = [ProductStock::OUT_OF_STOCK];
  674.         $session $request->getSession();
  675.         $session->set('eccube.admin.product.search'$searchData);
  676.         return $this->redirectToRoute('admin_product_page', [
  677.             'page_no' => 1,
  678.         ]);
  679.     }
  680.     /**
  681.      * 本会員の検索結果を表示する.
  682.      *
  683.      * @Route("/%eccube_admin_route%/search_customer", name="admin_homepage_customer", methods={"GET"})
  684.      *
  685.      * @param Request $request
  686.      *
  687.      * @return \Symfony\Component\HttpFoundation\Response
  688.      */
  689.     public function searchCustomer(Request $request)
  690.     {
  691.         $searchData = [];
  692.         $searchData['customer_status'] = [CustomerStatus::REGULAR];
  693.         $session $request->getSession();
  694.         $session->set('eccube.admin.customer.search'$searchData);
  695.         return $this->redirectToRoute('admin_customer_page', [
  696.             'page_no' => 1,
  697.         ]);
  698.     }
  699.     /**
  700.      * @param \Doctrine\ORM\EntityManagerInterface $em
  701.      * @param array $excludes
  702.      *
  703.      * @return Request|null
  704.      */
  705.     protected function getOrderEachStatus(array $excludes)
  706.     {
  707.         $sql 'SELECT
  708.                     t1.order_status_id as status,
  709.                     COUNT(t1.id) as count
  710.                 FROM
  711.                     dtb_order t1
  712.                 WHERE
  713.                     t1.order_status_id NOT IN (:excludes)
  714.                 GROUP BY
  715.                     t1.order_status_id
  716.                 ORDER BY
  717.                     t1.order_status_id';
  718.         $rsm = new ResultSetMapping();
  719.         $rsm->addScalarResult('status''status');
  720.         $rsm->addScalarResult('count''count');
  721.         $query $this->entityManager->createNativeQuery($sql$rsm);
  722.         $query->setParameters([':excludes' => $excludes]);
  723.         $result $query->getResult();
  724.         $orderArray = [];
  725.         foreach ($result as $row) {
  726.             $orderArray[$row['status']] = $row['count'];
  727.         }
  728.         return $orderArray;
  729.     }
  730.     /**
  731.      * @param \DateTime $dateTime
  732.      *
  733.      * @return array|mixed
  734.      *
  735.      * @throws \Doctrine\ORM\NonUniqueResultException
  736.      */
  737.     protected function getSalesByDay($dateTime)
  738.     {
  739.         $dateTimeStart = clone $dateTime;
  740.         $dateTimeStart->setTime(0000);
  741.         $dateTimeEnd = clone $dateTimeStart;
  742.         $dateTimeEnd->modify('+1 days');
  743.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  744.         $subQb $this->orderRepository->createQueryBuilder('o2')
  745.             ->select('MAX(s2.id)')
  746.             ->leftJoin('o2.Shippings''s2')
  747.             ->where('s2.shipping_date IS NOT NULL')
  748.             ->andWhere('o2.id = o.id')
  749.             ->orderBy('s2.shipping_date''DESC')
  750.             ->setMaxResults(1);
  751.         $subQueryDQL $subQb->getDQL();
  752.         $qb $this->orderRepository
  753.             ->createQueryBuilder('o')
  754.             ->select('
  755.       SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  756.       COUNT(DISTINCT o.id) AS order_count')
  757.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  758.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  759.             ->andWhere('s.shipping_date IS NOT NULL')
  760.             ->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
  761.             ->setParameter(':targetStatus'$this->targetStatus)
  762.             ->setParameter(':targetDateStart'$dateTimeStart)
  763.             ->setParameter(':targetDateEnd'$dateTimeEnd)
  764.             ->orderBy('s.shipping_date');
  765.         $q $qb->getQuery();
  766.         $result = [];
  767.         try {
  768.             $result $q->getSingleResult();
  769.         } catch (NoResultException $e) {
  770.             // 結果がない場合は空の配列を返す.
  771.         }
  772.         return $result;
  773.     }
  774.     /**
  775.      * @param \DateTime $dateTime
  776.      *
  777.      * @return array|mixed
  778.      *
  779.      * @throws \Doctrine\ORM\NonUniqueResultException
  780.      */
  781.     protected function getSalesByMonth($dateTime)
  782.     {
  783.         $dateTimeStart = clone $dateTime;
  784.         $dateTimeStart->setTime(0000);
  785.         $dateTimeStart->modify('first day of this month');
  786.         $dateTimeEnd = clone $dateTime;
  787.         $dateTimeEnd->setTime(0000);
  788.         $dateTimeEnd->modify('first day of 1 month');
  789.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  790.         $subQb $this->orderRepository->createQueryBuilder('o2')
  791.             ->select('MAX(s2.id)')
  792.             ->leftJoin('o2.Shippings''s2')
  793.             ->where('s2.shipping_date IS NOT NULL')
  794.             ->andWhere('o2.id = o.id')
  795.             ->orderBy('s2.shipping_date''DESC')
  796.             ->setMaxResults(1);
  797.         $subQueryDQL $subQb->getDQL();
  798.         // メインクエリ
  799.         $qb $this->orderRepository->createQueryBuilder('o')
  800.             ->select('
  801.         SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  802.         COUNT(o) AS order_count')
  803.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  804.             ->setParameter('targetStatus'$this->targetStatus)
  805.             ->setParameter('targetDateStart'$dateTimeStart)
  806.             ->setParameter('targetDateEnd'$dateTimeEnd)
  807.             ->andWhere('s.shipping_date IS NOT NULL')
  808.             ->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
  809.             ->andWhere('o.OrderStatus IN (:targetStatus)');
  810.         $q $qb->getQuery();
  811.         $result = [];
  812.         try {
  813.             $result $q->getSingleResult();
  814.         } catch (NoResultException $e) {
  815.             // 結果がない場合は空の配列を返す.
  816.         }
  817.         return $result;
  818.     }
  819.     /**
  820.      * カスタマイズ 新規作成
  821.      * 今週の売上とその件数を取得
  822.      *
  823.      * @param \DateTime $dateTime
  824.      * @return array|mixed
  825.      * @throws \Doctrine\ORM\NonUniqueResultException
  826.      */
  827.     protected function getSalesByWeek($dateTime)
  828.     {
  829.         $dateTimeStart = clone $dateTime;
  830.         $dateTimeStart->setTime(0000);
  831.         $dateTimeStart->modify('this week'); // 今週の開始日(通常は月曜日)
  832.         $dateTimeEnd = clone $dateTimeStart;
  833.         $dateTimeEnd->modify('+1 week'); // 次の週の開始日(今週の終了日の次の日)
  834.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  835.         $subQb $this->orderRepository->createQueryBuilder('o2')
  836.             ->select('MAX(s2.id)')
  837.             ->leftJoin('o2.Shippings''s2')
  838.             ->where('s2.shipping_date IS NOT NULL')
  839.             ->andWhere('o2.id = o.id')
  840.             ->orderBy('s2.shipping_date''DESC')
  841.             ->setMaxResults(1);
  842.         $subQueryDQL $subQb->getDQL();
  843.         // メインクエリ
  844.         $qb $this->orderRepository->createQueryBuilder('o')
  845.             ->select('
  846.      SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  847.      COUNT(o) AS order_count')
  848.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  849.             ->setParameter('targetStatus'$this->targetStatus)
  850.             ->setParameter('targetDateStart'$dateTimeStart)
  851.             ->setParameter('targetDateEnd'$dateTimeEnd)
  852.             ->andWhere('s.shipping_date IS NOT NULL')
  853.             ->andWhere(':targetDateStart <= s.shipping_date AND s.shipping_date < :targetDateEnd')
  854.             ->andWhere('o.OrderStatus IN (:targetStatus)');
  855.         $q $qb->getQuery();
  856.         $result = [];
  857.         try {
  858.             $result $q->getSingleResult();
  859.         } catch (NoResultException $e) {
  860.             // 結果がない場合は空の配列を返す
  861.         }
  862.         return $result;
  863.     }
  864.     /**
  865.      * カスタマイズ 新規作成
  866.      * 今年の売上とその件数を取得
  867.      *
  868.      * @return array|mixed
  869.      * @throws \Doctrine\ORM\NonUniqueResultException
  870.      */
  871.     protected function getSalesByYear()
  872.     {
  873.         $now = new \DateTime();
  874.         $yearStart Carbon::createFromDate($now->format('Y'), 11)->startOfDay(); // 今年の1月1日
  875.         $yearEnd Carbon::createFromDate($now->format('Y'), 1231)->endOfDay(); // 今年の12月31日
  876.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  877.         $subQb $this->orderRepository->createQueryBuilder('o2')
  878.             ->select('MAX(s2.id)')
  879.             ->leftJoin('o2.Shippings''s2')
  880.             ->where('s2.shipping_date IS NOT NULL')
  881.             ->andWhere('o2.id = o.id')
  882.             ->orderBy('s2.shipping_date''DESC')
  883.             ->setMaxResults(1);
  884.         $subQueryDQL $subQb->getDQL();
  885.         // メインクエリ
  886.         $qb $this->orderRepository->createQueryBuilder('o')
  887.             ->select('
  888.        SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  889.        COUNT(o) AS order_count')
  890.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  891.             ->andWhere('s.shipping_date IS NOT NULL')
  892.             ->andWhere('s.shipping_date >= :yearStart AND s.shipping_date <= :yearEnd')
  893.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  894.             ->setParameter('yearStart'$yearStart)
  895.             ->setParameter('yearEnd'$yearEnd)
  896.             ->setParameter('targetStatus'$this->targetStatus);
  897.         $q $qb->getQuery();
  898.         $result = [];
  899.         try {
  900.             $result $q->getSingleResult();
  901.         } catch (NoResultException $e) {
  902.             // 結果がない場合は空の配列を返す
  903.         }
  904.         return $result;
  905.     }
  906.     /**
  907.      * カスタマイズ 新規作成
  908.      * 前月の売上金額と売上件数を取得
  909.      *
  910.      * @return array|mixed
  911.      * @throws \Doctrine\ORM\NonUniqueResultException
  912.      */
  913.     protected function getSalesByLastMonth()
  914.     {
  915.         $lastMonthStart = new Carbon('first day of last month midnight'); // 前月の初日
  916.         $lastMonthEnd = new Carbon('last day of last month 23:59:59'); // 前月の最終日
  917.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  918.         $subQb $this->orderRepository->createQueryBuilder('o2')
  919.             ->select('MAX(s2.id)')
  920.             ->leftJoin('o2.Shippings''s2')
  921.             ->andWhere('s2.shipping_date IS NOT NULL')
  922.             ->andWhere('o2.id = o.id')
  923.             ->orderBy('s2.shipping_date''DESC')
  924.             ->setMaxResults(1);
  925.         $subQueryDQL $subQb->getDQL();
  926.         // メインクエリ
  927.         $qb $this->orderRepository
  928.             ->createQueryBuilder('o')
  929.             ->select('
  930.         SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  931.         COUNT(o) AS order_count')
  932.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  933.             ->andWhere('s.shipping_date >= :lastMonthStart AND s.shipping_date <= :lastMonthEnd')
  934.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  935.             ->setParameters([
  936.                 'lastMonthStart' => $lastMonthStart,
  937.                 'lastMonthEnd' => $lastMonthEnd,
  938.                 'targetStatus' => $this->targetStatus,
  939.             ]);
  940.         $q $qb->getQuery();
  941.         $result = [];
  942.         try {
  943.             $result $q->getSingleResult();
  944.         } catch (NoResultException $e) {
  945.             // 結果がない場合は空の配列を返す
  946.         }
  947.         return $result;
  948.     }
  949.     /**
  950.      * カスタマイズ 新規作成
  951.      * 前年同月の売上金額と売上件数を取得
  952.      *
  953.      * @return array|mixed
  954.      * @throws \Doctrine\ORM\NonUniqueResultException
  955.      */
  956.     protected function getSalesBySameMonthLastYear()
  957.     {
  958.         // 前年同月の初日
  959.         $sameMonthLastYearStart = new Carbon('first day of last year this month midnight');
  960.         // 前年同月の最終日
  961.         $sameMonthLastYearEnd = new Carbon('last day of last year this month 23:59:59');
  962.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  963.         $subQb $this->orderRepository->createQueryBuilder('o2')
  964.             ->select('MAX(s2.id)')
  965.             ->leftJoin('o2.Shippings''s2')
  966.             ->andWhere('s2.shipping_date IS NOT NULL')
  967.             ->andWhere('o2.id = o.id')
  968.             ->orderBy('s2.shipping_date''DESC')
  969.             ->setMaxResults(1);
  970.         $subQueryDQL $subQb->getDQL();
  971.         $qb $this->orderRepository
  972.             ->createQueryBuilder('o')
  973.             ->select('
  974.         SUM(o.payment_total - o.delivery_fee_total) AS order_amount,
  975.         COUNT(o) AS order_count')
  976.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  977.             ->andWhere('s.shipping_date >= :sameMonthLastYearStart AND s.shipping_date <= :sameMonthLastYearEnd')
  978.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  979.             ->setParameters([
  980.                 'sameMonthLastYearStart' => $sameMonthLastYearStart,
  981.                 'sameMonthLastYearEnd' => $sameMonthLastYearEnd,
  982.                 'targetStatus' => $this->targetStatus,
  983.             ]);
  984.         $q $qb->getQuery();
  985.         $result = [];
  986.         try {
  987.             $result $q->getSingleResult();
  988.         } catch (NoResultException $e) {
  989.             // 結果がない場合は空の配列を返す
  990.         }
  991.         return $result;
  992.     }
  993.     /**
  994.      * カスタマイズ 新規作成
  995.      * 累計の売上金額、平均単価、および売上件数を取得
  996.      *
  997.      * @return array
  998.      * @throws \Doctrine\ORM\NonUniqueResultException
  999.      */
  1000.     protected function getTotalSalesAndAveragePrice()
  1001.     {
  1002.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  1003.         $subQb $this->orderRepository->createQueryBuilder('o2')
  1004.             ->select('MAX(s2.id)')
  1005.             ->leftJoin('o2.Shippings''s2')
  1006.             ->where('s2.shipping_date IS NOT NULL')
  1007.             ->andWhere('o2.id = o.id')
  1008.             ->orderBy('s2.shipping_date''DESC')
  1009.             ->setMaxResults(1);
  1010.         $subQueryDQL $subQb->getDQL();
  1011.         $qb $this->orderRepository
  1012.             ->createQueryBuilder('o')
  1013.             ->select('
  1014.    SUM(o.payment_total - o.delivery_fee_total) AS total_amount,
  1015.    COUNT(o) AS total_count')
  1016.             ->leftJoin('o.Shippings''s''WITH''s.id IN (' $subQueryDQL ')')
  1017.             ->andWhere('s.shipping_date IS NOT NULL')
  1018.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  1019.             ->setParameter('targetStatus'$this->targetStatus);
  1020.         $q $qb->getQuery();
  1021.         try {
  1022.             $result $q->getSingleResult();
  1023.         } catch (NoResultException $e) {
  1024.             // 結果がない場合は空の配列を返す
  1025.             return [
  1026.                 'total_amount' => 0,
  1027.                 'total_count' => 0,
  1028.                 'average_price' => 0,
  1029.             ];
  1030.         }
  1031.         // 平均単価を計算(売上件数が0の場合は平均単価も0)
  1032.         $averagePrice $result['total_count'] > $result['total_amount'] / $result['total_count'] : 0;
  1033.         return [
  1034.             'total_amount' => $result['total_amount'],
  1035.             'total_count' => $result['total_count'],
  1036.             'average_price' => $averagePrice,
  1037.         ];
  1038.     }
  1039.     /**
  1040.      * 在庫切れ商品数を取得
  1041.      *
  1042.      * @return mixed
  1043.      *
  1044.      * @throws \Doctrine\ORM\NonUniqueResultException
  1045.      */
  1046.     protected function countNonStockProducts()
  1047.     {
  1048.         $qb $this->productRepository->createQueryBuilder('p')
  1049.             ->select('count(DISTINCT p.id)')
  1050.             ->innerJoin('p.ProductClasses''pc')
  1051.             ->where('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
  1052.             ->andWhere('pc.visible = :visible')
  1053.             ->setParameter('StockUnlimited'false)
  1054.             ->setParameter('visible'true);
  1055.         return $qb->getQuery()->getSingleScalarResult();
  1056.     }
  1057.     /**
  1058.      * 商品数を取得
  1059.      *
  1060.      * @return mixed
  1061.      *
  1062.      * @throws \Doctrine\ORM\NonUniqueResultException
  1063.      */
  1064.     protected function countProducts()
  1065.     {
  1066.         $qb $this->productRepository->createQueryBuilder('p')
  1067.             ->select('count(p.id)')
  1068.             ->where('p.Status in (:Status)')
  1069.             ->setParameter('Status', [ProductStatus::DISPLAY_SHOWProductStatus::DISPLAY_HIDE]);
  1070.         return $qb->getQuery()->getSingleScalarResult();
  1071.     }
  1072.     /**
  1073.      * 本会員数を取得
  1074.      *
  1075.      * @return mixed
  1076.      *
  1077.      * @throws \Doctrine\ORM\NonUniqueResultException
  1078.      */
  1079.     protected function countCustomers()
  1080.     {
  1081.         $qb $this->customerRepository->createQueryBuilder('c')
  1082.             ->select('count(c.id)')
  1083.             ->where('c.Status = :Status')
  1084.             ->setParameter('Status'CustomerStatus::REGULAR);
  1085.         return $qb->getQuery()->getSingleScalarResult();
  1086.     }
  1087.     /**
  1088.      * 期間指定のデータを取得
  1089.      *
  1090.      * @param Carbon $fromDate
  1091.      * @param Carbon $toDate
  1092.      * @param $format
  1093.      *
  1094.      * @return array
  1095.      */
  1096.     protected function getData(Carbon $fromDateCarbon $toDate$format)
  1097.     {
  1098.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  1099.         $subQb $this->orderRepository->createQueryBuilder('o2')
  1100.             ->select('MAX(s2.shipping_date)')
  1101.             ->leftJoin('o2.Shippings''s2')
  1102.             ->where('s2.shipping_date IS NOT NULL')
  1103.             ->andWhere('o2.id = o.id')
  1104.             ->orderBy('s2.shipping_date''DESC')
  1105.             ->setMaxResults(1);
  1106.         $subQueryDQL $subQb->getDQL();
  1107.         $qb $this->orderRepository->createQueryBuilder('o')
  1108.             ->select('DISTINCT o')
  1109.             // ->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
  1110.             // ->select('SUM(o.payment_total - o.delivery_fee_total) AS order_amount, COUNT(DISTINCT o) AS order_count')
  1111.             ->leftJoin('o.Shippings''s''WITH''s.shipping_date = (' $subQueryDQL ')')
  1112.             // ->andWhere('o.order_date >= :fromDate')
  1113.             // ->andWhere('o.order_date <= :toDate')
  1114.             ->andWhere('s.shipping_date >= :fromDate AND s.shipping_date <= :toDate')
  1115.             ->andWhere('s.shipping_date IS NOT NULL')
  1116.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  1117.             ->setParameter(':targetStatus'$this->targetStatus)
  1118.             ->setParameter(':fromDate'$fromDate->copy())
  1119.             ->setParameter(':toDate'$toDate->copy())
  1120.             ->groupBy('o.id');
  1121.         $result $qb->getQuery()->getResult();
  1122.         return $this->convert($result$fromDate$toDate$format);
  1123.     }
  1124.     /**
  1125.      * 期間指定のデータを取得
  1126.      *
  1127.      * @param Carbon $fromDate
  1128.      * @param Carbon $toDate
  1129.      * @param $format
  1130.      *
  1131.      * @return array
  1132.      */
  1133.     protected function getAgencyCommissionDataByPeriod(Carbon $fromDateCarbon $toDate$format)
  1134.     {
  1135.         // $qb = $this->orderRepository->createQueryBuilder('o')
  1136.         //     ->andWhere('o.order_date >= :fromDate')
  1137.         //     ->andWhere('o.order_date <= :toDate')
  1138.         //     ->andWhere('o.OrderStatus IN (:targetStatus)')
  1139.         //     ->setParameter(':targetStatus', $this->targetStatus)
  1140.         //     ->setParameter(':fromDate', $fromDate->copy())
  1141.         //     ->setParameter(':toDate', $toDate->copy())
  1142.         //     ->orderBy('o.order_date');
  1143.         // サブクエリを使ってshippingsの中からshipping_dateがNULLではない最後のshippingのIDを取得
  1144.         $subQb $this->orderRepository->createQueryBuilder('o2')
  1145.             ->select('MAX(s2.shipping_date)')
  1146.             ->leftJoin('o2.Shippings''s2')
  1147.             ->where('s2.shipping_date IS NOT NULL')
  1148.             ->andWhere('o2.id = o.id')
  1149.             ->orderBy('s2.shipping_date''DESC')
  1150.             ->setMaxResults(1);
  1151.         $subQueryDQL $subQb->getDQL();
  1152.         $qb $this->orderRepository->createQueryBuilder('o')
  1153.             ->select('DISTINCT o')
  1154.             // ->leftJoin('o.Shippings', 's', 'WITH', 's.id IN (' . $subQueryDQL . ')')
  1155.             ->leftJoin('o.Shippings''s''WITH''s.shipping_date = (' $subQueryDQL ')')
  1156.             // ->andWhere('o.order_date >= :fromDate')
  1157.             // ->andWhere('o.order_date <= :toDate')
  1158.             ->andWhere('s.shipping_date >= :fromDate AND s.shipping_date <= :toDate')
  1159.             ->andWhere('s.shipping_date IS NOT NULL')
  1160.             ->andWhere('o.OrderStatus IN (:targetStatus)')
  1161.             ->setParameter(':targetStatus'$this->targetStatus)
  1162.             ->setParameter(':fromDate'$fromDate->copy())
  1163.             ->setParameter(':toDate'$toDate->copy())
  1164.             ->groupBy('o.id');
  1165.         $result $qb->getQuery()->getResult();
  1166.         return $this->convertAgencyCommission($result$fromDate$toDate$format);
  1167.     }
  1168.     /**
  1169.      * 期間毎にデータをまとめる
  1170.      *
  1171.      * @param $result
  1172.      * @param Carbon $fromDate
  1173.      * @param Carbon $toDate
  1174.      * @param $format
  1175.      *
  1176.      * @return array
  1177.      */
  1178.     protected function convert($resultCarbon $fromDateCarbon $toDate$format)
  1179.     {
  1180.         $raw = [];
  1181.         for ($date $fromDate$date <= $toDate$date $date->addDay()) {
  1182.             $raw[$date->format($format)]['price'] = 0;
  1183.             $raw[$date->format($format)]['count'] = 0;
  1184.         }
  1185.         foreach ($result as $Order) {
  1186.             $shippings $Order->getShippings()->toArray();
  1187.             // shipping_dateがnullでないshippingsのみを抽出
  1188.             $validShippings array_filter($shippings, function ($shipping) {
  1189.                 return $shipping->getShippingDate() !== null;
  1190.             });
  1191.             // shipping_dateで降順にソート
  1192.             usort($validShippings, function ($a$b) {
  1193.                 return $b->getShippingDate() <=> $a->getShippingDate();
  1194.             });
  1195.             // 最初のshippingのshipping_dateを取得
  1196.             $orderDate reset($validShippings)->getShippingDate()->format($format);
  1197.             if (!isset($raw[$orderDate]['price'])) {
  1198.                 continue; // もしくはエラー処理を行うなど適切な処理を行う
  1199.             }
  1200.             $raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getPaymentTotal();
  1201.             ++$raw[$Order->getOrderDate()->format($format)]['count'];
  1202.         }
  1203.         return $raw;
  1204.     }
  1205.     /**
  1206.      * カスタマイズ 新規作成
  1207.      * 期間毎に代理店手数料データをまとめるためのカスタマイズされたconvert関数
  1208.      *
  1209.      * @param $result
  1210.      * @param Carbon $fromDate
  1211.      * @param Carbon $toDate
  1212.      * @param $format
  1213.      *
  1214.      * @return array
  1215.      */
  1216.     protected function convertAgencyCommission($resultCarbon $fromDateCarbon $toDate$format)
  1217.     {
  1218.         $raw = [];
  1219.         for ($date $fromDate$date <= $toDate$date $date->addDay()) {
  1220.             $raw[$date->format($format)]['price'] = 0;
  1221.             $raw[$date->format($format)]['count'] = 0;
  1222.         }
  1223.         foreach ($result as $Order) {
  1224.             $shippings $Order->getShippings()->toArray();
  1225.             // shipping_dateがnullでないshippingsのみを抽出
  1226.             $validShippings array_filter($shippings, function ($shipping) {
  1227.                 return $shipping->getShippingDate() !== null;
  1228.             });
  1229.             // shipping_dateで降順にソート
  1230.             usort($validShippings, function ($a$b) {
  1231.                 return $b->getShippingDate() <=> $a->getShippingDate();
  1232.             });
  1233.             // 最初のshippingのshipping_dateを取得
  1234.             $orderDate reset($validShippings)->getShippingDate()->format($format);
  1235.             // orderDateが期間内でない場合はスキップ
  1236.             $targetFromDate $fromDate->format($format);
  1237.             $targetToDate $toDate->format($format);
  1238.             error_log($orderDate);
  1239.             error_log($targetFromDate);
  1240.             error_log($targetToDate);
  1241.             if (!isset($raw[$orderDate]['price'])) {
  1242.                 continue; // もしくはエラー処理を行うなど適切な処理を行う
  1243.             }
  1244.             
  1245.             $raw[$Order->getOrderDate()->format($format)]['price'] += $Order->getAgencyCommissionAmount();
  1246.             ++$raw[$Order->getOrderDate()->format($format)]['count'];
  1247.         }
  1248.         return $raw;
  1249.     }
  1250. }