<?php
namespace Slivki\Controller;
use libphonenumber\PhoneNumberUtil;
use Slivki\BusinessFeature\VirtualWallet\Service\VirtualWalletChecker;
use Slivki\Dao\GiftCertificate\GiftCertificateDaoInterface;
use Slivki\Entity\Category;
use Slivki\Entity\Comment;
use Slivki\Entity\FoodFilterCounter;
use Slivki\Entity\GiftCertificate;
use Slivki\Entity\GiftCertificateOrder;
use Slivki\Entity\Media;
use Slivki\Entity\Offer;
use Slivki\Entity\OfferOrderDetails;
use Slivki\Entity\Seo;
use Slivki\Entity\User;
use Slivki\Entity\UserBalanceActivity;
use Slivki\Entity\Visit;
use Slivki\Enum\Order\PaymentType;
use Slivki\Exception\Order\InsufficientBalanceFundsException;
use Slivki\Repository\PurchaseCount\PurchaseCountRepositoryInterface;
use Slivki\Repository\SeoRepository;
use Slivki\Repository\User\CreditCardRepositoryInterface;
use Slivki\Services\GiftCertificate\SplitPaymentGiftCertificateChecker;
use Slivki\Services\GiftCertificateService;
use Slivki\Services\Offer\CustomProductOfferSorter;
use Slivki\Services\Offer\GiftCertificateSorter;
use Slivki\Services\Offer\OfferCacheService;
use Slivki\Services\PartnerBePaidService;
use Slivki\Services\Payment\OnlineOrderPaymentMethodService;
use Slivki\Services\Payment\PaymentService;
use Slivki\Services\Seo\SeoResourceService;
use Slivki\Services\Subscription\SubscriptionService;
use Slivki\Util\CommonUtil;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use function in_array;
class GiftCertificatesController extends SiteController
{
/**
* @Route("/gift-certificate/select/{offerID}", name="giftCertificates")
*/
public function giftCertificatesAction(
Request $request,
OfferCacheService $offerCacheService,
GiftCertificateService $giftCertificateService,
GiftCertificateSorter $giftCertificateSorter,
SeoResourceService $seoResourceService,
SubscriptionService $subscriptionService,
GiftCertificateDaoInterface $giftCertificateDao,
PurchaseCountRepositoryInterface $purchaseCountRepository,
$offerID
): Response {
$response = new Response();
$entityManager = $this->getDoctrine()->getManager();
$offer = $offerCacheService->getOffer($offerID, false, true);
if (!$offer) {
$offer = $entityManager->find(Offer::class, $offerID);
}
if (null === $offer || !$offer->hasFreeCodes() || 0 === $giftCertificateDao->getCountActiveByOfferId($offerID)) {
return $this->redirect($seoResourceService->getOfferSeo($offerID)->getMainAlias());
}
$ratingData = $entityManager->getRepository(Comment::class)->getEntityRatingWithCount(Category::OFFER_CATEGORY_ID, $offerID);
$certificates = $giftCertificateService->getCertificates($offer);
$user = $this->getUser();
$sortType = $request->query->get('sort', CustomProductOfferSorter::DEFAULT_CUSTOM_PRODUCT_SORT);
$certificates = $giftCertificateSorter->sort($certificates, $sortType);
$purchaseCount = $purchaseCountRepository->findByOfferId((int) $offerID);
$data = [
'dishes' => $certificates,
'options' => [],
'offer' => $offer,
'rating' => $ratingData['rating'] * 100 / 5,
'ratingCount' => $ratingData['ratingCount'],
'codeCostRegular' => 0,
'minSumForFreeDelivery' => 0,
'minOrderSum' => 0,
'showDelivery' => false,
'isCertificate' => true,
'footerOfferConditionID' => $offerID,
'pickupLocations' => '',
'offerGeoLocationData' => '',
'defaultLocationID' => null,
'visitCount' => $entityManager->getRepository(Visit::class)->getVisitCount($offerID, Visit::TYPE_OFFER, 30, false),
'sortList' => $giftCertificateSorter::SORT_LIST,
'filterAction' => $entityManager->getRepository(FoodFilterCounter::class)->findOneBy(['entityID' => $offerID]),
'purchaseCountMonth' => null === $purchaseCount ? 0 : $purchaseCount->getPurchaseCountLastMonthWithCorrection(),
];
if (null !== $user) {
if ($subscriptionService->isSubscriber($user)) {
$data['allowedCodesToBuy'] = $subscriptionService->getSubscription($user)->getNumberOfCodes();
} elseif ($user->isBatchCodesAllowed()) {
$data['allowedCodesToBuyBatchCodes'] = $user->getBatchCodesCount();
}
}
$data['codeCost'] = 0;
if (Offer::SOSEDI_OFFER_ID === $offerID) {
$hasSosediPurchase = $giftCertificateService->hasSosediPurchase($user);
$data['isFirstSosediPurchase'] = !$hasSosediPurchase;
$data['hasSosediPurchaseForUser'] = $hasSosediPurchase;
}
$data['topDishIDList'] = [];
for ($i = 0; $i < 3; $i++) {
if (isset($crtPurchaseCount[$i]['id'])) {
$data['topDishIDList'][] = $crtPurchaseCount[$i]['id'];
}
}
$offerRepository = $entityManager->getRepository(Offer::class);
$data['director'] = $offerRepository->getDirector($offerID);
$data['logoMedia'] = $data['director']->getOnlinePaymentLogo();
if (!$data['logoMedia']) {
$data['logoMedia'] = $entityManager->getRepository(Media::class)->getDirectorLogo($data['director']->getID());
}
$data['formAction'] = '/gift-certificate/order/checkout';
$data['categoryName'] = 'Подарочные сертификаты';
$data['categoryURL'] = $entityManager->getRepository(Seo::class)->getSeoForEntity(SeoRepository::RESOURCE_URL_OFFER_CATEGORY, 24)->getMainAlias();
$view = CommonUtil::isMobileDevice($request) ? 'Slivki/mobile/delivery/order.html.twig' : 'Slivki/delivery/order.html.twig';
$response->setContent($this->renderView($view, $data));
return $response;
}
/**
* @Route("/gift-certificate/order/checkout", name="gift_certificate_order_checkout")
*/
public function checkoutAction(
Request $request,
GiftCertificateService $giftCertificateService,
PartnerBePaidService $partnerBePaidService,
SeoResourceService $seoResourceService,
SubscriptionService $subscriptionService,
CreditCardRepositoryInterface $creditCardRepository,
OnlineOrderPaymentMethodService $onlineOrderPaymentMethodService,
VirtualWalletChecker $virtualWalletChecker
): Response {
$response = new Response();
$offerID = $request->request->getInt('offerID');
$entityManager = $this->getDoctrine()->getManager();
$offer = $entityManager->find(Offer::class, $offerID);
if (!$offer->isActive() || !$offer->isInActivePeriod()) {
throw $this->createNotFoundException();
}
if (!$offer->hasFreeCodes()) {
return $this->redirect($seoResourceService->getOfferSeo($offerID)->getMainAlias());
}
$codeCost = $this->getOfferRepository()->getCodeCost($offer);
$counts = $request->request->get('count');
/** @var User $user */
$user = $this->getUser();
$codeCostRegular = $codeCost;
if (null !== $user && $subscriptionService->isSubscriber($user)) {
$codeCost = 0;
$allowedCodesCount = $subscriptionService->getSubscription($user)->getNumberOfCodes();
} elseif ($user->isBatchCodesAllowed()) {
$codeCost = 0;
}
$order = new GiftCertificateOrder();
$deviceType = CommonUtil::isMobileDevice($request) ? SiteController::DEVICE_TYPE_MOBILE : SiteController::DEVICE_TYPE_DESKTOP;
$order->setDeviceType($deviceType);
$order->setUser($this->getUser());
$order->setOffer($offer);
$codesCount = 0;
$totalAmount = 0;
$totalOfferAmount = 0;
$totalCodes = 0;
$items = [];
$isFreeStyle = in_array($offerID, Offer::FREESTYLE_OFFER_IDS, true);
$isSosedi = $offerID === 283793;
$hasSosediPurchase = false;
if ($isSosedi) {
$hasSosediPurchase = $giftCertificateService->hasSosediPurchase($user);
}
$isBuyOnlyCodeResponse = false;
$loopFirst = true;
foreach ($request->request->get('dishID') as $key => $certificateID) {
/** @var GiftCertificate $certificate */
$certificate = $entityManager->find(GiftCertificate::class, $certificateID);
if (!$certificate) {
continue;
}
if ($certificate->isBuyOnlyCode()) {
$isBuyOnlyCodeResponse = true;
}
$items[$key] = 0;
for ($i = 0; $i < $counts[$key]; $i++) {
if (
Offer::BLACK_START_BURGER_OFFER_ID !== $offer->getID()
&& isset($allowedCodesCount) && $totalCodes >= $allowedCodesCount
) {
break;
}
if ($isFreeStyle || $isSosedi) {
$codeTill = $offer->getCodeActiveTill()->setTime(23,59,59);
} else {
$codeTill = new \DateTime("+" . $certificate->getActiveDays() ." days");
}
$details = new OfferOrderDetails();
$details->setGiftCertificate($certificate);
$details->setItemsCount(1);
$details->setCodeActiveTill($codeTill);
$purchasePrice = $certificate->getPriceOffer();
if ($isSosedi) {
if (!$hasSosediPurchase && $loopFirst && $i == 0 && $certificate->getPrice() != 100 && $certificate->getPrice() != 50) {
$purchasePrice = $certificate->getPrice() * 0.9;
$loopFirst = false;
}
$totalOfferAmount += $purchasePrice;
}
$details->setPurchasePrice($purchasePrice);
$order->addOfferOrderDetails($details);
$totalCodes++;
$items[$key] += 1;
}
$codesCount += $certificate->getCodesPerItem() * $items[$key];
$totalAmount += $certificate->getPriceRegular() * $items[$key];
if (!$isSosedi) {
$totalOfferAmount += $certificate->getPriceOffer() * $items[$key];
}
}
$codesCount = ceil($codesCount);
$allowedCodesToBuyBalance = false;
if ($codeCost !== 0 && $user->isBalanceAllowed($codesCount * $codeCost)) {
$codeCost = 0;
$allowedCodesToBuyBalance = true;
}
$order->setAmount($totalOfferAmount + $codesCount * $codeCost);
$order->setCodeCost($codeCost);
$order->setCodesCount($codesCount);
$entityManager->persist($order);
$entityManager->flush();
$view = CommonUtil::isMobileDevice($request) ? 'Slivki/mobile/delivery/delivery_checkout.html.twig'
: 'Slivki/delivery/delivery_checkout.html.twig';
$allowedPaymentMethodsForOffer = $onlineOrderPaymentMethodService->getAllowedPaymentMethodsForGiftCertificateOffer($offer, $offerID);
$allowedPaymentMethods = [
'delivery' => [
PaymentType::ONLINE => 0,
PaymentType::CASH => 0,
PaymentType::TERMINAL => 0,
PaymentType::SLIVKI_PAY => 0,
],
'pickup' => [
PaymentType::ONLINE => (int) $allowedPaymentMethodsForOffer->isOnline(),
PaymentType::CASH => (int) $allowedPaymentMethodsForOffer->isCash(),
PaymentType::TERMINAL => (int) $allowedPaymentMethodsForOffer->isTerminal(),
PaymentType::SLIVKI_PAY => (int) $allowedPaymentMethodsForOffer->isSlivkiPay(),
],
];
$partnerBePaidService->setOrder($order);
$fullOrderAmount = $order->getAmount();
if ($allowedCodesToBuyBalance || $codeCost === 0) {
$fullOrderAmount += $codeCostRegular * $order->getCodesCount();
}
$data = [
'order' => $order,
'offer' => $offer,
'codeCost' => $codeCost,
'codeCostRegular' => $codeCostRegular,
'deliveryPrice' => 0,
'offerURL' => $entityManager->getRepository(Seo::class)->getOfferURL($offer->getID())->getMainAlias(),
'totalAmount' => $totalAmount,
'director' => $offer->getDirectors()->first(),
'robotsMeta' => 'noindex, follow',
'offerID' => $offerID,
'adresses' => null,
'showCheckAddressButton' => false,
'streetSuggest' => false,
'workHourFrom' => 0,
'workHourTo' => 0,
'showDelivery' => false,
'pickupEnabled' => false,
'deliveryEnabled' => false,
'pickupLocations' => '',
'defaultLocationID' => null,
'footerOfferConditionID' => $offerID,
'pickupDiscount' => 0,
'allowedPaymentMethods' => $allowedPaymentMethods,
'isGiftCertificate' => true,
'pickupDeliveryType' => null,
'activeCreditCards' => !$offer->isRecurrentDisabled() ? $creditCardRepository->findActiveByUser($user) : [],
'allowedCodesToBuyBalance' => $allowedCodesToBuyBalance,
'isBuyOnlyCodeResponse' => $isBuyOnlyCodeResponse,
'isSlivkiPayAllowed' => $virtualWalletChecker->availableSlivkiPayForCertificate($fullOrderAmount, $order, $user),
];
$data['categoryName'] = 'Подарочные сертификаты';
$data['categoryURL'] = $entityManager->getRepository(Seo::class)->getSeoForEntity(SeoRepository::RESOURCE_URL_OFFER_CATEGORY, 24)->getMainAlias();
$data['formAction'] = '/gift-certificate/order/pay';
$data['giftCertificate'] = true;
$content = $this->renderView($view, $data);
$response->setContent($content);
return $response;
}
/**
* @Route("/gift-certificate/order/pay", name="gift_certificate_order_pay")
*/
public function payAction(
Request $request,
PartnerBePaidService $partnerBePaidService,
PaymentService $paymentService,
CreditCardRepositoryInterface $creditCardRepository,
SplitPaymentGiftCertificateChecker $splitPaymentGiftCertificateChecker,
SubscriptionService $subscriptionService,
PhoneNumberUtil $phoneNumberUtil,
SeoResourceService $seoResourceService
): JsonResponse {
if (!$request->isMethod(Request::METHOD_POST)) {
throw $this->createNotFoundException();
}
$entityManager = $this->getDoctrine()->getManager();
$orderID = $request->request->getInt('orderID');
$order = $entityManager->find(GiftCertificateOrder::class, $orderID);
if (!$order || $order->getUser()->getID() != $this->getUser()->getID() || $order->getStatus() != GiftCertificateOrder::STATUS_INIT) {
throw $this->createNotFoundException();
}
$phone = $request->request->get('phone');
if (\mb_strlen($phone) > 0) {
$phoneNumberObject = $phoneNumberUtil->parse($phone);
if (!$phoneNumberUtil->isValidNumber($phoneNumberObject)) {
return new JsonResponse(['error' => true, 'message' => 'Введен некорректный номер телефона']);
}
}
$order->setComment($request->request->get('comment'));
$order->setUsername($request->request->get('name'));
$order->setUserPhone($phone);
$paymentMethod = $request->request->getInt('paymentMethod', PaymentType::ONLINE);
$order->setPaymentType($paymentMethod);
$user = $order->getUser();
$codeCost = $this->getOfferRepository()->getCodeCost($order->getOffer(), $order->getCodesCount());
$resultCodeCost = $codeCost;
$subscriber = false;
if ($subscriptionService->isSubscriber($user)) {
$subscriber = true;
$codeCost = 0;
}
$isBatchCodes = false;
if ($user->isBatchCodesAllowed()) {
$isBatchCodes = true;
$codeCost = 0;
}
if ($paymentMethod === PaymentType::SLIVKI_PAY) {
$codeCostForSlivkiPay = $order->getCodesCount() * (float) $resultCodeCost;
try {
$orderAmount = $order->getAmount();
if ($codeCost > 0 && !$user->isBalanceAllowed($codeCost * $order->getCodesCount())) {
$orderAmount -= $codeCostForSlivkiPay;
}
$paymentService->payOrder($user, $orderAmount, $codeCostForSlivkiPay);
$paymentService->createCode($order, $order->getCodesCount(), false);
} catch (InsufficientBalanceFundsException $exception) {
return new JsonResponse(['error' => true]);
}
return new JsonResponse(['error' => false]);
}
if ($paymentMethod > PaymentType::ONLINE) {
if ($subscriber || $isBatchCodes) {
$order->setAmount($codeCost);
$paymentService->createCode(
$order,
$order->getCodesCount(),
false,
null,
false,
$isBatchCodes ? UserBalanceActivity::TYPE_REDUCTION_BATCH_CODES : null
);
return new JsonResponse(['error' => false]);
}
$order->setAmount($order->getCodesCount() * $codeCost);
$entityManager->flush();
return new JsonResponse([
'redirectURL' => $this->redirectToRoute('buyCode', [
'offerID' => $order->getOffer()->getID(),
'codesCount' => $order->getCodesCount(),
'orderID' => $order->getID(),
])->getTargetUrl()
]);
}
if ($splitPaymentGiftCertificateChecker->isCheck($order)) {
$amount = 0;
foreach ($order->getOfferOrderDetails() as $orderDetails) {
$amount += $orderDetails->getPurchasePrice();
}
$order->setAmount($amount);
}
$partnerBePaidService->setOrder($order);
if ($user->isBalanceAllowed($codeCost * $order->getCodeCost())) {
$codeCost = 0;
}
$card = $creditCardRepository->findById($request->request->getInt('creditCardID'));
if (null === $card || !$card->isOwner($this->getUser()->getID())) {
$paymentToken = $partnerBePaidService->getPaymentToken($order, null, $codeCost);
if (!$paymentToken) {
return new JsonResponse(['error' => true]);
}
$partnerBePaidService->createBePaidPaiment($order, $paymentToken['checkout']['token']);
return new JsonResponse(['token' => $paymentToken['checkout']['token']]);
}
$amount = $order->getAmount();
$result = $partnerBePaidService->checkoutByToken($order, $card->getID(), $amount);
if (!$result) {
return new JsonResponse(['error' => true]);
}
if (is_array($result) && isset($result['token'])) {
return new JsonResponse(['token' => $result['token']]);
}
$partnerBePaidService->createBePaidPaiment($order, $result);
$successModalView = CommonUtil::isMobileDevice($request)
? 'Slivki/mobile/delivery/modal/success.html.twig'
: 'Slivki/delivery/modal/success.html.twig';
return new JsonResponse([
'error' => false,
'successModal' => $this->renderView(
$successModalView, [
'offerURL' => $seoResourceService->getOfferSeo($order->getOffer()->getID())->getMainAlias(),
],
),
]);
}
}