<?php
namespace FMT\Application\Controller\Common;
use Exception;
use FMT\Data\Entity\CampaignBook;
use FMT\Domain\Service\CartManagerInterface;
use FMT\Domain\Service\PaymentManagerInterface;
use FMT\Domain\Type\Payment\Settings;
use FMT\Domain\Type\Payment\Donation;
use FMT\Domain\Exception\CartActionException;
use FMT\Application\Controller\AbstractBaseController;
use FMT\Application\Controller\Student\DashboardController;
use FMT\Application\FormType\PaymentType;
use FMT\Application\Voter\TransactionVoter;
use FMT\Application\Traits\ControllerHelperTrait;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class CartController
* @package FMT\Application\Controller\Common
* @Route("/cart")
* @Template()
*/
class CartController extends AbstractBaseController
{
use ControllerHelperTrait;
const TRANSACTION_SESSION_KEY = "thank_you_txn_id";
const ROUTE_ADD = 'fmt-cart-add';
const ROUTE_REMOVE = 'fmt-cart-remove';
const ROUTE_INDEX = 'fmt-cart-index';
const ROUTE_CHECKOUT_THANK_YOU = "fmt-checkout-thank-you";
/** @var string */
private $token;
/** @var PaymentManagerInterface */
private $manager;
/**
* CartController constructor.
* @param Settings $settings
*/
public function __construct(Settings $settings)
{
$this->token = $settings->publicKey;
}
/**
* @param PaymentManagerInterface $manager
* @required
*/
public function setPaymentManager(PaymentManagerInterface $manager)
{
$this->manager = $manager;
}
/**
* @param CampaignBook $product
* @param CartManagerInterface $cartManager
*
* @ParamConverter("product", class="FMT\Data\Entity\CampaignBook")
* @Method({"POST"})
* @Route("/add/{product}",
* name=CartController::ROUTE_ADD,
* requirements={"product"="\d+"},
* options={"expose"=true},
* condition="request.isXmlHttpRequest()"
* )
* @return JsonResponse
*/
public function addAction(CampaignBook $product, CartManagerInterface $cartManager)
{
$isSchoolActive = $product->getCampaign()->getSchool()->isActive();
try {
if ($cartManager->hasProduct($product)) {
$this->addFlashBagError('fmt.cart.product.add.duplicate_item');
return $this->createFailureAjaxResponse(['is_school_active' => $isSchoolActive]);
}
if (!$cartManager->canAddProduct($product)) {
$this->addFlashBagError('fmt.cart.product.add.can_not_add_item');
return $this->createFailureAjaxResponse(['is_school_active' => $isSchoolActive]);
}
$cartManager->addProduct($product);
$cartManager->save();
$this->addFlashBagNotice('fmt.cart.product.add.success');
return $this->createSuccessAjaxResponse([
'summary' => $cartManager->getSummary()->getFormattedArray(),
]);
} catch (Exception $exception) {
$isSchoolActive = $product->getCampaign()->getSchool()->isActive();
$cartManager->removeProducts();
$cartManager->delete();
return $this->createFailureAjaxResponse(['is_school_active' => $isSchoolActive]);
}
}
/**
* @param CampaignBook $product
* @param CartManagerInterface $cartManager
*
* @ParamConverter("product", class="FMT\Data\Entity\CampaignBook")
* @Route("/remove/{product}",
* name=CartController::ROUTE_REMOVE,
* requirements={"product"="\d+"},
* options={"expose"=true},
* condition="request.isXmlHttpRequest()"
* )
* @return JsonResponse
*/
public function removeAction(CampaignBook $product, CartManagerInterface $cartManager)
{
$isSchoolActive = $product->getCampaign()->getSchool()->isActive();
if (!$cartManager->hasProduct($product)) {
$this->addFlashBagError('fmt.cart.product.remove.absent_item');
return $this->createFailureAjaxResponse();
}
$cartManager->removeProduct($product);
$cartManager->save();
$this->addFlashBagNotice('fmt.cart.product.remove.success');
return $this->createSuccessAjaxResponse([
'summary' => $cartManager->getSummary()->getFormattedArray(),
'is_school_active' => $isSchoolActive,
]);
}
/**
* @param Request $request
* @param CartManagerInterface $cartManager
*
* @Route("/", name=CartController::ROUTE_INDEX)
*
* @Security(
"is_granted('ROLE_DONOR') or
is_granted('ROLE_STUDENT') or
is_granted('ROLE_ADMIN') or
is_granted('ROLE_SUPER_ADMIN') or
is_granted('IS_AUTHENTICATED_ANONYMOUSLY')"
)
* @return array|RedirectResponse
*/
public function cartAction(Request $request, CartManagerInterface $cartManager)
{
$user = $this->getUser();
$cart = $cartManager->getCart();
$campaign = $cart->getCampaign() ?: null;
if ($campaign && $campaign->isFinished()) {
$cartItems = $cart->getItems();
foreach ($cartItems as $cartItem) {
$book = $cartItem->getBook();
$cartManager->removeProduct($book);
$cartManager->save();
}
return $this->redirectToRoute(self::ROUTE_INDEX);
}
$payWithDonations = false;
if ($campaign) {
$cartItems = $cart->getItems();
$updatedCart = false;
foreach ($cartItems as $cartItem) {
$book = $cartItem->getBook();
if ($book->getStatus() !== CampaignBook::STATUS_UNAVAILABLE) {
$cartManager->removeProduct($book);
$cartManager->save();
$updatedCart = true;
}
}
if ($updatedCart) {
return $this->redirectToRoute(self::ROUTE_INDEX);
}
$payWithDonations = $cart->getShipping() + $cart->getPrice() <= $campaign->getFundedTotal();
}
$donation = new Donation($campaign ? $campaign->getUser() : $this->getUser());
$donation->setDonor($this->getUser());
if ($user && $user->isStudent()) {
$this->addFlashBagWarning('fmt.cart.errors.students_reminder');
}
$attributes = [
"action" => $this->generateUrl(self::ROUTE_INDEX),
"attr" => [
"data-checkout" => true,
"data-token" => $this->token
]
];
$form = $this->createForm(PaymentType::class, $donation, $attributes);
$form->handleRequest($request);
$response = [
"form" => $form->createView(),
"cart" => $cart,
"student" => $user && $user->isStudent(),
"payWithDonations" => $payWithDonations,
];
if ($form->isSubmitted() && $form->isValid()) {
try {
$orderInfo = $cartManager->sendDonationOrder($donation, $cart);
$orderExternalId = $orderInfo["orderExternalId"];
$transaction = $orderInfo["transaction"];
if (!$orderExternalId) {
$this->addFlashBagError('fmt.cart.errors.send_order');
return $response;
}
if (!$transaction) {
$this->addFlashBagError('fmt.cart.errors.send_payment');
return $response;
}
$this->setSessionVariable(self::TRANSACTION_SESSION_KEY, $transaction->getId());
} catch (CartActionException $exception) {
$this->addFlashBagError($exception->getMessage());
return $response;
}
if ($user && $user->isStudent()) {
return $this->redirectToRoute(DashboardController::ROUTE_INDEX);
}
return $this->redirectToRoute(self::ROUTE_CHECKOUT_THANK_YOU);
}
return $response;
}
/**
* @param Request $request
* @return array
* @Route("/thank-you-for-checkout", name=CartController::ROUTE_CHECKOUT_THANK_YOU)
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function checkoutThankYouAction(Request $request, PaymentManagerInterface $paymentManager)
{
if (!$this->hasSessionVariable(self::TRANSACTION_SESSION_KEY)) {
throw new NotFoundHttpException("Transaction is not defined for this page");
}
$transaction = $paymentManager->getTransaction($this->getSessionVariable(self::TRANSACTION_SESSION_KEY));
$this->denyAccessUnlessGranted(TransactionVoter::CAN_VIEW_TRANSACTION, $transaction);
return [
"student" => $transaction->getRecipient()->getProfile()->getFirstName(),
"isRegistered" => is_null($this->getUser()),
];
}
}