src/Service/EventsService.php line 381

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\Adverts;
  4. use App\Entity\Debit;
  5. use App\EntityOtpusk\Events;
  6. use App\Exception\EventsException;
  7. use App\Repository\AgencyRepository;
  8. use App\Repository\BlackListRepository;
  9. use App\Repository\DebitRepository;
  10. use App\Repository\EventsRepository;
  11. use App\Repository\TourRepository;
  12. use App\Repository\AdvertsRepository;
  13. use App\Repository\ManagerLogsRepository;
  14. use App\Service\Ipv6Service;
  15. use ClickHouseDB\Exception\ClickHouseException;
  16. use Doctrine\ORM\EntityManagerInterface;
  17. use Psr\Log\LoggerInterface;
  18. use Symfony\Component\Cache\Adapter\MemcachedAdapter;
  19. use Symfony\Component\HttpClient\HttpClient;
  20. use Symfony\Component\HttpKernel\KernelInterface;
  21. use Symfony\Contracts\HttpClient\HttpClientInterface;
  22. use Symfony\Component\HttpFoundation\RequestStack;
  23. /**
  24.  * Class EventsService
  25.  * @package App\Service
  26.  */
  27. class EventsService
  28. {
  29.     /**
  30.      * @var EntityManagerInterface
  31.      */
  32.     private $em;
  33.     /**
  34.      * @var TourRepository
  35.      */
  36.     private $tourRepository;
  37.     /**
  38.      * @var EventsRepository
  39.      */
  40.     private $eventsRepository;
  41.     /**
  42.      * @var KernelInterface
  43.      */
  44.     private $kernelInterface;
  45.     /**
  46.      * @var DebitRepository
  47.      */
  48.     private $debitRepository;
  49.     /**
  50.      * @var AgencyRepository
  51.      */
  52.     private $agencyRepository;
  53.     /**
  54.      * @var NotificationService
  55.      */
  56.     private $notificationService;
  57.     /**
  58.      * @var ManagerLogsRepository
  59.      */
  60.     private $managerLogsRepository;
  61.     /**
  62.      * @var AdvertsRepository
  63.      */
  64.     private $advertsRepository;
  65.     /**
  66.      * @var HttpClientInterface
  67.      */
  68.     private $client;
  69.     /**
  70.      * @var LoggerInterface
  71.      */
  72.     private $logger;
  73.     /**
  74.      * @var \App\Repository\BlackListRepository
  75.      */
  76.     private BlackListRepository $blackListRepository;
  77.     /**
  78.      * @var RequestStack
  79.      */
  80.     private $requestStack;
  81.     /**
  82.      * EventsService constructor.
  83.      *
  84.      * @param EntityManagerInterface $em
  85.      * @param TourRepository $tourRepository
  86.      * @param EventsRepository $eventsRepository
  87.      * @param BlackListRepository $blackListRepository
  88.      * @param DebitRepository $debitRepository
  89.      * @param AgencyRepository $agencyRepository
  90.      * @param NotificationService $notificationService
  91.      * @param ManagerLogsRepository $managerLogsRepository
  92.      * @param AdvertsRepository $advertsRepository
  93.      * @param KernelInterface $kernelInterface
  94.      * @param LoggerInterface $logger
  95.      * @param HttpClientInterface $client
  96.      * @param Request $request
  97.      */
  98.     public function __construct(
  99.         EntityManagerInterface $em,
  100.         TourRepository $tourRepository,
  101.         EventsRepository $eventsRepository,
  102.         BlackListRepository $blackListRepository,
  103.         DebitRepository $debitRepository,
  104.         AgencyRepository $agencyRepository,
  105.         NotificationService $notificationService,
  106.         ManagerLogsRepository $managerLogsRepository,
  107.         AdvertsRepository $advertsRepository,
  108.         KernelInterface $kernelInterface,
  109.         HttpClientInterface $client,
  110.         LoggerInterface $logger,
  111.         RequestStack $requestStack
  112.     ) {
  113.         $this->em                    = $em;
  114.         $this->tourRepository        = $tourRepository;
  115.         $this->eventsRepository      = $eventsRepository;
  116.         $this->blackListRepository   = $blackListRepository;
  117.         $this->kernelInterface       = $kernelInterface;
  118.         $this->debitRepository       = $debitRepository;
  119.         $this->agencyRepository      = $agencyRepository;
  120.         $this->managerLogsRepository = $managerLogsRepository;
  121.         $this->advertsRepository     = $advertsRepository;
  122.         $this->notificationService   = $notificationService;
  123.         $this->client                = $client;
  124.         $this->logger                = $logger;
  125.         $this->requestStack          = $requestStack;
  126.     }
  127.     public function getEvents(Events $event)
  128.     {
  129.         $this->eventsRepository->_connect();
  130.     }
  131.     public function saveEvent(Events $event, $getEvent=false)
  132.     {
  133.         $tourInfo = $this->tourRepository->getTourInfo(
  134.             $event->getOfferId(),
  135.             $event->getOperatorId(),
  136.             $event->getCruiseId()
  137.         );
  138.         if (isset($_COOKIE['DM'])) {
  139.             header('X-tourInfo: '.json_encode([
  140.                 'offerId' => $tourInfo->getOfferId(),
  141.                 'operatorId' => $tourInfo->getOperatorId(),
  142.                 'countryId' => $tourInfo->getCountryId(),
  143.                 'hotelId' => $tourInfo->getHotelId(),
  144.                 'fromCityId' => $tourInfo->getFromCityId(),
  145.                 'checkIn' => $tourInfo->getCheckIn() ? $tourInfo->getCheckIn()->format('Y-m-d') : null,
  146.                 'nights' => $tourInfo->getNights(),
  147.                 'adult' => $tourInfo->getAdult(),
  148.                 'child' => $tourInfo->getChild(),
  149.                 'food' => $tourInfo->getFood(),
  150.                 'transport' => $tourInfo->getTransport(),
  151.                 'roomId' => $tourInfo->getRoomId(),
  152.                 'price' => $tourInfo->getPrice(),
  153.                 'currency' => $tourInfo->getCurrency(),
  154.             ]));
  155.         }
  156.         if ($tourInfo && $tourInfo->getOfferId()) {
  157.             $event->setOperatorId($tourInfo->getOperatorId());
  158.             $event->setCountryId($tourInfo->getCountryId());
  159.             $event->setHotelId($tourInfo->getHotelId());
  160.             $event->setFromCityId($tourInfo->getFromCityId());
  161.             $event->setCheckIn($tourInfo->getCheckIn());
  162.             $event->setNights($tourInfo->getNights());
  163.             if (empty($tourInfo->getChild())) {
  164.                 $event->setPeople($tourInfo->getAdult());
  165.             } else {
  166.                 if (empty($event->getPeople())) {
  167.                     $child = $tourInfo->getChild();
  168.                     if ($child == 1) {
  169.                         $child = '06';
  170.                     } elseif ($child == 2) {
  171.                         $child = '0608';
  172.                     } elseif ($child == 3) {
  173.                         $child = '060810';
  174.                     } elseif ($child == 4) {
  175.                         $child = '05070911';
  176.                     }
  177.                     $event->setPeople($tourInfo->getAdult().$child);
  178.                 }
  179.             }
  180.             $event->setFood($tourInfo->getFood());
  181.             $event->setTransport($tourInfo->getTransport());
  182.             $event->setRoomId($tourInfo->getRoomId());
  183.             if ($tourInfo->getCurrency() == 'uah') {
  184.                 $priceUah = $tourInfo->getPrice();
  185.             } else {
  186.                 $priceUah = $tourInfo->getPriceUah();
  187.                 if (empty($priceUah)) {
  188.                     $priceUah = $tourInfo->getPrice() * $tourInfo->getCurrencyRate();
  189.                 }
  190.             }
  191.             $event->setPrice($priceUah);
  192.             $event->setCurrency($tourInfo->getCurrency());
  193.         }
  194.         if (empty($event->getHotelId()) && !empty($event->getOfferId())) {
  195.             $httpClient = HttpClient::create();
  196.             $offerId    = $event->getOfferId();
  197.             $response   = $httpClient->request(
  198.                 'GET',
  199.                 'https://api.otpusk.com/api/2.5/tours/offer?offerId='.$offerId.'&access_token=4b26a-e5237-9b0fa-bcc36-67732',
  200.                 ['headers' => ['User-Agent' => 'pro.otpusk.com']]
  201.             );
  202.             if (200 == $response->getStatusCode()) {
  203.                 $offer = $response->toArray();
  204.                 $content = $offer['offer'];
  205.                 if (empty($event->getCountryId()) && isset($content['countryId']))
  206.                     $event->setCountryId($content['countryId']);
  207.                 if (empty($event->getHotelId()) && isset($offer['hotelId']))
  208.                     $event->setHotelId($offer['hotelId']);
  209.                 if (empty($event->getHotelId()) && isset($content['hotelId']))
  210.                     $event->setHotelId($content['hotelId']);
  211.                 if (empty($event->getFromCityId()) && isset($content['fromCity']))
  212.                     $event->setFromCityId($content['fromCity']);
  213.                 if (empty($event->getRoomId()) && isset($content['roomId']))
  214.                     $event->setRoomId($content['roomId']);
  215.                 if (empty($event->getPrice()) && isset($content['priceUah']))
  216.                     $event->setPrice($content['priceUah']);
  217.             }
  218.         }
  219.         $event->setEventPrice(0);
  220.         // Ищем объявление
  221.         $advertsRepository = $this->em->getRepository(Adverts::class);
  222.         /** @var $advertInfo Adverts */
  223.         $advertInfo = $advertsRepository->find($event->getAdvertId());
  224.         $event->setEventPrice(0);
  225.         if ($advertInfo && $advertInfo->getAgency() &&
  226.             $advertInfo->getAgency()->getActive() > 0 &&
  227.             $advertInfo->getAgency()->getStatus() == 'premium')
  228.         {
  229.             $event->setAgencyId($advertInfo->getAgency()->getId());
  230.             $event->setEventPrice($advertInfo->getPrice() ?? 50);
  231.         } else {
  232.             $event->setEventPosition(22);
  233.         }
  234.         /*
  235.         if ($event->getEventPrice() > 0) {
  236.             if (!$tourInfo || !$tourInfo->getOfferId()) {
  237.                 // если не найден offer
  238.                 $event->setEventPrice(0);
  239.                 $event->setEventPosition(24);
  240.             }
  241.         }
  242.         */
  243.         // fixme: нужно как-то проверять относится ли officeId и advertId к одному агентству
  244.         if ($event->getEventPrice() > 0) {
  245.             try {
  246.                 $ip = $this->blackListRepository->getByIp(intval($event->getIp()));
  247.                 if ($ip) {
  248.                     // Ip в черном списке
  249.                     $event->setEventPrice(0);
  250.                     $event->setEventPosition(31);
  251.                 }
  252.             } catch (ClickHouseException $e) {
  253.                 $events = 'error: '.$e->getMessage();
  254.             }
  255.             if (isset($_COOKIE['DM'])) {
  256.                 header('X-31-blacklist: '.$ip);
  257.             }
  258.         }
  259.         if ($event->getEventPrice() > 0) {
  260.             // проверяем был ли уже такое событие сегодня у этого объявления
  261.             $option    = array(
  262.                 \Memcached::OPT_PREFIX_KEY => (getenv('MEMCACHED_PREFIX') ? getenv(
  263.                     'MEMCACHED_PREFIX'
  264.                 ) : ($_ENV['MEMCACHED_PREFIX'] ?? '')),
  265.             );
  266.             $client    = MemcachedAdapter::createConnection(
  267.                 getenv('MEMCACHED_SERV') ? getenv('MEMCACHED_SERV') : $_ENV['MEMCACHED_SERV'],
  268.                 $option
  269.             );
  270.             $cache_key = 'event_'.$event->getEventDate()->format('Ymd').'_'.$event->getAdvertId().'_'.$event->getIp();
  271.             if (isset($_COOKIE['DM'])) {
  272.                 $MEMCACHED_PREFIX = (getenv('MEMCACHED_PREFIX') ? getenv(
  273.                     'MEMCACHED_PREFIX'
  274.                 ) : ($_ENV['MEMCACHED_PREFIX'] ?? ''));
  275.                 $MEMCACHED_SERV   = (getenv('MEMCACHED_SERV') ? getenv('MEMCACHED_SERV') : $_ENV['MEMCACHED_SERV']);
  276.                 $client->set('test_'.$cache_key, time(), 24 * 60 * 60);
  277.                 header(
  278.                     'X-26-memTest: '.$MEMCACHED_SERV.'@'.$MEMCACHED_PREFIX.'&'.$cache_key.'='.$client->get(
  279.                         'test_'.$cache_key
  280.                     )
  281.                 );
  282.             }
  283.             if ($event_time = $client->get($cache_key)) {
  284.                 // уникальное событие, если сегодня уже было
  285.                 $event->setEventPrice(0);
  286.                 $event->setEventPosition(26);
  287.             } else {
  288.                 $client->set($cache_key, time(), time() + (24 * 60 * 60));
  289.             }
  290.         }
  291.         if ($event->getEventPrice() > 0) {
  292.             try {
  293.                 $log = $this->managerLogsRepository->findOneBy(['ip' => $event->getIp()]);
  294.                 if ($log && $log->getIp2Long() == $event->getIp()) {
  295.                     // игнорируем если это IP менеджера любого из агентства
  296.                     $event->setEventPrice(0);
  297.                     $event->setEventPosition(27);
  298.                 }
  299.             } catch (Exception $e) {
  300.             }
  301.             if (isset($_COOKIE['DM'])) {
  302.                 header('X-27-isManager: '.$event->getIp().'='.($log ? $log->getIp2Long() : -1));
  303.             }
  304.         }
  305.         if ($event->getEventPrice() > 0) {
  306.             if ($advertInfo->getDaybudget() > 0) {
  307.                 try {
  308.                     $dayCost = $this->debitRepository->findCostsForCurrentDay($advertInfo->getAgency()->getId());
  309.                     if ($dayCost > 0 && $dayCost >= $advertInfo->getDaybudget()) {
  310.                         // если превысили дневной бюджет
  311.                         $event->setEventPrice(0);
  312.                         $event->setEventPosition(28);
  313.                     }
  314.                 } catch (Exception $e) {
  315.                     $dayCost = 'error: '.$e->getMessage();
  316.                 }
  317.                 if (isset($_COOKIE['DM'])) {
  318.                     header('X-28-dayLimit: '.$dayCost.'>='.$advertInfo->getDaybudget());
  319.                 }
  320.             }
  321.         }
  322.         if ($event->getEventPrice() > 0) {
  323.             try {
  324.                 $events = $this->eventsRepository->getTotalEventsToDayByIp(intval($event->getIp()));
  325.                 if ($events >= 3) {
  326.                     // Не более 3-х платных взаимодействий в сутки с одного IP в не зависимости от объявлений
  327.                     $event->setEventPrice(0);
  328.                     $event->setEventPosition(29);
  329.                 }
  330.             } catch (ClickHouseException $e) {
  331.                 $events = 'error: '.$e->getMessage();
  332.             }
  333.             if (isset($_COOKIE['DM'])) {
  334.                 header('X-29-events: '.$events.'>='. 3);
  335.             }
  336.         }
  337.         // TODO return if didnt help
  338.         /*
  339.         if ($event->getEventPrice() > 0) {
  340.             if (isset($_COOKIE['DM'])) header('X-30-IP: ' . $event->getIp() .' = '. Ipv6Service::long2ip($event->getIp()));
  341.             try {
  342.                 $countryIso = $this->eventsRepository->checkCountryIp($event->getIp());
  343.                 if ($countryIso && $countryIso != 'UA') {
  344.                     //Если ip не из Украины
  345.                     $event->setEventPrice(0);
  346.                     $event->setEventPosition(30);
  347.                 }
  348.             } catch (ClickHouseException $e) {
  349.                 $countryIso = 'error: '.$e->getMessage();
  350.             }
  351.             if (isset($_COOKIE['DM'])) header('X-30-CountryCode: ' . $countryIso);
  352.         }
  353.         */
  354.         try {
  355.             $this->eventsRepository->save($event);
  356.         } catch (ClickHouseException $e) {
  357.             if (isset($_COOKIE['DM'])) {
  358.                 header('X-88-error: '.$e->getMessage());
  359.             }
  360.         }
  361.         if (false !== stripos($getEvent??$event->getEventTypeName(), 'showFormAd')) {
  362.             // для собитий show ціни немає
  363.             $event->setEventPrice(0);
  364.             $event->setEventPosition(32);
  365.         }
  366.         return $event->getEventPrice();
  367.     }
  368.     public function sendGA($clientId=false, $sessionId=false, Events $event, $getEvent=false)
  369.     {
  370.         $APP_ENV = (getenv('APP_ENV') ? getenv('APP_ENV') : $_ENV['APP_ENV']);
  371.         if ($APP_ENV != 'prod') {
  372.             $this->logger->info('Error Send GA env: '.$APP_ENV);
  373.             return false;
  374.         }
  375.         $account_id = (getenv('GA_ACCOUNT') ? getenv('GA_ACCOUNT') : ($_ENV['GA_ACCOUNT'] ?? ''));
  376.         if (isset($_COOKIE['DM'])) {
  377.             header('X-ACCOUNT-ID: '.$account_id);
  378.             header('X-Event-Price: '.$event->getEventPrice());
  379.             header('X-client: '.$clientId);
  380.         }
  381.         if ($event->getEventPrice() <= 0) {
  382.             $this->logger->info('Error Send GA price: '.$event->getEventPrice());
  383.             return false;
  384.         }
  385.         if (empty($account_id)) {
  386.             $this->logger->info('Error Send GA account: '.$account_id);
  387.             return null;
  388.         }
  389.         $trans_id = date('d0m').'0'
  390.                     .sprintf("%u", $event->getIp()).'_'
  391.                     .$event->getAdvertId().'_'
  392.                     .($event->getCruiseId() > 0 ? '' : $event->getCountryId().'_')
  393.                     .$event->getRegionId().'_'
  394.                     .($event->getCruiseId() > 0 ? 'excursion' : 'package');
  395.         if (isset($_COOKIE['DM'])) {
  396.             header('X-TRANS-ID: '.$trans_id);
  397.         }
  398.         if (!empty($clientId)) {
  399.             /*
  400.             // send GA #33394
  401.             $response = $this->client->request(
  402.                 'POST',
  403.                 'http://www.google-analytics.com/collect',
  404.                 [
  405.                     'body' => [
  406.                         'v' => '1', //версия протокола, походу второй нет, только первая
  407.                         'tid' => $account_id, //ид гугл аналитики.
  408.                         'cid' => $clientId, //Client ID – будет отдавать фронт
  409.                         't' => 'transaction', //тип хита в этом случае
  410.                         'ti' => $trans_id, //ид транзакции
  411.                         'tr' => $event->getEventPrice() // сумма транзакции
  412.                     ],
  413.                     'timeout' => 5,
  414.                     'max_duration' => 5,
  415.                     'buffer' => false,
  416.                     'verify_peer' => false,
  417.                     'verify_host' => false,
  418.                     'headers' => [
  419.                         'User-Agent' => 'www.otpusk.com',
  420.                     ],
  421.                 ]
  422.             );
  423.             $this->logger->info('Send GA1 ' . $response->getInfo('url') . ' status: ' . $response->getStatusCode());
  424.             if (isset($_COOKIE['DM'])) {
  425.                 header('X-SEND-GA1: ' . $response->getStatusCode());
  426.             }
  427.             $response = $this->client->request(
  428.                 'POST',
  429.                 'http://www.google-analytics.com/collect',
  430.                 [
  431.                     'body' => [
  432.                         'v' => '1', //версия протокола, походу второй нет, только первая
  433.                         'tid' => $account_id, //ид гугл аналитики.
  434.                         'cid' => $clientId, //Client ID – будет отдавать фронт
  435.                         't' => 'item', //тип хита в этом случае
  436.                         'ti' => $trans_id, //ид транзакции
  437.                         'in' => $event->getAgencyId(), // Название турфирмы
  438.                         'ip' => $event->getPrice(), // стоимость товара
  439.                         'iv' => ($event->getCruiseId() > 0 ? 'Excursion' : 'Package'),
  440.                     ],
  441.                     'timeout' => 5,
  442.                     'max_duration' => 5,
  443.                     'buffer' => false,
  444.                     'verify_peer' => false,
  445.                     'verify_host' => false,
  446.                     'headers' => [
  447.                         'User-Agent' => 'www.otpusk.com',
  448.                     ],
  449.                 ]
  450.             );
  451.             $this->logger->info('Send GA2 ' . $response->getInfo('url') . ' status: ' . $response->getStatusCode());
  452.             if (isset($_COOKIE['DM'])) {
  453.                 header('X-SEND-GA2: ' . $response->getStatusCode());
  454.             }
  455.             */
  456.             $post = [
  457.                 'client_id' => $clientId,
  458.                 'non_personalized_ads' => false,
  459.                 'events' => [
  460.                     [
  461.                         //'name' => 'purchase',
  462.                         'name' => 'api'.ucfirst($getEvent??$event->getEventTypeName()), // 'purchase',
  463.                         //'timestamp_micros' => microtime(true) * 1000000,
  464.                         'params' => [
  465.                             'eventCategory' => 'offerPageAds',
  466.                             'type' => ($event->getCruiseId() > 0 ? 'Excursion' : 'Package'),
  467.                             'value' => (float)$event->getEventPrice(),
  468.                             'lang' => 'UK',
  469.                             'currency' => 'UAH',
  470.                             'client_id' => $clientId,
  471.                             'session_id' => $sessionId,
  472.                             'debug_mode' => true,
  473.                             /*
  474.                             'transaction_id' => $trans_id,
  475.                             //'agency_id' => $event->getAgencyId(),
  476.                             'items' => [
  477.                                 [
  478.                                     'item_id' => (string)($event->getAdvertId() . $event->getRegionId()),
  479.                                     'item_name' => 'id: ' . ((string)$event->getAdvertId() . $event->getRegionId()),
  480.                                     //'item_name' => $event->getEventType(),
  481.                                     'item_category' => ($event->getCruiseId() > 0 ? 'Excursion' : 'Package'),
  482.                                     //'item_brand' => $event->getAgencyId(),
  483.                                     'price' => (float)$event->getEventPrice(),
  484.                                     'currency' => 'UAH',
  485.                                     'quantity' => 1,
  486.                                 ]
  487.                             ],
  488.                             */
  489.                         ]
  490.                     ]
  491.                 ],
  492.                 //'validationBehavior' => 'ENFORCE_RECOMMENDATIONS'
  493.             ];
  494.             $userAgent = 'www.otpusk.com';
  495.             $currentRequest = $this->requestStack->getCurrentRequest();
  496.             if ($currentRequest) {
  497.                 $userAgent = $currentRequest->headers->get('User-Agent', 'www.otpusk.com');
  498.             }
  499.             $headers = [
  500.                 'User-Agent' => $userAgent,
  501.                 'Content-Type' => 'application/json',
  502.                 //'Accept' => 'application/json',
  503.             ];
  504.             $response = $this->client->request(
  505.                 'POST',
  506.                 //'https://www.google-analytics.com/debug/mp/collect?api_secret=' . urlencode('X7iB6sPfTWqTIvOwjwqV7Q') . '&measurement_id=' . urlencode('G-V52QLQJ1C6'),
  507.                 'https://www.google-analytics.com/mp/collect?api_secret=' . urlencode('X7iB6sPfTWqTIvOwjwqV7Q') . '&measurement_id=' . urlencode('G-V52QLQJ1C6'),
  508.                 [
  509.                     'body' => json_encode($post),
  510.                     'timeout' => 5,
  511.                     'max_duration' => 5,
  512.                     'buffer' => false,
  513.                     'verify_peer' => false,
  514.                     'verify_host' => false,
  515.                     'headers' => $headers,
  516.                 ]
  517.             );
  518.             $this->logger->info('Send GA4 ' . $response->getInfo('url') . ' status: ' . $response->getStatusCode());
  519.             if (isset($_COOKIE['DM'])) {
  520.                 header('X-SEND-GA4: ' . $response->getInfo('url') . ' ' . $response->getStatusCode() . ' ' . json_encode($post) . ' ' . json_encode($headers));
  521.             }
  522.         }
  523.         return true;
  524.     }
  525.     public function gelLast($limit = 20)
  526.     {
  527.         return $this->eventsRepository->findAll(10);
  528.     }
  529.     public function updateDebitsByEvents($date = null)
  530.     {
  531.         $startTime = microtime(true);
  532.         $startMemory = memory_get_usage(true);
  533.         
  534.         if ($date) {
  535.             try {
  536.                 $now = new \DateTime($date);
  537.                 $now = $now->format('Y-m-d');
  538.             } catch (\Exception $e) {
  539.                 echo 'Invalid date format. Use YYYY-MM-DD'.PHP_EOL;
  540.                 return;
  541.             }
  542.         } else {
  543.             $now    = new \DateTime('now');
  544.             $now    = $now->format('Y-m-d');
  545.         }
  546.         
  547.         echo PHP_EOL.'========== DEBITS BY EVENTS PROCESSING =========='.PHP_EOL;
  548.         echo 'Start time: '.date('Y-m-d H:i:s').PHP_EOL;
  549.         echo 'Processing date: '.$now.PHP_EOL.PHP_EOL;
  550.         
  551.         $events = $this->eventsRepository->findToDayEvents($now);
  552.         echo '[INFO] Found '.count($events).' events/agencies'.PHP_EOL;
  553.         
  554.         $totalProcessed = 0;
  555.         $totalCreated = 0;
  556.         $totalUpdated = 0;
  557.         $totalErrors = 0;
  558.         $negativeBalanceAgencies = 0;
  559.         
  560.         foreach ($events as $event) {
  561.             $data = false;
  562.             if ($event['agencyId']) {
  563.                 $agency = $this->agencyRepository->findOneBy(['id' => $event['agencyId'], 'status' => 'premium']);
  564.                 if ($agency) {
  565.                     $debit = $this->debitRepository->findByAgencyAndEvent($agency, $now);
  566.                     $isNew = !$debit;
  567.                     
  568.                     if ( ! $debit) {
  569.                         $debit = new Debit();
  570.                         $debit->setAgency($agency);
  571.                         $debit->setDateType(3);
  572.                         $debit->setType('event');
  573.                         $debit->setStatus('paid');
  574.                         $debit->setDate(new \DateTime($now));
  575.                         $debit->setCreatedAt(new \DateTime($now.' 12:00:00'));
  576.                         $totalCreated++;
  577.                     } else {
  578.                         $totalUpdated++;
  579.                     }
  580.                     
  581.                     $debit->setSum($event['eventPrices']);
  582.                     $this->em->persist($debit);
  583.                     $this->em->flush();
  584.                     $totalProcessed++;
  585.                     
  586.                     echo '  [OK] Agency #'.$event['agencyId'].' - Sum: '.$event['eventPrices'].' ('.($isNew ? 'CREATED' : 'UPDATED').')'.PHP_EOL;
  587.                     
  588.                     $data = $this->debitRepository->checkingNegativeBalance($event['agencyId']);
  589.                 } else {
  590.                     $totalErrors++;
  591.                     echo '  [ERROR] Agency #'.$event['agencyId'].' is not premium or not found'.PHP_EOL;
  592.                 }
  593.             }
  594.             
  595.             if ($data) {
  596.                 $negativeBalanceAgencies++;
  597.                 if ($agency && $agency->getStatus() == 'premium') {
  598.                     echo '  [WARNING] Agency #'.$event['agencyId'].' has NEGATIVE BALANCE: '.$data['balance'].PHP_EOL;
  599.                     $handle = curl_init();
  600.                     $url    = 'https://control.otpusk.com/api/setagencynopaid?agencyId='.$event['agencyId'].'&access_token=4b26a-e5237-9b0fa-bcc36-67732';
  601.                     curl_setopt($handle, CURLOPT_USERAGENT, 'pro.otpusk.com');
  602.                     curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
  603.                     curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
  604.                     curl_setopt($handle, CURLOPT_URL, $url);
  605.                     curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  606.                     curl_setopt($handle, CURLOPT_FAILONERROR, true);
  607.                     $curlResult = curl_exec($handle);
  608.                     $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
  609.                     echo '    [CURL] HTTP Code: '.$httpCode.PHP_EOL;
  610.                     echo 'http code: '.var_export(curl_getinfo($handle, CURLINFO_HTTP_CODE), 1).PHP_EOL;
  611.                     echo 'agencyId: '.var_export($event['agencyId'], 1).PHP_EOL;
  612.                     echo 'curl error: '.var_export(curl_error($handle), 1).PHP_EOL;
  613.                     curl_close($handle);
  614.                     if ($data['email']) {
  615.                         echo '    [EMAIL] Notification sent to: '.$data['email'].PHP_EOL;
  616.                         $this->notificationService->negativeBalance($data);
  617.                     }
  618.                 }
  619.             }
  620.         }
  621.         
  622.         $endTime = microtime(true);
  623.         $endMemory = memory_get_usage(true);
  624.         $duration = round($endTime - $startTime, 2);
  625.         $memoryUsed = round(($endMemory - $startMemory) / 1024 / 1024, 2);
  626.         
  627.         echo PHP_EOL.'========== PROCESSING SUMMARY =========='.PHP_EOL;
  628.         echo 'Date processed: '.$now.PHP_EOL;
  629.         echo 'Total events found: '.count($events).PHP_EOL;
  630.         echo 'Total debits processed: '.$totalProcessed.PHP_EOL;
  631.         echo '  - Created: '.$totalCreated.PHP_EOL;
  632.         echo '  - Updated: '.$totalUpdated.PHP_EOL;
  633.         echo '  - Errors: '.$totalErrors.PHP_EOL;
  634.         echo 'Agencies with negative balance: '.$negativeBalanceAgencies.PHP_EOL;
  635.         echo 'Execution time: '.$duration.' sec'.PHP_EOL;
  636.         echo 'Memory used: '.$memoryUsed.' MB'.PHP_EOL;
  637.         echo 'End time: '.date('Y-m-d H:i:s').PHP_EOL;
  638.         echo '=========================================='.PHP_EOL.PHP_EOL;
  639.     }
  640.     public function updateDebitsByEventsAgencies($date = null)
  641.     {
  642.         $startTime = microtime(true);
  643.         $startMemory = memory_get_usage(true);
  644.         
  645.         if ($date) {
  646.             try {
  647.                 $now = new \DateTime($date);
  648.                 $now = $now->format('Y-m-d');
  649.             } catch (\Exception $e) {
  650.                 echo 'Invalid date format. Use YYYY-MM-DD'.PHP_EOL;
  651.                 return;
  652.             }
  653.         } else {
  654.             $now      = new \DateTime('now');
  655.             $now      = $now->format('Y-m-d');
  656.         }
  657.         
  658.         echo PHP_EOL.'========== DEBITS BY EVENTS AGENCIES PROCESSING =========='.PHP_EOL;
  659.         echo 'Start time: '.date('Y-m-d H:i:s').PHP_EOL;
  660.         echo 'Processing date: '.$now.PHP_EOL.PHP_EOL;
  661.         
  662.         $agencies = $this->eventsRepository->findAgenciesByEvents();
  663.         $agencies = array_column($agencies, 'agencyId');
  664.         
  665.         echo '[INFO] Found '.count($agencies).' active agencies (last 1 min)'.PHP_EOL;
  666.         
  667.         $totalProcessed = 0;
  668.         $totalCreated = 0;
  669.         $totalUpdated = 0;
  670.         $totalErrors = 0;
  671.         $negativeBalanceAgencies = 0;
  672.         
  673.         if ( ! empty($agencies)) {
  674.             $events = $this->eventsRepository->findToDayEvents($now, $agencies);
  675.             echo '[INFO] Found '.count($events).' events for these agencies'.PHP_EOL.PHP_EOL;
  676.             
  677.             foreach ($events as $event) {
  678.                 $data = false;
  679.                 if ($event['agencyId']) {
  680.                     $agency = $this->agencyRepository->findOneBy(['id' => $event['agencyId'], 'status' => 'premium']);
  681.                     if ($agency) {
  682.                         $debit = $this->debitRepository->findByAgencyAndEvent($agency, $now);
  683.                         $isNew = !$debit;
  684.                         
  685.                         if ( ! $debit) {
  686.                             $debit = new Debit();
  687.                             $debit->setAgency($agency);
  688.                             $debit->setDateType(3);
  689.                             $debit->setType('event');
  690.                             $debit->setStatus('paid');
  691.                             $debit->setDate(new \DateTime($now));
  692.                             $debit->setCreatedAt(new \DateTime($now.' 12:00:00'));
  693.                             $totalCreated++;
  694.                         } else {
  695.                             $totalUpdated++;
  696.                         }
  697.                         
  698.                         $debit->setSum($event['eventPrices']);
  699.                         $this->em->persist($debit);
  700.                         $this->em->flush();
  701.                         $totalProcessed++;
  702.                         
  703.                         echo '  [OK] Agency #'.$event['agencyId'].' - Sum: '.$event['eventPrices'].' ('.($isNew ? 'CREATED' : 'UPDATED').')'.PHP_EOL;
  704.                         
  705.                         $data = $this->debitRepository->checkingNegativeBalance($event['agencyId']);
  706.                     } else {
  707.                         $totalErrors++;
  708.                         echo '  [ERROR] Agency #'.$event['agencyId'].' is not premium or not found'.PHP_EOL;
  709.                     }
  710.                 }
  711.                 
  712.                 if ($data) {
  713.                     $negativeBalanceAgencies++;
  714.                     if ($agency && $agency->getStatus() == 'premium') {
  715.                         echo '  [WARNING] Agency #'.$event['agencyId'].' has NEGATIVE BALANCE: '.$data['balance'].PHP_EOL;
  716.                         $handle = curl_init();
  717.                         $url    = 'https://control.otpusk.com/api/setagencynopaid?agencyId='.$event['agencyId'].'&access_token=4b26a-e5237-9b0fa-bcc36-67732';
  718.                         curl_setopt($handle, CURLOPT_USERAGENT, 'pro.otpusk.com');
  719.                         curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
  720.                         curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
  721.                         curl_setopt($handle, CURLOPT_URL, $url);
  722.                         curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  723.                         curl_setopt($handle, CURLOPT_FAILONERROR, true);
  724.                         $curlResult = curl_exec($handle);
  725.                         $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
  726.                         echo '    [CURL] HTTP Code: '.$httpCode.PHP_EOL;
  727.                         
  728.                         curl_close($handle);
  729.                         if ($data['email']) {
  730.                             echo '    [EMAIL] Notification sent to: '.$data['email'].PHP_EOL;
  731.                             $this->notificationService->negativeBalance($data);
  732.                         }
  733.                     }
  734.                 }
  735.             }
  736.         } else {
  737.             echo '[INFO] No active agencies found'.PHP_EOL;
  738.         }
  739.         
  740.         $endTime = microtime(true);
  741.         $endMemory = memory_get_usage(true);
  742.         $duration = round($endTime - $startTime, 2);
  743.         $memoryUsed = round(($endMemory - $startMemory) / 1024 / 1024, 2);
  744.         
  745.         echo PHP_EOL.'========== PROCESSING SUMMARY =========='.PHP_EOL;
  746.         echo 'Date processed: '.$now.PHP_EOL;
  747.         echo 'Active agencies: '.count($agencies).PHP_EOL;
  748.         echo 'Total debits processed: '.$totalProcessed.PHP_EOL;
  749.         echo '  - Created: '.$totalCreated.PHP_EOL;
  750.         echo '  - Updated: '.$totalUpdated.PHP_EOL;
  751.         echo '  - Errors: '.$totalErrors.PHP_EOL;
  752.         echo 'Agencies with negative balance: '.$negativeBalanceAgencies.PHP_EOL;
  753.         echo 'Execution time: '.$duration.' sec'.PHP_EOL;
  754.         echo 'Memory used: '.$memoryUsed.' MB'.PHP_EOL;
  755.         echo 'End time: '.date('Y-m-d H:i:s').PHP_EOL;
  756.         echo '=========================================='.PHP_EOL.PHP_EOL;
  757.     }
  758.     public function fixDebitsByEvents()
  759.     {
  760.         $datesArray = [
  761.             '2021-04-12',
  762.             '2021-04-11',
  763.             '2021-04-10',
  764.             '2021-04-09',
  765.             '2021-04-08',
  766.             '2021-04-07',
  767.             '2021-04-06',
  768.             '2021-04-05',
  769.             '2021-04-04',
  770.             '2021-04-03',
  771.             '2021-04-02',
  772.         ];
  773.         foreach ($datesArray as $now) {
  774.             $events = $this->eventsRepository->findToDayEvents($now);
  775.             foreach ($events as $event) {
  776.                 if ($event['agencyId']) {
  777.                     $agency = $this->agencyRepository->findOneBy(['id' => $event['agencyId']]);
  778.                     if ($agency) {
  779.                         $debit = $this->debitRepository->findByAgencyAndEvent($agency, $now);
  780.                         if ( ! $debit) {
  781.                             $debit = new Debit();
  782.                             $debit->setAgency($agency);
  783.                             $debit->setDateType(3);
  784.                             $debit->setType('event');
  785.                             $debit->setStatus('paid');
  786.                             $debit->setDate(new \DateTime($now));
  787.                         }
  788.                         $debit->setSum($event['eventPrices']);
  789.                         $this->em->persist($debit);
  790.                         $this->em->flush();
  791.                     }
  792.                 }
  793.             }
  794.         }
  795.     }
  796.     public function updateEmptyEvents()
  797.     {
  798.         $events = $this->eventsRepository->findEventsUntillNow('2021-10-19');
  799.         foreach ($events as $event) {
  800.             $httpClient = HttpClient::create();
  801.             $offreId    = $event['offerId'];
  802.             $response   = $httpClient->request(
  803.                 'GET',
  804.                 'https://api.otpusk.com/api/2.5/tours/offer?offerId='.$offreId.'&access_token=4b26a-e5237-9b0fa-bcc36-67732',
  805.                 ['headers' => ['User-Agent' => 'pro.otpusk.com']]
  806.             );
  807.             if (200 == $response->getStatusCode()) {
  808.                 $content = $response->toArray()['offer'];
  809.                 if ($content) {
  810.                     var_dump($event['offerId']);
  811.                     $this->eventsRepository->updateEvent($event, $content);
  812.                     sleep(1);
  813.                 }
  814.             }
  815.         }
  816.     }
  817. }