src/Controller/MobileApi/OfferController.php line 238

Open in your IDE?
  1. <?php
  2. namespace Slivki\Controller\MobileApi;
  3. use Slivki\Controller\SiteController;
  4. use Slivki\Dao\Banner\AppCategoryBannerDaoInterface;
  5. use Slivki\Dao\TireFilter\TireFilterDaoInterface;
  6. use Slivki\Dto\Filter\PaginatorFilterDto;
  7. use Slivki\Dto\Filter\PriceFilterDto;
  8. use Slivki\Dto\Filter\SortFilterDto;
  9. use Slivki\Entity\City;
  10. use Slivki\Entity\CreditCard;
  11. use Slivki\Entity\OfferOrder;
  12. use Slivki\Entity\User;
  13. use Slivki\Entity\Visit;
  14. use Slivki\Enum\DeviceTypeEnum;
  15. use Slivki\Enum\LocalizationLanguage;
  16. use Slivki\Enum\Location\DefaultCoordinates;
  17. use Slivki\Enum\Offer\OfferApiSort;
  18. use Slivki\Message\Query\Tire\GetTireOffersQuery;
  19. use Slivki\Paginator\Offer\OfferIdsByGiftCertificateFilterPaginatorInterface;
  20. use Slivki\Services\CacheService;
  21. use Slivki\Services\Category\CategoryDeliveryFoodOffersCustomSorter;
  22. use Slivki\Services\Localization\TranslateService;
  23. use Slivki\Services\MainMenu\MainMenuAppCacheService;
  24. use Slivki\Services\MainMenu\MainMenuOplatiCacheService;
  25. use Slivki\Services\MobApiCacheService;
  26. use Slivki\Services\MobApiOfferResponseUpdater;
  27. use Slivki\Services\Offer\GeoLocationService;
  28. use Slivki\Services\Offer\OfferResponseCacheService;
  29. use Slivki\Services\Offer\PhoneService;
  30. use Slivki\Services\Payment\PaymentService;
  31. use Slivki\Services\Subscription\SubscriptionService;
  32. use Slivki\Services\UserGetter;
  33. use Slivki\ValueObject\Coordinate;
  34. use Symfony\Component\HttpFoundation\JsonResponse;
  35. use Symfony\Component\HttpFoundation\Response;
  36. use Symfony\Component\Routing\Annotation\Route;
  37. use Slivki\Entity\Category;
  38. use Slivki\Entity\GeoLocation;
  39. use Slivki\Entity\MainMenu;
  40. use Slivki\Entity\Offer;
  41. use Slivki\Services\Offer\OfferCacheService;
  42. use Slivki\Services\ImageService;
  43. use Slivki\Util\SoftCache;
  44. use Symfony\Component\HttpFoundation\Request;
  45. use Slivki\Util\Logger;
  46. use OpenApi\Annotations as OA;
  47. use Nelmio\ApiDocBundle\Annotation\Model;
  48. use Slivki\Entity\EntityOption;
  49. use Slivki\Services\BePaidService;
  50. use Slivki\Dto\Offer\GiftCertificate\FilterDto;
  51. use Slivki\Dto\Media\ImageWithTagDto;
  52. use function array_map;
  53. use function array_slice;
  54. use function count;
  55. class OfferController extends MobileApiController
  56. {
  57.     /**
  58.      * Список категорий по городам
  59.      * @Route("/mobile/api/v2/city/{cityID}/categories/{pageNumber}", methods={"GET"});
  60.      * @OA\Response(
  61.      *     response = 200,
  62.      *     description = "Список категорий",
  63.      *     @OA\Schema(
  64.      *        @OA\Property(property = "ID", type = "integer", description = "ID категории"),
  65.      *        @OA\Property(property = "name", type = "varchar", description = "название категории"),
  66.      *        @OA\Property(property = "entityCount", type = "integer", description = "количество акций"),
  67.      *        @OA\Property(property = "iconURL", type = "varchar", description = "изображение категории"),
  68.      *        @OA\Property(property = "offers", type = "object", description = "акции",
  69.      *          @OA\Property(property = "ID", type = "integer", description = "ID акции"),
  70.      *          @OA\Property(property = "name", type = "varchar", description = "название акции"),
  71.      *          @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
  72.      *          @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
  73.      *          @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
  74.      *          @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
  75.      *          @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
  76.      *          @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
  77.      *          @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
  78.      *          @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
  79.      *          @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
  80.      *          @OA\Property(property = "offerType", type = "integer", description = "тип оффера. 0 обычный, 1 - онлайн ордер, 2 оноайл ордер без возможности купить код отдельно, 3 трайпл, 4 шм, 5 сертификаты"),
  81.      *          @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
  82.      *              @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  83.      *              @OA\Property(property = "label", type = "varchar", description = "метка"),
  84.      *          ),
  85.      *          @OA\Property(property = "locations", type = "object", description = "местоположение",
  86.      *              @OA\Property(property = "address", type = "varchar", description = "адрес"),
  87.      *              @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
  88.      *              @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
  89.      *                  @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  90.      *                  @OA\Property(property = "label", type = "varchar", description = "метка"),
  91.      *              ),
  92.      *              @OA\Property(property = "description", type = "varchar", description = "описание"),
  93.      *              @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
  94.      *          ),
  95.      *          @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
  96.      *          @OA\Property(property = "address", type = "varchar", description = "адрес"),
  97.      *          @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
  98.      *          @OA\Property(property = "companyLogoImage", type = "varchar", description = "URL лого компании"),
  99.      *          @OA\Property(property = "buyButtonLabel", type = "varchar", description = "текст кнопки покупки кода"),
  100.      *          @OA\Property(property = "openOnlineOrderButtonLabel", type = "varchar", description = "текст кнопки онлайн заказа"),
  101.      *        ),
  102.      *        @OA\Property(property = "categoryCount", type = "integer", description = "количество категорий"),
  103.      *        @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
  104.      *        @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
  105.      *        type="object",
  106.      *     )
  107.      * )
  108.      * @OA\Parameter(
  109.      *     name = "cityID",
  110.      *     in = "path",
  111.      *     description = "ID города",
  112.      *     required = true,
  113.      *     @OA\Schema(type="integer"),
  114.      *)
  115.      * @OA\Parameter(
  116.      *     name = "pageNumber",
  117.      *     in = "path",
  118.      *     description = "номер страницы",
  119.      *     required = true,
  120.      *     @OA\Schema(type="integer"),
  121.      *)
  122.      * @OA\Parameter(
  123.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  124.      *     in = "header",
  125.      *     description = "Токен юзера",
  126.      *     required = true,
  127.      *     @OA\Schema(type="string"),
  128.      *)
  129.      * @OA\Tag(name="Category")
  130.      */
  131.     public function getCategoriesAction(MobApiCacheService $mobApiCacheServiceOfferCacheService $offerCacheServiceImageService $imageService$cityID$pageNumber)
  132.     {
  133.         ini_set('memory_limit''4g');
  134.         $categories $this->getCategories(
  135.             $mobApiCacheService,
  136.             $offerCacheService,
  137.             $imageService,
  138.             0,
  139.             $pageNumber,
  140.             $cityID,
  141.             true,
  142.             $this->getUser(),
  143.         );
  144.         return $this->getResponse($categories200);
  145.     }
  146.     /**
  147.      * Список категорий и субкатегорий
  148.      * @Route("/mobile/api/v2/city/{cityID}/categories-tree", methods={"GET"});
  149.      * @OA\Response(
  150.      *     response=Response::HTTP_OK,
  151.      *     description="Список категорий и субкатегорий",
  152.      *     @OA\JsonContent(
  153.      *         @OA\Property(property="categories", type="array", description="Категории",
  154.      *             @OA\Items(
  155.      *                 @OA\Property(property="ID", type="string", description="ID категории"),
  156.      *                 @OA\Property(property="topParentCategoryId", type="integer", nullable=true, description="Id главной родительской категории"),
  157.      *                 @OA\Property(property="name", type="string", description="название категории"),
  158.      *                 @OA\Property(property="entityCount", type="integer", description="количество акций"),
  159.      *                 @OA\Property(property="iconURL", type="string", description="изображение категории"),
  160.      *                 @OA\Property(property="workExamplesCount", type="integer", description="количество примеров работ"),
  161.      *                 @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
  162.      *                 @OA\Property(property="interiorGalleryOffersCount", type="integer", description="количество акций с галереей интерьеров"),
  163.      *                 @OA\Property(property="hideWorkExamples", type="boolean", description="Не показывать примеры работ"),
  164.      *                 @OA\Property(property="tireFilterCategories", type="array", description="доступные категории для фильтра шиномонтажа",
  165.      *                     @OA\Items(
  166.      *                         @OA\Property(property="id", type="integer", description="ID категории"),
  167.      *                         @OA\Property(property="name", type="string", description="название категории"),
  168.      *                     ),
  169.      *                 ),
  170.      *                 @OA\Property(property="subcategories", type="array", description="субкатегории",
  171.      *                     @OA\Items(
  172.      *                         @OA\Property(property="ID", type="integer", description="ID субкатегории"),
  173.      *                         @OA\Property(property="topParentCategoryId", type="integer", nullable=true, description="Id главной родительской категории"),
  174.      *                         @OA\Property(property="name", type="string", description="название субкатегории"),
  175.      *                         @OA\Property(property="entityCount", type="integer", description="количество акций субкатегории"),
  176.      *                         @OA\Property(property="iconURL", type="string", description="изображение субкатегории"),
  177.      *                         @OA\Property(property="workExamplesCount", type="integer", description="количество примеров работ"),
  178.      *                         @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
  179.      *                         @OA\Property(property="interiorGalleryOffersCount", type="integer", description="количество акций с галереей интерьеров"),
  180.      *                         @OA\Property(property="hideWorkExamples", type="boolean", description="Не показывать примеры работ"),
  181.      *                         @OA\Property(property="tireFilterCategories", type="array", description="доступные категории для фильтра шиномонтажа",
  182.      *                             @OA\Items(
  183.      *                                 @OA\Property(property="id", type="integer", description="ID категории"),
  184.      *                                 @OA\Property(property="name", type="string", description="название категории"),
  185.      *                             ),
  186.      *                         ),
  187.      *                         @OA\Property(property="tireFilterDiscountCategories", type="array", description="доступные категории cо скидками для фильтра шиномонтажа",
  188.      *                             @OA\Items(
  189.      *                                 @OA\Property(property="id", type="integer", description="ID категории"),
  190.      *                                 @OA\Property(property="name", type="string", description="название категории"),
  191.      *                             ),
  192.      *                         ),
  193.      *                         @OA\Property(property="giftCertificateFilterCategories", type="array", description="доступные категории для фильтра по сертификатам",
  194.      *                             @OA\Items(
  195.      *                                 @OA\Property(property="id", type="integer", description="ID категории"),
  196.      *                                 @OA\Property(property="name", type="string", description="название категории"),
  197.      *                             ),
  198.      *                         ),
  199.      *                     ),
  200.      *                 ),
  201.      *             ),
  202.      *        ),
  203.      *        @OA\Property(property="categoryCount", type="integer", description="количество категорий"),
  204.      *        type="object",
  205.      *     ),
  206.      * ),
  207.      * @OA\Parameter(
  208.      *     name = "cityID",
  209.      *     in = "path",
  210.      *     description = "ID города",
  211.      *     required = true,
  212.      *     @OA\Schema(type="integer"),
  213.      *)
  214.      * @OA\Parameter(
  215.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  216.      *     in = "header",
  217.      *     description = "Токен юзера",
  218.      *     required = true,
  219.      *     @OA\Schema(type="string"),
  220.      *)
  221.      * @OA\Tag(name="Category")
  222.      */
  223.     public function getCategoriesTreeAction(
  224.         Request $request,
  225.         MainMenuAppCacheService $mainMenuAppCacheService,
  226.         MainMenuOplatiCacheService $mainMenuOplatiCacheService,
  227.         TranslateService $translateService,
  228.         int $cityID
  229.     ): JsonResponse {
  230.         $locale LocalizationLanguage::language($request->query->get('language'));
  231.         $mainMenu User::OPLATI_PARTNER_TOKEN === $request->headers->get('HTTP-SLIVKi-PARTNER-TOKEN')
  232.             ? $mainMenuOplatiCacheService->getMainMenuCached($cityID)
  233.             : $mainMenuAppCacheService->getMainMenuCached($cityID);
  234.         foreach ($mainMenu['categories'] as $key => $category) {
  235.             $mainMenu['categories'][$key]['name'] = $translateService->getCategoryTranslationForField(
  236.                 $category['ID'],
  237.                 'name',
  238.                 $locale,
  239.                 $category['name']
  240.             );
  241.         }
  242.         return $this->getResponseWithoutUser($mainMenuResponse::HTTP_OK);
  243.     }
  244.     public function getCategories(MobApiCacheService $mobApiCacheServiceOfferCacheService $offerCacheServiceImageService $imageService$parentID$pageNumber$cityID null$withOffers false$user null) {
  245.         $categories $this->getCategoriesCached($mobApiCacheService$imageService$parentID$pageNumber$cityID$withOffers$user);
  246.         if (!$withOffers) {
  247.             return $categories;
  248.         }
  249.         $offerRepository $this->getDoctrine()->getRepository(Offer::class);
  250.         $result = [];
  251.         foreach ($categories['categories'] as $key => $category) {
  252.             foreach ($category['offers']['offers'] as $offerKey => $offerArray) {
  253.                 $offer $offerCacheService->getOffer($offerArray['ID']);
  254.                 $categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] = false;
  255.                 if ($offer) {
  256.                     $categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] = $offerRepository->isOfferFreeForUser($offer$user);
  257.                     if ($categories['categories'][$key]['offers']['offers'][$offerKey]['isFreeCode'] && !$offer->isFree()
  258.                         && $offer->getID() != Offer::PETROL_OFFER_ID) {
  259.                         $categories['categories'][$key]['offers']['offers'][$offerKey]['freeCodeCount'] = 1;
  260.                     }
  261.                 }
  262.             }
  263.             $result['categories'][] = $categories['categories'][$key];
  264.         }
  265.         $result['categoryCount'] = $categories['categoryCount'];
  266.         $result['isLast'] = $categories['isLast'];
  267.         return $result;
  268.     }
  269.     private function getCategoriesCached(MobApiCacheService $mobApiCacheServiceImageService $imageService$parentID$pageNumber$cityID null$withOffers false$user null) {
  270.         $softCache = new SoftCache('MOBILE-1-');
  271.         $cacheKey 'categories-19-' $parentID;
  272.         if ($pageNumber) {
  273.             $cacheKey .= '-page-' $pageNumber;
  274.         }
  275.         if ($cityID) {
  276.             $cacheKey .= '-city-' $cityID;
  277.         }
  278.         if ($withOffers) {
  279.             $cacheKey .= '-offers';
  280.         }
  281.         $result $softCache->get($cacheKey);
  282.         if ($result) {
  283.             return $result;
  284.         }
  285.         Logger::instance('CACHEDEBUG')->info('category not in cache ' $parentID);
  286.         $result =  [];
  287.         $entityManager $this->getDoctrine()->getManager();
  288.         $itemList $entityManager->getRepository(MainMenu::class)->getItemList(MainMenu::MENU_ID_MAIN$cityID);
  289.         $categoryIDs = [];
  290.         /** @var MainMenu $item */
  291.         foreach ($itemList as $item) {
  292.             if ($item->getType() == MainMenu::TYPE_OFFER_CATEGORY) {
  293.                 $categoryID $item->getEntityID();
  294.                 $category $entityManager->getRepository(Category::class)->getCategoryForMobileApi($categoryID);
  295.                 if (!$category) {
  296.                     continue;
  297.                 }
  298.                 $categoryIDs[] = $item->getEntityID();
  299.             }
  300.         }
  301.         $categoryCount count($categoryIDs);
  302.         if ($cityID == City::DEFAULT_CITY_ID) {
  303.             array_unshift($categoryIDsCategory::FOOD_DELIVERY_CATEGORY_ID);
  304.         }
  305.         $perPage 3;
  306.         $isLast true;
  307.         if ($pageNumber) {
  308.             $offset = ($pageNumber 1) * $perPage;
  309.             $isLast count($categoryIDs) <= $offset $perPage;
  310.             $categoryIDs array_slice($categoryIDs$offset$perPage);
  311.         }
  312.         /** @var Category $category */
  313.         foreach ($categoryIDs as $categoryID) {
  314.             $category $entityManager->getRepository(Category::class)->getCategoryForMobileApi($categoryID);
  315.             if (!$category) {
  316.                 continue;
  317.             }
  318.             $iconURL 'https://www.slivki.by';
  319.             $media $category->getAppIconMedia();
  320.             if (!$media) {
  321.                 $media $category->getMobileMenuIconMedia();
  322.             }
  323.             $iconURL .= $media $imageService->getImageURL($media232232) : ImageService::FALLBACK_IMAGE;
  324.             $item = [
  325.                 'ID' => $category->getID(),
  326.                 'name' => $category->getName(),
  327.                 'entityCount' => $category->getEntityCount(),
  328.                 'iconURL' => $iconURL
  329.             ];
  330.             if ($withOffers) {
  331.                 $item['offers'] = $mobApiCacheService->getOffersByCategoryIDCached($category->getID(), 10);
  332.             }
  333.             $result[] = $item;
  334.         }
  335.         $data = [];
  336.         $data['categoryCount'] = $categoryCount;
  337.         if ($parentID == 0) {
  338.             $data['categories'] = $result;
  339.         } else {
  340.             $data $result;
  341.         }
  342.         if ($pageNumber) {
  343.             $data['isLast'] = $isLast;
  344.         }
  345.         $softCache->set($cacheKey$data,  60 60);
  346.         return $data;
  347.     }
  348.     /**
  349.      * Список акций по категории
  350.      * @Route("/mobile/api/v2/category/{categoryID}/{pageNumber}", methods={"GET"});
  351.      * @OA\Response(
  352.      *     response = 200,
  353.      *     description = "Список акций по категории",
  354.      *     @OA\Schema(
  355.      *        @OA\Property(property = "offers", type = "object", description = "акции",
  356.      *          @OA\Property(property = "ID", type = "integer", description = "ID акции"),
  357.      *          @OA\Property(property = "name", type = "varchar", description = "название акции"),
  358.      *          @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
  359.      *          @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
  360.      *          @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
  361.      *          @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
  362.      *          @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
  363.      *          @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
  364.      *          @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
  365.      *          @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
  366.      *          @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
  367.      *          @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
  368.      *              @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  369.      *              @OA\Property(property = "label", type = "varchar", description = "метка"),
  370.      *          ),
  371.      *          @OA\Property(property = "locations", type = "object", description = "местоположение",
  372.      *              @OA\Property(property = "address", type = "varchar", description = "адрес"),
  373.      *              @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
  374.      *              @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
  375.      *                  @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  376.      *                  @OA\Property(property = "label", type = "varchar", description = "метка"),
  377.      *              ),
  378.      *              @OA\Property(property = "description", type = "varchar", description = "описание"),
  379.      *              @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
  380.      *          ),
  381.      *          @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
  382.      *          @OA\Property(property = "address", type = "varchar", description = "адрес"),
  383.      *          @OA\Property(property = "offerType", type = "integer", description = "тип акции"),
  384.      *          @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
  385.      *          @OA\Property(property = "companyLogoImage", type = "object", description = "URL лого компании"),
  386.      *          @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
  387.      *          @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
  388.      *        ),
  389.      *        @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
  390.      *        @OA\Property(property = "offersCount", type = "integer", description = "количество акций"),
  391.      *        @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
  392.      *        type="object",
  393.      *     )
  394.      * )
  395.      * @OA\Parameter(
  396.      *     name = "categoryID",
  397.      *     in = "path",
  398.      *     description = "ID категории",
  399.      *     required = true,
  400.      *     @OA\Schema(type="integer"),
  401.      *)
  402.      * @OA\Parameter(
  403.      *     name = "pageNumber",
  404.      *     in = "path",
  405.      *     description = "номер страницы",
  406.      *     required = true,
  407.      *     @OA\Schema(type="integer"),
  408.      *)
  409.      * @OA\Parameter(
  410.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  411.      *     in = "header",
  412.      *     description = "Токен юзера",
  413.      *     required = true,
  414.      *     @OA\Schema(type="string"),
  415.      *)
  416.      * @OA\Tag(name="Category")
  417.      */
  418.     public function getOffersAction(
  419.         OfferCacheService $offerCacheService,
  420.         MobApiCacheService $mobApiCacheService,
  421.         $categoryID,
  422.         $pageNumber
  423.     ): JsonResponse {
  424.         ini_set('memory_limit''4g');
  425.         $user $this->getUser();
  426.         $result $mobApiCacheService->getOffersByCategoryIDCached($categoryID$pageNumber);
  427.         $offerRepository $this->getDoctrine()->getRepository(Offer::class);
  428.         foreach ($result['offers'] as $key => $offerArray) {
  429.             $offer $offerCacheService->getOffer($offerArray['ID']);
  430.             if ($offer) {
  431.                 if ($offer->isBuyCodeDisable() || $offer->isHideInApp()) {
  432.                     unset($result['offers'][$key]);
  433.                 }
  434.                 $result['offers'][$key]['isFreeCode'] = $offerRepository->isOfferFreeForUser($offer$user);
  435.             }
  436.         }
  437.         return $this->getResponse($result200);
  438.     }
  439.     /**
  440.      * Отсортированные акции категории
  441.      * @Route("/mobile/api/v2/category-sorted", methods={"POST"});
  442.      * @OA\Response(
  443.      *     response = 200,
  444.      *     description = "Отсортированные акции категории",
  445.      *     @OA\JsonContent(
  446.      *        @OA\Property(property="offers", type="object", description="акции",
  447.      *          @OA\Property(property="ID", type="integer", description="ID акции"),
  448.      *          @OA\Property(property="name", type="string", description="название акции"),
  449.      *          @OA\Property(property="regularPrice", type="number", format="decimal", description="обычная цена"),
  450.      *          @OA\Property(property="offerPrice", type="number", format="decimal", description="цена со скидкой"),
  451.      *          @OA\Property(property="discountPercent", type="string", description="процент скидки"),
  452.      *          @OA\Property(property="isWithoutCodes", type="boolean", description="Акция без промокодов"),
  453.      *          @OA\Property(property="saleCount", type="integer", description="количество проданных кодов"),
  454.      *          @OA\Property(property="imageURL", type="string", description="URL изображения для тизера"),
  455.      *          @OA\Property(property="verticalImageUrl", type="string", description="URL вертикального изображения для тизера"),
  456.      *          @OA\Property(property="activeTill", type="datetime", description="дата окончания акции"),
  457.      *          @OA\Property(property="rating", type="number", format="decimal", description="рейтинг акции"),
  458.      *          @OA\Property(property="codeCost", type="string", description="цена кода"),
  459.      *          @OA\Property(property="conversion", type="number", description="Конверсия"),
  460.      *          @OA\Property(property="commentsCount", type="integer", description="Количество комментариев"),
  461.      *          @OA\Property(property="phoneNumbersWithoutLocation", type="object", description="номера телефонов без местоположения",
  462.      *              @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
  463.      *              @OA\Property(property="label", type="string", description="метка"),
  464.      *          ),
  465.      *          @OA\Property(
  466.      *              property="images",
  467.      *              description="Список изображений акции",
  468.      *              type="array",
  469.      *              @OA\Items(ref=@Model(type=ImageWithTagDto::class)),
  470.      *          ),
  471.      *          @OA\Property(property="locations", type="object", description="местоположение",
  472.      *              @OA\Property(property="address", type="string", description="адрес"),
  473.      *              @OA\Property(property="workingHours", type="string", description="время работы"),
  474.      *              @OA\Property(property="phoneNumbers", type="object", description="номера телефонов",
  475.      *                  @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
  476.      *                  @OA\Property(property="label", type="string", description="метка"),
  477.      *              ),
  478.      *              @OA\Property(property="description", type="string", description="описание"),
  479.      *              @OA\Property(property="geoLocation", type="string", description="массив координат"),
  480.      *          ),
  481.      *          @OA\Property(property="visitCount", type="integer", description="количество посещений"),
  482.      *          @OA\Property(property="address", type="string", description="адрес"),
  483.      *          @OA\Property(property="offerType", type="integer", description="тип акции"),
  484.      *          @OA\Property(property="dynamicData", type="object", description="Динамические данные акции",
  485.      *               @OA\Property(property="offerId", type="integer", description="Id акции"),
  486.      *               @OA\Property(property="realTimeDeliveryTime", type="integer", description="Количество минут до ближайшей доставки"),
  487.      *               @OA\Property(property="realTimeShippingTime", type="integer", description="Количество минут до ближайшего самовывоза"),
  488.      *          ),
  489.      *          @OA\Property(property="allowedOnlineOrderTypes", type="array", description="Типы онлайн заказа",
  490.      *              @OA\Items(type="integer", description="1 - Food, 2 - Gift-certificate, 3 - tire"),
  491.      *          ),
  492.      *          @OA\Property(property="isFreeCode", type="boolean", description="бесплатный ли код"),
  493.      *          @OA\Property(property="companyLogoImage", type="object", description="URL лого компании"),
  494.      *          @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
  495.      *          @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
  496.      *          @OA\Property(property="deliveryTime", type="object", nullable="true", description="Время работы доставки",
  497.      *              @OA\Property(property="text", type="string", description="Текст доставки"),
  498.      *              @OA\Property(property="isOpen", type="boolean", description="Открыто заведение"),
  499.      *          ),
  500.      *        ),
  501.      *        @OA\Property(property="paymentMethodsForOnlineOrder", nullable=true, type="object", description="Способы оплаты",
  502.      *           @OA\Property(property="delivery", type="object", description="Доставка",
  503.      *           @OA\Property(property="online", type="boolean", description="Онлайн оплата"),
  504.      *           @OA\Property(property="cache", type="boolean", description="Оплата наличными"),
  505.      *           @OA\Property(property="terminal", type="boolean", description="Оплата картой"),
  506.      *           @OA\Property(property="slivki-pay", type="boolean", description="Оплата через Slivki Pay"),
  507.      *        )),
  508.      *        @OA\Property(property="isLast", type="boolean", description="последняя ли страница"),
  509.      *        @OA\Property(property="offersCount", type="integer", description="количество акций"),
  510.      *        @OA\Property(property="showItemsCatalog", type="boolean", description="выводить ли в категории товарный каталог"),
  511.      *        @OA\Property(property="userBalance", type="string", description="баланс пользователя"),
  512.      *        @OA\Property(property="currency", type="object", nullable="true", description="Курс для перевода в валюту",
  513.      *            @OA\Property(property="code", type="string", description="Название валюты (GEL)"),
  514.      *            @OA\Property(property="rate", type="number", description="Курс конверсии в бел рубль"),
  515.      *        ),
  516.      *        type="object",
  517.      *     )
  518.      * )
  519.      * @OA\RequestBody(
  520.      *     required=true,
  521.      *     @OA\MediaType(
  522.      *         mediaType="application/json",
  523.      *         @OA\Schema(
  524.      *              required={"categoryID", "longitude", "latitude", "pageNumber", "sortBy"},
  525.      *              @OA\Property(property="categoryID", description="ID категории", type="integer"),
  526.      *              @OA\Property(property="longitude", description="долгота", type="float"),
  527.      *              @OA\Property(property="latitude", description="широта", type="float"),
  528.      *              @OA\Property(property="pageNumber", description="номер страницы", type="integer"),
  529.      *              @OA\Property(property="sortBy", description="сортировка", type="integer"),
  530.      *              @OA\Property(
  531.      *                  property="tireFilter",
  532.      *                  description="Фильтр онлайн заказа",
  533.      *                  type="object",
  534.      *                  nullable=true,
  535.      *                  @OA\Property(property="weekday", type="integer", description="День недели 0 - воскресенье", example="0"),
  536.      *                  @OA\Property(property="time", type="string", description="Время в формате HH:MM", example="12:30"),
  537.      *              ),
  538.      *              @OA\Property(
  539.      *                  property="giftCertificateFilter",
  540.      *                  type="object",
  541.      *                  nullable=true,
  542.      *                  description="Фильтр по сертификатам",
  543.      *                  @OA\Property(
  544.      *                      property="price",
  545.      *                      nullable=true,
  546.      *                      type="object",
  547.      *                      description="Фильтр по цене",
  548.      *                      ref=@Model(type=PriceFilterDto::class),
  549.      *                  ),
  550.      *                  @OA\Property(
  551.      *                      property="paginator",
  552.      *                      type="object",
  553.      *                      description="Пагинация",
  554.      *                      ref=@Model(type=PaginatorFilterDto::class),
  555.      *                  ),
  556.      *                  @OA\Property(
  557.      *                      property="sort",
  558.      *                      type="object",
  559.      *                      description="Сортировка",
  560.      *                      ref=@Model(type=SortFilterDto::class),
  561.      *                  ),
  562.      *                  @OA\Property(property="giftCertificateCategoryId", type="integer", description="Время в формате HH:MM"),
  563.      *              ),
  564.      *         ),
  565.      *     ),
  566.      * ),
  567.      * @OA\Parameter(
  568.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  569.      *     in = "header",
  570.      *     description = "Токен юзера",
  571.      *     required = true,
  572.      *     @OA\Schema(type="string"),
  573.      *)
  574.      * @OA\Tag(name="Offer")
  575.      */
  576.     public function getOffersSortedAction(
  577.         Request $request,
  578.         OfferCacheService $offerCacheService,
  579.         MobApiCacheService $mobApiCacheService,
  580.         TireFilterDaoInterface $tireFilterDao,
  581.         OfferIdsByGiftCertificateFilterPaginatorInterface $offerIdsByGiftCertificateFilterPaginator,
  582.         CategoryDeliveryFoodOffersCustomSorter $categoryDeliveryFoodOffersCustomSorter,
  583.         AppCategoryBannerDaoInterface $appCategoryBannerDao,
  584.         MobApiOfferResponseUpdater $mobApiOfferResponseUpdater
  585.     ): JsonResponse {
  586.         $user $this->getUser();
  587.         $data json_decode($request->getContent());
  588.         $categoryID $data->categoryID;
  589.         $pageNumber $data->pageNumber;
  590.         $sortBy $data->sortBy;
  591.         $location = [];
  592.         if (OfferApiSort::DISTANCE === $data->sortBy) {
  593.             $location['latitude'] = $data->latitude ?: DefaultCoordinates::LATITUDE;
  594.             $location['longitude'] = $data->longitude ?: DefaultCoordinates::LONGITUDE;
  595.         }
  596.         $tireFilter $data->tireFilter ?? null;
  597.         $giftCertificateFilter $data->giftCertificateFilter ?? null;
  598.         if (null !== $tireFilter) {
  599.             $offersData $tireFilterDao->getOffersByTireFilter(new GetTireOffersQuery($categoryID$tireFilter->weekday$tireFilter->time));
  600.             $offers $offerCacheService->getOffers(array_keys($offersData), truetrue);
  601.             $result = [
  602.                 'offers' => $mobApiCacheService->createOfferResponse($offers),
  603.                 'isLast' => true,
  604.                 'offersCount' => count($offers),
  605.                 'offerAvailableAddress' => $offersData
  606.             ];
  607.         } elseif (null !== $giftCertificateFilter) {
  608.             $offerIds array_map(
  609.                 static fn (array $offer): int => (int) $offer['id'],
  610.                 (array) $offerIdsByGiftCertificateFilterPaginator->findOfferIdsByGiftCertificateFilter(new FilterDto(
  611.                     true,
  612.                     new SortFilterDto($giftCertificateFilter->sort->by$giftCertificateFilter->sort->direction),
  613.                     new PaginatorFilterDto($giftCertificateFilter->paginator->page$giftCertificateFilter->paginator->perPage),
  614.                     $categoryID,
  615.                     $giftCertificateFilter->giftCertificateCategoryId,
  616.                     property_exists($giftCertificateFilter'price')
  617.                         ? new PriceFilterDto($giftCertificateFilter->price->minPrice$giftCertificateFilter->price->maxPrice)
  618.                         : null,
  619.                     null !== $location['latitude'] && null !== $location['longitude']
  620.                         ? new Coordinate($location['latitude'], $location['longitude'])
  621.                         : null,
  622.                 ))->getItems(),
  623.             );
  624.             $offers $offerCacheService->getOffers($offerIdstruetrue);
  625.             $offersCount count($offers);
  626.             $result = [
  627.                 'offers' => $mobApiCacheService->createOfferResponse($offers),
  628.                 'isLast' =>  $offersCount $giftCertificateFilter->paginator->perPage,
  629.                 'offersCount' => $offersCount,
  630.             ];
  631.         } elseif ($sortBy !== OfferApiSort::TIMETABLE && $categoryDeliveryFoodOffersCustomSorter->support($categoryID)) {
  632.             $offers $mobApiCacheService->getAllOffersByCategoryIdCached($categoryID);
  633.             $offset = ($pageNumber 1) * MobApiCacheService::OFFERS_PER_PAGE;
  634.             $offerCount count($offers);
  635.             $result = [
  636.                 'isLast' => $offset MobApiCacheService::OFFERS_PER_PAGE >= $offerCount,
  637.                 'offersCount' => $offerCount,
  638.                 'offers' => array_slice(
  639.                     $categoryDeliveryFoodOffersCustomSorter->sort($offers),
  640.                     $offset,
  641.                     MobApiCacheService::OFFERS_PER_PAGE,
  642.                 ),
  643.             ];
  644.         } else {
  645.             $result $mobApiCacheService->getOffersByCategoryIDCached($categoryID$pageNumber$sortBy$location);
  646.         }
  647.         $category $mobApiCacheService->findCachedCategory($categoryID);
  648.         $result['showItemsCatalog'] = null !== $category && $category->getShowItemsCatalog();
  649.         foreach ($result['offers'] as $key => $offerArray) {
  650.             $result['offers'][$key] = $mobApiOfferResponseUpdater->updateOfferResponse($result['offers'][$key], $user$category);
  651.         }
  652.         $result['banners'] = $appCategoryBannerDao->getActiveByCategoryId($categoryID);
  653.         return $this->getResponse($resultResponse::HTTP_OK);
  654.     }
  655.     /**
  656.      * Акция
  657.      * @Route("/mobile/api/v2/offer", methods={"POST"});
  658.      * @OA\Response(
  659.      *     response=Response::HTTP_OK,
  660.      *     description="Описание акции",
  661.      *     content={
  662.      *         @OA\MediaType(
  663.      *             mediaType="application/json",
  664.      *             @OA\Schema(
  665.      *                  @OA\Property(property="ID", type="integer", description="ID акции"),
  666.      *                  @OA\Property(property="name", type="string", description="название акции"),
  667.      *                  @OA\Property(property="cityId", type="integer", description="ID города"),
  668.      *                  @OA\Property(property="saleCount", type="integer", description="количество проданных кодов"),
  669.      *                  @OA\Property(property="lastMonthSaleCount", type="integer", description="количество проданных кодов за последний месяц"),
  670.      *                  @OA\Property(property="visitCount", type="integer", description="Количество посещений"),
  671.      *                  @OA\Property(property="rating", type="float", description="рейтинг акции"),
  672.      *                  @OA\Property(property="active", type="boolean", description="акция активна"),
  673.      *                  @OA\Property(property="isFreeCode", type="boolean", description="платный/бесплатный код"),
  674.      *                  @OA\Property(property="codeCost", type="number", description="цена за промокод"),
  675.      *                  @OA\Property(property="phoneNumbersWithoutLocation", type="object", description="номера телефонов без локаций",
  676.      *                      @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
  677.      *                      @OA\Property(property="label", type="string", description="примечание"),
  678.      *                  ),
  679.      *                  @OA\Property(property="offerUrl", type="string", description="ссылка на акцию"),
  680.      *                  @OA\Property(property="offerType", type="integer", description="тип оффера"),
  681.      *                  @OA\Property(property="isWithoutCodes", type="boolean", description="Акция без промокодов"),
  682.      *                  @OA\Property(property="allowedOnlineOrderTypes", type="array", description="Типы онлайн заказа",
  683.      *                      @OA\Items(type="integer", description="1 - Food, 2 - Gift-certificate, 3 - tire"),
  684.      *                  ),
  685.      *                  @OA\Property(property="activeTill", type="datetime", description="дата окончания акции"),
  686.      *                  @OA\Property(property="conditions", type="string", description="условия акции"),
  687.      *                  @OA\Property(property="description", type="string", description="описание акции"),
  688.      *                  @OA\Property(property="features", type="string", description="особенности"),
  689.      *                  @OA\Property(property="workingHours", type="text", description="Время работы"),
  690.      *                  @OA\Property(property="locations", type="object", description="адреса акции",
  691.      *                      @OA\Property(property="address", type="string", description="адрес. г. Минск, ул. Захарова, 40"),
  692.      *                      @OA\Property(property="workingHours", type="string", description="часы работы заведения"),
  693.      *                      @OA\Property(property="phoneNumbers", type="object", description="номера телефонов в акции",
  694.      *                          @OA\Property(property="phoneNumber", type="string", description="номер телефона"),
  695.      *                          @OA\Property(property="label", type="string", description="примечание")
  696.      *                      ),
  697.      *                      @OA\Property(property="description", type="string", description="описание"),
  698.      *                      @OA\Property(property="geoLocation", type="string", description="координаты заведения"),
  699.      *                  ),
  700.      *                  @OA\Property(property="discountPercent", type="string", description="процент скидки"),
  701.      *                  @OA\Property(
  702.      *                      property="images",
  703.      *                      description="Тизеры акциии",
  704.      *                      type="array",
  705.      *                      @OA\Items(ref=@Model(type=ImageWithTagDto::class)),
  706.      *                  ),
  707.      *                  @OA\Property(property="shopImages", type="array", description="Изображения заведения",
  708.      *                      @OA\Items(type="string", description="URL изображения"),
  709.      *                  ),
  710.      *                  @OA\Property(property="galleryVideos", type="array", description="Видео в галерее акции",
  711.      *                      @OA\Items(type="object", description="Видео",
  712.      *                          @OA\Property(property="title", type="string", description="Заголовок видео"),
  713.      *                          @OA\Property(property="url", type="string", description="URL видео"),
  714.      *                      ),
  715.      *                  ),
  716.      *                  @OA\Property(property="galleryVideoPackage", type="object", description="Видео пакет в галерее акции",
  717.      *                      @OA\Property(property="title", type="string", description="Заголовок пакета"),
  718.      *                      @OA\Property(property="previewImageUrl", type="string", description="URL превью изображения"),
  719.      *                  ),
  720.      *                  @OA\Property(property="regularPrice", type="string", description="обычная цена (может быть строка от 50 руб.)"),
  721.      *                  @OA\Property(property="offerPrice", type="string", description="цена со скидкой"),
  722.      *                  @OA\Property(property="companyID", type="integer", description="ID компании"),
  723.      *                  @OA\Property(property="companyLogoImage", type="object", description="URL лого компании"),
  724.      *                  @OA\Property(property="buyButtonLabel", type="string", description="текст кнопки покупки кода"),
  725.      *                  @OA\Property(property="openOnlineOrderButtonLabel", type="string", description="текст кнопки онлайн заказа"),
  726.      *                  @OA\Property(property="conversion", type="number", description="Конверсия"),
  727.      *                  @OA\Property(property="numberOfPromocodesPerDay", type="number", description="Количество покупок за сутки"),
  728.      *                  @OA\Property(property="onlineAutoOpened", type="boolean", description="Открывать онлайн в апп автоматически"),
  729.      *                  @OA\Property(property="onlineOrderGiftEnabled", type="boolean", description="Заказ в подарок"),
  730.      *                  @OA\Property(property="userBalance", type="string", description="баланс пользователя"),
  731.      *                  @OA\Property(property="availableOnFood", type="boolean", description="Доступна в еде"),
  732.      *                  @OA\Property(property="workExamplesCount", type="integer", description="Количество примеров работ"),
  733.      *                  @OA\Property(property="beautyMasterCount", type="integer", description="количество мастеров"),
  734.      *                  @OA\Property(property="usersWithOfferInFavouritesCount", type="integer", description="Количество пользователей у которых акция в избранных"),
  735.      *                  @OA\Property(property="titleFontColor", type="string", description="Цвет шрифта названия акции"),
  736.      *                  @OA\Property(property="separateTabForCertificatesInApp", type="string", description="Вывод сертификатов в отдельном табе"),
  737.      *                  @OA\Property(property="telegram", type="string", description="Ник в телеграм через @"),
  738.      *                  @OA\Property(property="viber", type="string", description="Номер телефона в вайбер"),
  739.      *                  @OA\Property(property="companyName", type="string", description="Название компании", nullable=true),
  740.      *                  @OA\Property(property="foodOfferDeliveryZones", type="array", description="Зоны доставки еды акции",
  741.      *                      @OA\Items(type="object", description="Зона",
  742.      *                          @OA\Property(property="position", type="integer", description="Позиция зоны"),
  743.      *                          @OA\Property(property="name", type="string", description="Название зоны"),
  744.      *                      ),
  745.      *                  ),
  746.      *                  @OA\Property(property="messengerCallBack", type="string", description="Обратный звонок (телеграм/вайбер)"),
  747.      *                  @OA\Property(property="commentBanners", type="array", description="Баннеры в комментах",
  748.      *                      @OA\Items(type="object", description="Баннеры в комментах: 100% и 50%",
  749.      *                          @OA\Property(property="id", type="integer", description="Id баннера"),
  750.      *                          @OA\Property(property="type", type="integer", description="Тип баннера"),
  751.      *                          @OA\Property(property="title", type="string", description="Название баннера"),
  752.      *                          @OA\Property(property="url", type="string", description="Внешняя ссылка"),
  753.      *                          @OA\Property(property="filePath", type="string", description="Путь к картинке"),
  754.      *                          @OA\Property(property="position", type="integer", description="Позиция для отображения в списке"),
  755.      *                      ),
  756.      *                  ),
  757.      *                  @OA\Property(property = "paymentMethodsForOnlineOrder", type = "object", description = "Способы оплаты",
  758.      *                      @OA\Property(property = "delivery", type = "object", description = "Доставка",
  759.      *                      @OA\Property(property = "online", type = "boolean", description = "Онлайн оплата"),
  760.      *                      @OA\Property(property = "cache", type = "boolean", description = "Оплата наличными"),
  761.      *                      @OA\Property(property = "terminal", type = "boolean", description = "Оплата картой"),
  762.      *                      @OA\Property(property="slivki-pay", type="boolean", description="Оплата через Slivki Pay"),
  763.      *                  ),
  764.      *                  @OA\Property(property = "pickup", type = "object", description = "Самовывоз",
  765.      *                     @OA\Property(property = "online", type = "boolean", description = "Онлайн оплата"),
  766.      *                     @OA\Property(property = "cache", type = "boolean", description = "Оплата наличными"),
  767.      *                     @OA\Property(property = "terminal", type = "boolean", description = "Оплата картой"),
  768.      *                  )),
  769.      *                  type="object",
  770.      *              ),
  771.      *          ),
  772.      *      }
  773.      *   )
  774.      * )
  775.      * @OA\Response(
  776.      *     response = Response::HTTP_NOT_FOUND,
  777.      *     description = "Акция не найдена"
  778.      * )
  779.      * @OA\RequestBody(
  780.      *     required=true,
  781.      *       @OA\MediaType(
  782.      *           mediaType="application/json",
  783.      *           @OA\Schema(
  784.      *               type="object",
  785.      *               @OA\Property(
  786.      *                  property = "longitude",
  787.      *                  description = "долгота",
  788.      *                  type="number",
  789.      *                  example="27.557008",
  790.      *               ),
  791.      *               @OA\Property(
  792.      *                  property = "latitude",
  793.      *                  description = "широта",
  794.      *                  type="number",
  795.      *                  example="53.911724",
  796.      *               ),
  797.      *               @OA\Property(
  798.      *                  property = "offerID",
  799.      *                  description = "id акции",
  800.      *                  type="integer",
  801.      *                  example="1",
  802.      *               ),
  803.      *               @OA\Property(
  804.      *                  property = "language",
  805.      *                  description = "язык контента",
  806.      *                  type="string",
  807.      *                  example="ru",
  808.      *               ),
  809.      *           ),
  810.      *       ),
  811.      * ),
  812.      * @OA\Parameter(
  813.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  814.      *     in = "header",
  815.      *     description = "Токен юзера",
  816.      *     required = true,
  817.      *     @OA\Schema(type="string"),
  818.      *)
  819.      * @OA\Tag(name="Offer")
  820.      */
  821.     public function offerDetailsAction(
  822.         Request $request,
  823.         OfferResponseCacheService $offerResponseCacheService
  824.     ): JsonResponse {
  825.         $data json_decode($request->getContent());
  826.         $language $data->language ?? null;
  827.         $user $this->getUser();
  828.         $offerID = (int) $data->offerID;
  829.         $isPartner $request->headers->get(User::HTTP_PARTNER_TOKEN_HEADER_NAME) === User::OPLATI_PARTNER_TOKEN;
  830.         $offerResponse $offerResponseCacheService->getOfferResponseByOfferId($offerID$isPartner$language$user);
  831.         $this->addVisit($offerIDVisit::TYPE_OFFERDeviceTypeEnum::APP$user$request->getClientIp());
  832.         return $this->getResponse($offerResponse->toArray(), Response::HTTP_OK);
  833.     }
  834.     /**
  835.      * Список локаций акции по категории
  836.      * @Route("/mobile/api/v2/offer/locations",methods={"POST"})
  837.      * @OA\Response(
  838.      *     response = 200,
  839.      *     description = "Список локаций акции",
  840.      *     @OA\JsonContent(
  841.      *        @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
  842.      *        @OA\Property(property = "name", type = "varchar", description = "название акции"),
  843.      *        @OA\Property(property = "label", type = "varchar", description = "метка"),
  844.      *        @OA\Property(property = "imageURL", type = "varchar", description = "тизер акции"),
  845.      *        @OA\Property(property = "locations", type = "object", description = "местоположение",
  846.      *          @OA\Property(property = "latitude", type = "varchar", description = "широта"),
  847.      *          @OA\Property(property = "longitude", type = "varchar", description = "долгота"),
  848.      *        ),
  849.      *        type="object",
  850.      *     )
  851.      * )
  852.      * @OA\RequestBody(
  853.      *     required=true,
  854.      *     @OA\JsonContent(
  855.      *         @OA\Schema (
  856.      *              type="object",
  857.      *              @OA\Property(
  858.      *                  property="categoryID",
  859.      *                  required=true,
  860.      *                  description="id категори",
  861.      *                  @OA\Schema(type="integer"),
  862.      *              ),
  863.      *         )
  864.      *     )
  865.      * )
  866.      * @OA\Parameter(
  867.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  868.      *     in = "header",
  869.      *     description = "Токен юзера",
  870.      *     required = true,
  871.      *     @OA\Schema(type="string"),
  872.      *)
  873.      * @OA\Tag(name="Offer")
  874.      */
  875.     public function offerLocationsAction(Request $requestImageService $imageService) {
  876.         ini_set('memory_limit''8g');
  877.         $data json_decode($request->getContent());
  878.         $softCache = new SoftCache('mobapi-7');
  879.         $cacheKey 'locations-3-' $data->categoryID;
  880.         $result $softCache->get($cacheKey);
  881.         if ($result) {
  882.             return $this->getResponse($result200);
  883.         }
  884.         $entityManager $this->getDoctrine()->getManager();
  885.         if ($data->categoryID == 0) {
  886.             $offerList $entityManager->getRepository(Offer::class)->getAllActiveOffersCached();
  887.         } else {
  888.             $offerList $entityManager->getRepository(Offer::class)->getActiveOffersByCategoryID($data->categoryID);
  889.         }
  890.         $result = [];
  891.         /** @var Offer $offer */
  892.         foreach ($offerList as $offer) {
  893.             if (!$offer || $offer->isHideInApp()) {
  894.                 continue;
  895.             }
  896.             $item = [
  897.                 'offerID' => $offer->getID(),
  898.                 'name' => $offer->getTitle(),
  899.                 'label' => $offer->getCompanyName(),
  900.                 'imageURL' => 'https://www.slivki.by' $imageService->getImageURLCached($offer->getTeaserMedia() ?: null500324)
  901.             ];
  902.             /** @var GeoLocation $location */
  903.             foreach ($offer->getGeoLocations() as $location) {
  904.                 $item['locations'][] = [
  905.                     'latitude' => $location->getLatitude(),
  906.                     'longitude' => $location->getLongitude()
  907.                 ];
  908.             }
  909.             $result[] = $item;
  910.         }
  911.         $softCache->set($cacheKey$result12 60 60);
  912.         return $this->getResponseWithoutUser($result200);
  913.     }
  914.     /**
  915.      * Геолокация активных акций
  916.      * @Route("/mobile/api/offers/geo-locations", methods={"GET"});
  917.      * @OA\Response(
  918.      *     response=200,
  919.      *     description = "Геолокация активных акций",
  920.      *     @OA\Schema(
  921.      *        @OA\Property(property = "type", type = "varchar", description = "тип колекции"),
  922.      *        @OA\Property(property = "features", type = "object", description = "содержание коллекции",
  923.      *          @OA\Property(property = "type", type = "varchar", description = "тип локации"),
  924.      *          @OA\Property(property = "id", type = "integer", description = "ID локации"),
  925.      *          @OA\Property(property = "geometry", type = "object", description = "местоположение",
  926.      *              @OA\Property(property = "type", type = "varchar", description = "тип маркера"),
  927.      *              @OA\Property(property = "coordinates", type = "object", description = "массив координат  [53.914176,27.589859]"),
  928.      *          ),
  929.      *          @OA\Property(property = "properties", type = "object", description = "свойства",
  930.      *              @OA\Property(property = "iconClass", type = "varchar", description = "класс маркера"),
  931.      *              @OA\Property(property = "iconContent", type = "varchar", description = "содержание маркера"),
  932.      *              @OA\Property(property = "locationID", type = "integer", description = "ID локации"),
  933.      *              @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
  934.      *              @OA\Property(property = "offerTitle", type = "varchar", description = "название акции"),
  935.      *              @OA\Property(property = "teaserURL", type = "varchar", description = "тизер акции"),
  936.      *              @OA\Property(property = "url", type = "varchar", description = "url акции"),
  937.      *              @OA\Property(property = "hintContent", type = "varchar", description = "текст маркера"),
  938.      *          ),
  939.      *        ),
  940.      *        type="object",
  941.      *   )
  942.      * )
  943.      * @OA\Tag(name="Offer")
  944.      */
  945.     public function getOffersGeoLocations(ImageService $imageService) {
  946.         ini_set('memory_limit''8g');
  947.         $softCache = new SoftCache('mobapi-7');
  948.         $cacheKey 'geo-locations';
  949.         $result $softCache->get($cacheKey);
  950.         if ($result) {
  951.             return $this->getResponse($result200);
  952.         }
  953.         $entityManager $this->getDoctrine()->getManager();
  954.         $result = [];
  955.         $offerList $entityManager->getRepository(Offer::class)->getAllActiveOffersNoCache();
  956.         $offerRepository $this->getOfferRepository();
  957.         foreach ($offerList as $key=>$offer) {
  958.             if (!$offer->isHideInApp()) {
  959.                 $result[$key] = $offerRepository->getOfferGeoLocationData($offer, [], $imageServicefalse$this->getParameter('base_url'));
  960.             }
  961.         }
  962.         $softCache->set($cacheKey$result12 60 60);
  963.         return $this->getResponseWithoutUser($result200);
  964.     }
  965.     /**
  966.      * Геолокация активных акций по категории
  967.      * @Route("/mobile/api/offers/geo-locations/{categoryID}", methods={"GET"});
  968.      * @OA\Response(
  969.      *     response=200,
  970.      *     description = "Геолокация активных акций",
  971.      *     @OA\Schema(
  972.      *        @OA\Property(property = "type", type = "varchar", description = "тип колекции"),
  973.      *        @OA\Property(property = "features", type = "object", description = "содержание коллекции",
  974.      *          @OA\Property(property = "type", type = "varchar", description = "тип локации"),
  975.      *          @OA\Property(property = "id", type = "integer", description = "ID локации"),
  976.      *          @OA\Property(property = "geometry", type = "object", description = "местоположение",
  977.      *              @OA\Property(property = "type", type = "varchar", description = "тип маркера"),
  978.      *              @OA\Property(property = "coordinates", type = "object", description = "массив координат  [53.914176,27.589859]"),
  979.      *          ),
  980.      *          @OA\Property(property = "properties", type = "object", description = "свойства",
  981.      *              @OA\Property(property = "iconClass", type = "varchar", description = "класс маркера"),
  982.      *              @OA\Property(property = "iconContent", type = "varchar", description = "содержание маркера"),
  983.      *              @OA\Property(property = "locationID", type = "integer", description = "ID локации"),
  984.      *              @OA\Property(property = "offerID", type = "integer", description = "ID акции"),
  985.      *              @OA\Property(property = "offerTitle", type = "varchar", description = "название акции"),
  986.      *              @OA\Property(property = "teaserURL", type = "varchar", description = "тизер акции"),
  987.      *              @OA\Property(property = "offerType", type = "integer", description = "тип оффера. 0 обычный, 1 - онлайн ордер, 2 оноайл ордер без возможности купить код отдельно, 3 трайпл, 4 шм, 5 сертификаты"),
  988.      *              @OA\Property(property = "url", type = "varchar", description = "url акции"),
  989.      *              @OA\Property(property = "hintContent", type = "varchar", description = "текст маркера"),
  990.      *          ),
  991.      *        ),
  992.      *        type="object",
  993.      *   )
  994.      * )
  995.      * @OA\Tag(name="Offer")
  996.      */
  997.     public function getOffersGeoLocationsByCategory(
  998.         $categoryID,
  999.         ImageService $imageService,
  1000.         CacheService $cacheService
  1001.     ): JsonResponse {
  1002.         ini_set('memory_limit''8g');
  1003.         $softCache = new SoftCache('mobapi-7');
  1004.         $cacheKey 'geo-locations-by-category' $categoryID;
  1005.         $result $softCache->get($cacheKey);
  1006.         if ($result) {
  1007.             return $this->getResponseWithoutUser($result200);
  1008.         }
  1009.         $entityManager $this->getDoctrine()->getManager();
  1010.         $result = [];
  1011.         $offerList $entityManager->getRepository(Offer::class)->getActiveOffersByCategoryID($categoryID);
  1012.         $offerRepository $this->getOfferRepository();
  1013.         foreach ($offerList as $key=>$offer) {
  1014.             if (!$offer->isHideInApp()) {
  1015.                 $offer->setGeoLocation($cacheService->getGeoLocations(GeoLocation::TYPEGeoLocation::class, $offer->getID()));
  1016.                 $result[$key] = $offerRepository->getOfferGeoLocationData($offer, [], $imageServicefalse$this->getParameter('base_url'));
  1017.             }
  1018.         }
  1019.         $softCache->set($cacheKey$result12 60 60);
  1020.         return $this->getResponseWithoutUser($result200);
  1021.     }
  1022.     /**
  1023.      * Получение бесплатного кода трайпл
  1024.      * @Route("/mobile/api/azs-triple/get-code", methods={"POST"});
  1025.      * @OA\Response(
  1026.      *     response=200,
  1027.      *     description = "Получение бесплатного кода трайпл",
  1028.      *     @OA\Schema(
  1029.      *        @OA\Property(property = "code", type = "varchar", description = "Промокод"),
  1030.      *        @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
  1031.      *        @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
  1032.      *        type="object",
  1033.      *        example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
  1034.      *   )
  1035.      * )
  1036.      * @OA\Response(
  1037.      *     response = 400,
  1038.      *     description = "Wrong data"
  1039.      * )
  1040.      * @OA\Response(
  1041.      *     response = 403,
  1042.      *     description = "Лимит бесплатных кодов исчерпан"
  1043.      * )
  1044.      * @OA\Response(
  1045.      *     response = 404,
  1046.      *     description = "Юзер не найден"
  1047.      * )
  1048.      * @OA\RequestBody(
  1049.      *     required=true,
  1050.      *     @OA\JsonContent(
  1051.      *         @OA\Schema (
  1052.      *              type="object",
  1053.      *              @OA\Property(
  1054.      *                  property="carNumber",
  1055.      *                  required=true,
  1056.      *                  description="Номер авто",
  1057.      *                  @OA\Schema(type="string"),
  1058.      *              ),
  1059.      *              @OA\Property(
  1060.      *                  property="phoneNumber",
  1061.      *                  required=true,
  1062.      *                  description="Номер телефона",
  1063.      *                  @OA\Schema(type="string"),
  1064.      *              ),
  1065.      *         ),
  1066.      *     ),
  1067.      * ),
  1068.      * @OA\Parameter(
  1069.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  1070.      *     in = "header",
  1071.      *     description = "Токен юзера",
  1072.      *     required = true,
  1073.      *     @OA\Schema(type="string"),
  1074.      *)
  1075.      * @OA\Tag(name="Offer")
  1076.      */
  1077.     public function getPetrolCodeAction(
  1078.         Request $request,
  1079.         OfferCacheService $offerCacheService,
  1080.         PaymentService $paymentService,
  1081.         UserGetter $userGetter
  1082.     ) {
  1083.         $user $userGetter->get();
  1084.         $data json_decode($request->getContent());
  1085.         $carNumber $data->carNumber;
  1086.         $phoneNumber $data->phoneNumber;
  1087.         $codesCount 1;
  1088.         $result = [];
  1089.         if (mb_strlen($carNumber) != 4) {
  1090.             $result['error'] = true;
  1091.             $result['message'] = 'Введите номер авто';
  1092.             return $this->getResponse($result400$result['message']);
  1093.         }
  1094.         if (mb_strlen($phoneNumber) != 12) {
  1095.             $result['error'] = true;
  1096.             $result['message'] = 'Введите номер телефона';
  1097.             return $this->getResponse($result400$result['message']);
  1098.         }
  1099.         $offer $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
  1100.         $entityManager $this->getDoctrine()->getManager();
  1101.         $offerRepository $entityManager->getRepository(Offer::class);
  1102.         if (!$offerRepository->isOfferFreeForUser($offer$user)) {
  1103.             return $this->getResponse(['error' => true'message' => 'Вы не можете получить еще один бесплатный код сегодня.'], 403'Вы не можете получить еще один бесплатный код сегодня.');
  1104.         }
  1105.         $offerOrder $this->createNewOfferOrder(
  1106.             $request,
  1107.             Offer::PETROL_OFFER_ID,
  1108.             $codesCount,
  1109.             $user
  1110.         );
  1111.         if (!$offerOrder) {
  1112.             return $this->getResponse([
  1113.                 'error' => true,
  1114.                 'message' => 'Акция не действительна.'
  1115.             ], 403'Акция не действительна.');
  1116.         }
  1117.         $orderOptionList = [
  1118.             ['name' => EntityOption::OPTION_CAR_NUMBER'value' => $carNumber],
  1119.             ['name' => EntityOption::OPTION_PHONE_NUMBER'value' => $phoneNumber]
  1120.         ];
  1121.         foreach ($orderOptionList as $item) {
  1122.             $orderOption = new EntityOption();
  1123.             $orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
  1124.             $orderOption->setEntityID($offerOrder->getID());
  1125.             $orderOption->setName($item['name']);
  1126.             $orderOption->setValue($item['value']);
  1127.             $entityManager->persist($orderOption);
  1128.         }
  1129.         $entityManager->flush();
  1130.         $codeCost $offerRepository->getCodeCost($offer);
  1131.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1132.         if ($offerFreeForUser || $user->getFullBalance() >= $codeCost $codesCount) {
  1133.             $codeList $paymentService->createCode($offerOrder$codesCounttrue);
  1134.             if(empty($codeList)) {
  1135.                 return $this->getResponse([], 404);
  1136.             }
  1137.             $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1138.             return $this->getResponse([
  1139.                 'code' => $codeList[0],
  1140.                 'enoughBalance' => $user->getFullBalance() >= $codeCost,
  1141.                 'moreFree' => $offerFreeForUser
  1142.             ], 200);
  1143.         }
  1144.         return $this->getResponse([], 403);
  1145.     }
  1146.     /**
  1147.      * Покупка кода трайпл через баланс
  1148.      * @Route("/mobile/api/azs-triple/buy/code/balance", methods={"POST"});
  1149.      * @OA\Response(
  1150.      *     response=200,
  1151.      *     description = "Покупка кода трайпл",
  1152.      *     @OA\Schema(
  1153.      *        @OA\Property(property = "code", type = "varchar", description = "Промокод"),
  1154.      *        @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
  1155.      *        @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
  1156.      *        type="object",
  1157.      *        example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
  1158.      *   )
  1159.      * )
  1160.      * @OA\Response(
  1161.      *     response = 400,
  1162.      *     description = "Wrong data"
  1163.      * )
  1164.      * @OA\Response(
  1165.      *     response = 403,
  1166.      *     description = "Недостаточно денег на балансе"
  1167.      * )
  1168.      * @OA\Response(
  1169.      *     response = 404,
  1170.      *     description = "Юзер не найден"
  1171.      * )
  1172.      * @OA\RequestBody(
  1173.      *     required=true,
  1174.      *     @OA\JsonContent(
  1175.      *         @OA\Schema (
  1176.      *              type="object",
  1177.      *              @OA\Property(
  1178.      *                  property="carNumber",
  1179.      *                  required=true,
  1180.      *                  description="Номер авто",
  1181.      *                  @OA\Schema(type="string"),
  1182.      *              ),
  1183.      *              @OA\Property(
  1184.      *                  property="phoneNumber",
  1185.      *                  required=true,
  1186.      *                  description="Номер телефона",
  1187.      *                  @OA\Schema(type="string"),
  1188.      *              ),
  1189.      *         ),
  1190.      *     ),
  1191.      * ),
  1192.      * @OA\Parameter(
  1193.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  1194.      *     in = "header",
  1195.      *     description = "Токен юзера",
  1196.      *     required = true,
  1197.      *     @OA\Schema(type="string"),
  1198.      *)
  1199.      * @OA\Tag(name="Offer")
  1200.      */
  1201.     public function buyPetrolCodeBalanceAction(
  1202.         Request $request,
  1203.         OfferCacheService $offerCacheService,
  1204.         PaymentService $paymentService,
  1205.         SubscriptionService $subscriptionService,
  1206.         UserGetter $userGetter
  1207.     ) {
  1208.         $user $userGetter->get();
  1209.         $data json_decode($request->getContent());
  1210.         $carNumber $data->carNumber;
  1211.         $phoneNumber $data->phoneNumber;
  1212.         $codesCount 1;
  1213.         $result = [];
  1214.         if (mb_strlen($carNumber) != 4) {
  1215.             $result['error'] = true;
  1216.             $result['message'] = 'Введите номер авто';
  1217.             return $this->getResponse($result400$result['message']);
  1218.         }
  1219.         if (mb_strlen($phoneNumber) != 12) {
  1220.             $result['error'] = true;
  1221.             $result['message'] = 'Введите номер телефона';
  1222.             return $this->getResponse($result400$result['message']);
  1223.         }
  1224.         $offer $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
  1225.         $entityManager $this->getDoctrine()->getManager();
  1226.         $offerRepository $entityManager->getRepository(Offer::class);
  1227.         $codeCost $offerRepository->getCodeCost($offer);
  1228.         $amount $codeCost $codesCount;
  1229.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1230.         $offerOrder $this->createNewOfferOrder(
  1231.             $request,
  1232.             Offer::PETROL_OFFER_ID,
  1233.             $codesCount,
  1234.             $user,
  1235.             SiteController::DEVICE_TYPE_MOBILE_APP,
  1236.             OfferOrder::METHOD_BALANCE
  1237.         );
  1238.         if (!$offerOrder) {
  1239.             return $this->getResponse([
  1240.                 'error' => true,
  1241.                 'message' => 'Акция не действительна.'
  1242.             ], 403'Акция не действительна.');
  1243.         }
  1244.         $isSubscriber $subscriptionService->isSubscriber($user);
  1245.         if (!$offerFreeForUser && !$isSubscriber && $amount $user->getFullBalance()) {
  1246.             return $this->getResponse([
  1247.                 'error' => true,
  1248.                 'message' => 'Недостаточно денег на балансе.'
  1249.             ], 403'Недостаточно денег на балансе.');
  1250.         }
  1251.         $orderOptionList = [
  1252.             ['name' => EntityOption::OPTION_CAR_NUMBER'value' => $carNumber],
  1253.             ['name' => EntityOption::OPTION_PHONE_NUMBER'value' => $phoneNumber]
  1254.         ];
  1255.         foreach ($orderOptionList as $item) {
  1256.             $orderOption = new EntityOption();
  1257.             $orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
  1258.             $orderOption->setEntityID($offerOrder->getID());
  1259.             $orderOption->setName($item['name']);
  1260.             $orderOption->setValue($item['value']);
  1261.             $entityManager->persist($orderOption);
  1262.         }
  1263.         $entityManager->flush();
  1264.         $codeList $paymentService->createCode($offerOrder$codesCounttrue);
  1265.         if (empty($codeList)) {
  1266.             return $this->getResponse([], 405);
  1267.         }
  1268.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1269.         return $this->getResponse([
  1270.             'code' => $codeList[0],
  1271.             'enoughBalance' => $user->getFullBalance() >= $codeCost,
  1272.             'moreFree' => $offerFreeForUser
  1273.         ], 200);
  1274.     }
  1275.     /**
  1276.      * Покупка кода трайпл через bepaid
  1277.      * @Route("/mobile/api/azs-triple/buy/code/card", methods={"POST"});
  1278.      * @OA\Response(
  1279.      *     response=200,
  1280.      *     description = "Покупка кода трайпл",
  1281.      *     @OA\Schema(
  1282.      *        @OA\Property(property = "code", type = "varchar", description = "Промокод"),
  1283.      *        @OA\Property(property = "enoughBalance", type = "boolean", description = "достаточно ли средств на счету для покупки еще одного кода"),
  1284.      *        @OA\Property(property = "moreFree", type = "bool", description = "Доступен ли еще халявный код"),
  1285.      *        type="object",
  1286.      *        example = { "message": "Ваш промокод: <span>52-467</span><br>", "enoughBalance": true, "moreFree": true }
  1287.      *   )
  1288.      * )
  1289.      * @OA\Response(
  1290.      *     response = 400,
  1291.      *     description = "Wrong data"
  1292.      * )
  1293.      * @OA\Response(
  1294.      *     response = 401,
  1295.      *     description = "Ошибка оплаты"
  1296.      * )
  1297.      * @OA\Response(
  1298.      *     response = 402,
  1299.      *     description = "Карта не найдена"
  1300.      * )
  1301.      * @OA\Response(
  1302.      *     response = 403,
  1303.      *     description = "Акция не действительна"
  1304.      * )
  1305.      * @OA\Response(
  1306.      *     response = 404,
  1307.      *     description = "Юзер не найден"
  1308.      * )
  1309.      * @OA\RequestBody(
  1310.      *     required=true,
  1311.      *     @OA\JsonContent(
  1312.      *         @OA\Schema (
  1313.      *              type="object",
  1314.      *              @OA\Property(
  1315.      *                  property="carNumber",
  1316.      *                  required=true,
  1317.      *                  description="Номер авто",
  1318.      *                  @OA\Schema(type="string"),
  1319.      *              ),
  1320.      *              @OA\Property(
  1321.      *                  property="phoneNumber",
  1322.      *                  required=true,
  1323.      *                  description="Номер телефона",
  1324.      *                  @OA\Schema(type="string"),
  1325.      *              ),
  1326.      *         ),
  1327.      *     ),
  1328.      * ),
  1329.      * @OA\Parameter(
  1330.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  1331.      *     in = "header",
  1332.      *     description = "Токен юзера",
  1333.      *     required = true,
  1334.      *     @OA\Schema(type="string"),
  1335.      *)
  1336.      * @OA\Tag(name="Offer")
  1337.      */
  1338.     public function buyPetrolCodeCardAction(
  1339.         Request $request,
  1340.         OfferCacheService $offerCacheService,
  1341.         BePaidService $bePaidService,
  1342.         PaymentService $paymentService,
  1343.         UserGetter $userGetter
  1344.     ) {
  1345.         $user $userGetter->get();
  1346.         $data json_decode($request->getContent());
  1347.         $carNumber $data->carNumber;
  1348.         $phoneNumber $data->phoneNumber;
  1349.         $codesCount 1;
  1350.         $result = [];
  1351.         if (mb_strlen($carNumber) != 4) {
  1352.             $result['error'] = true;
  1353.             $result['message'] = 'Введите номер авто';
  1354.             return $this->getResponse($result400$result['message']);
  1355.         }
  1356.         if (mb_strlen($phoneNumber) != 12) {
  1357.             $result['error'] = true;
  1358.             $result['message'] = 'Введите номер телефона';
  1359.             return $this->getResponse($result400$result['message']);
  1360.         }
  1361.         $offer $offerCacheService->getOffer(Offer::PETROL_OFFER_ID);
  1362.         $entityManager $this->getDoctrine()->getManager();
  1363.         $offerRepository $entityManager->getRepository(Offer::class);
  1364.         $codeCost $offerRepository->getCodeCost($offer);
  1365.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1366.         $offerOrder $this->createNewOfferOrder(
  1367.             $request,
  1368.             Offer::PETROL_OFFER_ID,
  1369.             $codesCount,
  1370.             $user,
  1371.             SiteController::DEVICE_TYPE_MOBILE_APP
  1372.         );
  1373.         if (!$offerOrder) {
  1374.             return $this->getResponse(['error' => true'message' => 'Акция не действительна.'], 403'Акция не действительна.');
  1375.         }
  1376.         $orderOptionList = [
  1377.             ['name' => EntityOption::OPTION_CAR_NUMBER'value' => $carNumber],
  1378.             ['name' => EntityOption::OPTION_PHONE_NUMBER'value' => $phoneNumber]
  1379.         ];
  1380.         foreach ($orderOptionList as $item) {
  1381.             $orderOption = new EntityOption();
  1382.             $orderOption->setEntityTypeID(EntityOption::ORDER_ENTITY_TYPE);
  1383.             $orderOption->setEntityID($offerOrder->getID());
  1384.             $orderOption->setName($item['name']);
  1385.             $orderOption->setValue($item['value']);
  1386.             $entityManager->persist($orderOption);
  1387.         }
  1388.         $entityManager->flush();
  1389.         if (!$offerFreeForUser) {
  1390.             $creditCard $entityManager->find(CreditCard::class, $data->cardID);
  1391.             if (!$creditCard || !$creditCard->isOwner($user->getID())) {
  1392.                 return $this->getResponse([],402);
  1393.             }
  1394.             $result $bePaidService->checkoutByToken($offerOrder$creditCard->getID());
  1395.             if (!$result) {
  1396.                 return $this->getResponse([],401);
  1397.             }
  1398.             $bePaidService->createBePaidPaiment($offerOrder$result);
  1399.             $entityManager->flush();
  1400.         }
  1401.         $codeList $paymentService->createCode($offerOrder$codesCount,false);
  1402.         if (empty($codeList)) {
  1403.             return $this->getResponse([], 405);
  1404.         }
  1405.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1406.         return $this->getResponse([
  1407.             'code' => $codeList[0],
  1408.             'enoughBalance' => $user->getFullBalance() >= $codeCost,
  1409.             'moreFree' => $offerFreeForUser
  1410.         ], 200);
  1411.     }
  1412.     /**
  1413.      * Бесплатный ли код
  1414.      * @Route("/mobile/api/offer/is-free", methods={"POST"});
  1415.      * @OA\Response(
  1416.      *     response=200,
  1417.      *     description = "Бесплатный ли код для юзера",
  1418.      *     @OA\Schema(
  1419.      *        @OA\Property(property = "isOfferFree", type = "boolean", description = "Бесплатный ли код для юзера"),
  1420.      *        type="object",
  1421.      *   )
  1422.      * )
  1423.      * @OA\Response(
  1424.      *     response = 404,
  1425.      *     description = "Юзер не найден"
  1426.      * )
  1427.      * @OA\RequestBody(
  1428.      *     required=true,
  1429.      *     @OA\JsonContent(
  1430.      *         @OA\Schema (
  1431.      *              type="object",
  1432.      *              @OA\Property(
  1433.      *                  property="offerID",
  1434.      *                  required=true,
  1435.      *                  description="id акции",
  1436.      *                  @OA\Schema(type="integer"),
  1437.      *              ),
  1438.      *         ),
  1439.      *     ),
  1440.      * ),
  1441.      * @OA\Parameter(
  1442.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  1443.      *     in = "header",
  1444.      *     description = "Токен юзера",
  1445.      *     required = true,
  1446.      *     @OA\Schema(type="string"),
  1447.      *)
  1448.      * @OA\Tag(name="Offer")
  1449.      */
  1450.     public function isOfferFreeAction(
  1451.         Request $request,
  1452.         OfferCacheService $offerCacheService,
  1453.         UserGetter $userGetter
  1454.     ): JsonResponse {
  1455.         $user $userGetter->get();
  1456.         $data json_decode($request->getContent());
  1457.         $offerID $data->offerID;
  1458.         $offer $offerCacheService->getOffer($offerID);
  1459.         $entityManager $this->getDoctrine()->getManager();
  1460.         $offerRepository $entityManager->getRepository(Offer::class);
  1461.         $offerFreeForUser $offerRepository->isOfferFreeForUser($offer$user);
  1462.         return $this->getResponse(['isOfferFree' => $offerFreeForUser], 200);
  1463.     }
  1464.     /**
  1465.      * Список адресов
  1466.      * @Route("/mobile/api/v2/geo-locations/{offerID}", methods={"GET"});
  1467.      * @OA\Tag(name="Geo locations")
  1468.      * @OA\Response(
  1469.      *     response=200,
  1470.      *     description = "Список адресов",
  1471.      *     @OA\Schema(
  1472.      *        @OA\Property(property = "places", type = "object", description = "Список адресов",
  1473.      *          @OA\Property(property = "description", type = "string", description = "Описание"),
  1474.      *          @OA\Property(property = "city", type = "string", description = "Город"),
  1475.      *          @OA\Property(property = "street", type = "string", description = "Улица"),
  1476.      *          @OA\Property(property = "house", type = "string", description = "Дом"),
  1477.      *          @OA\Property(property = "latitude", type = "string", description = "Широта"),
  1478.      *          @OA\Property(property = "longitude", type = "string", description = "Долгота"),
  1479.      *          @OA\Property(property = "workingHours", type = "string", description = "Время работы"),
  1480.      *          @OA\Property(property = "id", type = "iteger", description = "ID"),
  1481.      *         ),
  1482.      *     )
  1483.      * )
  1484.      * @OA\Parameter(
  1485.      *     name="offerID",
  1486.      *     in="path",
  1487.      *     description="ID акции",
  1488.      *     @OA\Schema(type="string"),
  1489.      * )
  1490.      */
  1491.     public function getGeoLocationsInfoAction(
  1492.         OfferCacheService $offerCacheService,
  1493.         GeoLocationService $geoLocationService,
  1494.         PhoneService $phoneService,
  1495.         $offerID
  1496.     ) {
  1497.         $entityManager $this->getDoctrine()->getManager();
  1498.         $offer $offerCacheService->getOffer($offerIDfalsetrue);
  1499.         if (!$offer) {
  1500.             $offer $entityManager->find(Offer::class, $offerID);
  1501.         }
  1502.         return $this->getResponseWithoutUser([
  1503.             'places' => $geoLocationService->getPlace($offer),
  1504.         ], 200);
  1505.     }
  1506.     /**
  1507.      * Список телефонов
  1508.      * @Route("/mobile/api/v2/phone-numbers/{offerID}", methods={"GET"});
  1509.      * @OA\Tag(name="Phone numbers")
  1510.      * @OA\Response(
  1511.      *     response=200,
  1512.      *     description = "Список телефонов",
  1513.      *     @OA\Schema(
  1514.      *        @OA\Property(property = "phones", type = "object", description = "Список телефонов",
  1515.      *          @OA\Property(property = "number", type = "string", description = "Номер"),
  1516.      *          @OA\Property(property = "link", type = "string", description = "Линк"),
  1517.      *          @OA\Property(property = "label", type = "string", description = "Ярлык"),
  1518.      *          @OA\Property(property = "id", type = "integer", description = "ID"),
  1519.      *          @OA\Property(property = "geoLocationId", type = "integer", description = "geo location id"),
  1520.      *         ),
  1521.      *     )
  1522.      * )
  1523.      * @OA\Parameter(
  1524.      *     name="offerID",
  1525.      *     in="path",
  1526.      *     description="ID акции",
  1527.      *     @OA\Schema(type="string"),
  1528.      * )
  1529.      */
  1530.     public function getPhoneNumbersInfoAction(
  1531.         OfferCacheService $offerCacheService,
  1532.         PhoneService $phoneService,
  1533.         $offerID
  1534.     ) {
  1535.         $entityManager $this->getDoctrine()->getManager();
  1536.         $offer $offerCacheService->getOffer($offerIDfalsetrue);
  1537.         if (!$offer) {
  1538.             $offer $entityManager->find(Offer::class, $offerID);
  1539.         }
  1540.         return $this->getResponseWithoutUser([
  1541.             'phones' => $phoneService->getPhone($offer),
  1542.         ], 200);
  1543.     }
  1544.     /**
  1545.      * Список акций по категории
  1546.      * @Route("/mobile/api/v2/oplati/category/{categoryID}/{pageNumber}", methods={"GET"});
  1547.      * @OA\Response(
  1548.      *     response = 200,
  1549.      *     description = "Список акций по категории",
  1550.      *     @OA\Schema(
  1551.      *        @OA\Property(property = "offers", type = "object", description = "акции",
  1552.      *          @OA\Property(property = "ID", type = "integer", description = "ID акции"),
  1553.      *          @OA\Property(property = "name", type = "varchar", description = "название акции"),
  1554.      *          @OA\Property(property = "regularPrice", type = "decimal", description = "обычная цена"),
  1555.      *          @OA\Property(property = "offerPrice", type = "decimal", description = "цена со скидкой"),
  1556.      *          @OA\Property(property = "discountPercent", type = "varchar", description = "процент скидки"),
  1557.      *          @OA\Property(property = "saleCount", type = "integer", description = "количество проданных кодов"),
  1558.      *          @OA\Property(property = "imageURL", type = "varchar", description = "URL изображения для тизера"),
  1559.      *          @OA\Property(property = "verticalImageUrl", type = "varchar", description = "URL вертикального изображения для тизера"),
  1560.      *          @OA\Property(property = "activeTill", type = "datetime", description = "дата окончания акции"),
  1561.      *          @OA\Property(property = "rating", type = "decimal", description = "рейтинг акции"),
  1562.      *          @OA\Property(property = "codeCost", type = "varchar", description = "цена кода"),
  1563.      *          @OA\Property(property = "phoneNumbersWithoutLocation", type = "object", description = "номера телефонов без местоположения",
  1564.      *              @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  1565.      *              @OA\Property(property = "label", type = "varchar", description = "метка"),
  1566.      *          ),
  1567.      *          @OA\Property(property = "locations", type = "object", description = "местоположение",
  1568.      *              @OA\Property(property = "address", type = "varchar", description = "адрес"),
  1569.      *              @OA\Property(property = "workingHours", type = "text", description = "время роботы"),
  1570.      *              @OA\Property(property = "phoneNumbers", type = "object", description = "номера телефонов",
  1571.      *                  @OA\Property(property = "phoneNumber", type = "varchar", description = "номер телефона"),
  1572.      *                  @OA\Property(property = "label", type = "varchar", description = "метка"),
  1573.      *              ),
  1574.      *              @OA\Property(property = "description", type = "varchar", description = "описание"),
  1575.      *              @OA\Property(property = "geoLocation", type = "varchar", description = "массив координат"),
  1576.      *          ),
  1577.      *          @OA\Property(property = "visitCount", type = "integer", description = "количество посещений"),
  1578.      *          @OA\Property(property = "address", type = "varchar", description = "адрес"),
  1579.      *          @OA\Property(property = "offerType", type = "integer", description = "тип акции"),
  1580.      *          @OA\Property(property = "isFreeCode", type = "boolean", description = "бесплатный ли код"),
  1581.      *          @OA\Property(property = "companyLogoImage", type = "object", description = "URL лого компании"),
  1582.      *        ),
  1583.      *        @OA\Property(property = "isLast", type = "boolean", description = "последняя ли страница"),
  1584.      *        @OA\Property(property = "offersCount", type = "integer", description = "количество акций"),
  1585.      *        @OA\Property(property = "userBalance", type = "varchar", description = "баланс пользователя"),
  1586.      *        type="object",
  1587.      *     )
  1588.      * )
  1589.      * @OA\Parameter(
  1590.      *     name = "categoryID",
  1591.      *     in = "path",
  1592.      *     description = "ID категории",
  1593.      *     required = true,
  1594.      *     @OA\Schema(type="integer"),
  1595.      *)
  1596.      * @OA\Parameter(
  1597.      *     name = "pageNumber",
  1598.      *     in = "path",
  1599.      *     description = "номер страницы",
  1600.      *     required = true,
  1601.      *     @OA\Schema(type="integer"),
  1602.      *)
  1603.      * @OA\Parameter(
  1604.      *     name = "HTTP-SLIVKi-USER-TOKEN",
  1605.      *     in = "header",
  1606.      *     description = "Токен юзера",
  1607.      *     required = true,
  1608.      *     @OA\Schema(type="string"),
  1609.      *)
  1610.      * @OA\Tag(name="Category")
  1611.      */
  1612.     public function getOffersForOplatiAction(
  1613.         OfferCacheService $offerCacheService,
  1614.         MobApiCacheService $mobApiCacheService,
  1615.         $categoryID,
  1616.         $pageNumber
  1617.     ) {
  1618.         ini_set('memory_limit''4g');
  1619.         $offers = [];
  1620.         $tmpPageNumber 1;
  1621.         do {
  1622.             $result $mobApiCacheService->getOffersByCategoryIDCached($categoryID$tmpPageNumber);
  1623.             $offers array_merge($offers$result['offers']);
  1624.             $tmpPageNumber++;
  1625.         } while (!$result['isLast']);
  1626.         $offerRepository $this->getDoctrine()->getRepository(Offer::class);
  1627.         foreach ($offers as $key => &$offerArray) {
  1628.             $offer $offerCacheService->getOffer($offerArray['ID']);
  1629.             if ($offer) {
  1630.                 if ($offer->isBuyCodeDisable() || $offer->isHideInApp() || !$offer->isInActivePeriod()) {
  1631.                     unset($offers[$key]);
  1632.                     continue;
  1633.                 }
  1634.                 $isFreeCode $offerRepository->isOfferFreeForUser($offer$this->getUser());
  1635.                 if ($isFreeCode) {
  1636.                     unset($offers[$key]);
  1637.                     continue;
  1638.                 }
  1639.                 $offerArray['isFreeCode'] = $isFreeCode;
  1640.             }
  1641.         }
  1642.         $offersCount count($offers);
  1643.         $offset = ($pageNumber 1) * MobApiCacheService::OFFERS_PER_PAGE;
  1644.         $offersSlice array_slice($offers$offsetMobApiCacheService::OFFERS_PER_PAGE);
  1645.         return $this->getResponse([
  1646.             'offersCount' => $offersCount,
  1647.             'isLast' => ($offset MobApiCacheService::OFFERS_PER_PAGE) >= $offersCount,
  1648.             'offers' => $offersSlice
  1649.         ], 200);
  1650.     }
  1651. }