<?php
namespace App\Controller\Customer;
use App\Classes\Customer\OrderPostmanPersist;
use App\Classes\Tools\FlexibeeConnector;
use App\Entity\OrderPostman;
use App\Exception\InvalidPriceException;
use App\Repository\ConfigurationRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Contracts\Translation\TranslatorInterface;
use App\Classes\Customer\QrPayment;
use App\Classes\Tools\Mailer;
use App\Classes\Tools\Tools;
use App\Entity\CarrierPricePostman;
use App\Entity\Configuration;
use App\Entity\Country;
use App\Entity\PackageCode;
use App\Entity\Order;
use App\Entity\PregeneratedCode;
use App\Form\Customer\SimpleOrderType;
use Psr\Log\LoggerInterface;
use Exception;
use Throwable;
class FormController extends AbstractController
{
/**
* Controller zobrazi homepage stranku custommer casti a resi odeslani formulare s objednavkou zakaznika
*
* @param EntityManagerInterface $em
* @param LoggerInterface $ordersLogger log channel orders
* @param Mailer $mailer
* @param Request $request
* @param TranslatorInterface $translator
* @param FlexibeeConnector $flexibeeConnector
* @param string|null $deliveryTo nepovinny parametr pro zemi doruceni
*
* @return Response
* @throws Exception
*/
public function index(
EntityManagerInterface $em,
LoggerInterface $ordersLogger,
Mailer $mailer,
Request $request,
TranslatorInterface $translator,
FlexibeeConnector $flexibeeConnector,
?string $deliveryTo
): Response {
$config = $em->getRepository(Configuration::class);
$session = $request->getSession();
$defaultCountry = $this->getDefaultDeliveryCountry($em, $session, $translator, $deliveryTo);
//nacitani predgenerovanych kodu
$code = $session->get('code');
if (is_null($code)) {
$code = $this->getPregenCodeAndSetInSession($em, $session, $mailer);
}
//formular objednavky
$simpleOrderForm = $this->createForm(
SimpleOrderType::class,
null,
[
'entityManager' => $em,
'deliveryCountry' => $defaultCountry->getIso()
]
);
$simpleOrderForm->handleRequest($request);
if ($simpleOrderForm->isSubmitted() && $this->simpleOrderTypeValidation(
$simpleOrderForm,
$translator,
$ordersLogger
)) {
return $this->processSubmitedFormAndRedirect(
$simpleOrderForm,
$code,
$session,
$mailer,
$config,
$translator,
$flexibeeConnector,
$ordersLogger,
$em
);
}
return $this->render(
'Customer/Postman/form.html.twig',
[
'defaultCountry' => $defaultCountry,
'code' => $code,
'orderForm' => $simpleOrderForm->createView(),
'depo_address_name' => $config->get('depo_address_name'),
'depo_address_street' => $config->get('depo_address_street'),
'depo_address_city' => $config->get('depo_address_city'),
'depo_address_zipcode' => $config->get('depo_address_zipcode'),
'support_postman_email' => $config->get('support_postman_email'),
'support_postman_phone' => $config->get('support_postman_phone')
]
);
}
/**
* Zobrazi dekovnou stranku s udaji o platbe
*
* @param EntityManagerInterface $em
* @param Request $request
* @param QrPayment $qrPayment
* @param TranslatorInterface $translator
* @param string $code
*
* @return Response
* @throws Exception
*/
public function thankYouPage(
EntityManagerInterface $em,
Request $request,
QrPayment $qrPayment,
TranslatorInterface $translator,
string $code
): Response {
$session = $request->getSession();
$sessionCode = $session->get('code');
if (!is_null($sessionCode)) {
$session->remove('code');
}
if (is_null($sessionCode) || Tools::formatCode($sessionCode) !== $code) {
$this->addFlash('error', $translator->trans('Špatný kód objednávky!', [], 'customer'));
return $this->redirectToRoute('customer_index');
}
$config = $em->getRepository(Configuration::class);
$packageCodeRepository = $em->getRepository(PackageCode::class);
$packageCode = $packageCodeRepository->findOneBy(['code' => str_replace('-', '', $code)]);
if (is_null($packageCode)) {
throw new Exception('Nepodařilo se najít v systému kód balíku: "' . $code . '"!');
}
$order = $packageCode->getCodeLocation()->getObject()->getOrder();
return $this->render(
'Customer/Postman/thank_you.html.twig',
[
'order' => $order,
'accountNumber' => $config->get('order_payment_account_czk'),
'qrImage' => $qrPayment->createQRPaymentImageCZK($order->getPriceVat(), $order->getIdentifier()),
'code' => $code,
'support_postman_email' => $config->get('support_postman_email'),
'support_postman_phone' => $config->get('support_postman_phone'),
]
);
}
/**
* Metoda loguje neuspesne pokusy o vytvoreni objednavky
*
* @param Mailer $mailer
* @param LoggerInterface $ordersLogger
* @param TranslatorInterface $translator
* @param array $formData
* @param Throwable $throwable
*
* @return Response
*/
private function logAcquisitionException(
Mailer $mailer,
LoggerInterface $ordersLogger,
TranslatorInterface $translator,
array $formData,
Throwable $throwable
): Response {
try {
$json = json_encode([
'calculation' => (array)$formData['calculation'],
'deliveryTo' => (array)$formData['deliveryTo'],
'returnTo' => (array)$formData['returnTo'],
'billing' => (array)$formData['billing'],
'contentDescription' => (array)$formData['contentDescription'],
], JSON_THROW_ON_ERROR);
} catch (Throwable $e) {
$json = 'chyba JSONu: ' . $e->getMessage();
}
$ordersLogger->critical('CHYBA ULOZENI OBJEDNAVKY: ' . $throwable->getMessage());
$ordersLogger->critical('Form data: ' . $json);
$mailData = [
'formData' => $formData,
'throwable' => $throwable->getMessage(),
// 'request' => $request,
];
$mailer->sendMail(
Mailer::ERROR_ADDRESS,
'Nepodařilo se uložit objednávku!',
Mailer::TEMPLATE_POSTMAN_ACQUISITION_ERROR,
$mailData
);
if ($throwable instanceof InvalidPriceException) {
$this->addFlash('error', $throwable->getMessage());
} else {
$this->addFlash(
'error',
$translator->trans('Při uložení objednávky se vyskytla chyba!', [], 'customer')
);
}
return $this->redirectToRoute('customer_index');
}
/**
* Metoda nastavuje vychozi zemi doruceni, ktera se urcuje temito pravidla
* 1. hledam zemi podle parametru v url
* 2. hledam zemi podle parametru v session
* 3. hledam zemi podle vychozi zeme v konfiguraci
*
* @param EntityManagerInterface $em
* @param Session $session
* @param TranslatorInterface $translator
* @param string|null $deliveryTo
*
* @return Country
* @throws \Doctrine\ORM\NonUniqueResultException
*/
private function getDefaultDeliveryCountry(
EntityManagerInterface $em,
Session $session,
TranslatorInterface $translator,
?string $deliveryTo
): Country {
$config = $em
->getRepository(Configuration::class);
$countryRepo = $em->getRepository(Country::class);
// nastavuji iso z url, pripadne z session
if (!is_null($deliveryTo)) {
$iso = $deliveryTo;
} elseif ($session->get('deliveryTo')) {
$iso = $session->get('deliveryTo');
}
// iso mam nastavene, ale uzivatel mohl do url nastavit neexistujii hodnotu
if (isset($iso)) {
$country = $countryRepo->findActiveCountryByIso($iso);
}
if (!isset($country)) {
// pokud nebyla zeme nalezena, zkousim zachranou variantu vychozi zeme
$iso = $config->get('default_delivery_country_iso');
$country = $countryRepo->findActiveCountryByIso($iso);
}
if (!$country) {
// ani vychozi zeme neni nalezena (nemelo by nastavat)
throw new Exception(
$translator->trans(
'Nebyla naleza země!',
[],
'customer'
)
);
}
$session->set('deliveryTo', $country->getIso());
return $country;
}
/**
* Ziskani noveho kodu pro balik
*
* @param EntityManagerInterface $em
* @param Session $session
* @param Mailer $mailer
*
* @return string
*/
private function getPregenCodeAndSetInSession(
EntityManagerInterface $em,
Session $session,
Mailer $mailer
): string {
$pregenCodeRepo = $em
->getRepository(PregeneratedCode::class);
$code = $pregenCodeRepo->getUnusedCode();
$availableCodes = $pregenCodeRepo->checkAvailableCodes();
if ($availableCodes['sendMail'] === true) {
$msg = 'Zbývá jen ' . $availableCodes['availableCodes']
. ' volných kódů.';
$mailer->sendMail(
Mailer::ERROR_ADDRESS,
'Nedostatek volných kódů!',
null,
['message' => $msg]
);
}
$session->set('code', $code);
return $code;
}
/**
* Zpracovani odeslaneho formulare:
* ulozime a zpracujeme objednavku
* odesleme objednavkovy email
* presmerujeme na thankYou
* v pripade problemu zpet na index
*
* @param FormInterface $simpleOrderForm
* @param string $code
* @param Session $session
* @param Mailer $mailer
* @param ConfigurationRepository $config
* @param TranslatorInterface $translator
* @param FlexibeeConnector $flexibeeConnector
* @param LoggerInterface $ordersLogger
* @param EntityManagerInterface $em
* @return Response
* @throws Exception
*/
private function processSubmitedFormAndRedirect(
FormInterface $simpleOrderForm,
string $code,
Session $session,
Mailer $mailer,
ConfigurationRepository $config,
TranslatorInterface $translator,
FlexibeeConnector $flexibeeConnector,
LoggerInterface $ordersLogger,
EntityManagerInterface $em
): Response {
$data
= $simpleOrderForm->getData();
try {
$persistData = [
'calculation' => $data['calculation'],
'deliveryAddress' => $data['deliveryTo'],
'returnAddress' => $data['returnTo'],
'billingAddress' => $data['billing'],
'contentDescription' => $data['contentDescription'],
'code' => $code
];
$orderPersist = new OrderPostmanPersist(
$em,
$translator,
$persistData
);
$order = $orderPersist->processAndPersistOrder();
$orderPersist->sendOrderToFlexibee($mailer, $translator, $flexibeeConnector);
$mailer->sendMail(
$data['returnTo']->getEmail(),
'Potvrzení objednávky',
Mailer::TEMPLATE_POSTMAN_THANK_YOU,
[
'headline' => 'Potvrzení objednávky',
'order' => $order,
'accountNumber' => $config->get('order_payment_account_czk'),
'qrImageUriHash' => $order->getProformaInvoiceHash(),
'code' => $code,
'depo_address_name' => $config->get('depo_address_name'),
'depo_address_street' => $config->get('depo_address_street'),
'depo_address_city' => $config->get('depo_address_city'),
'depo_address_zipcode' => $config->get('depo_address_zipcode'),
'support_postman_email' => $config->get('support_postman_email'),
'support_postman_phone' => $config->get('support_postman_phone'),
'depo_phone' => $config->get('depo_phone')
]
);
} catch (Throwable $ex) {
if (!is_null($session->get('code'))) {
$session->remove('code');
}
return $this->logAcquisitionException(
$mailer,
$ordersLogger,
$translator,
$data,
$ex
);
}
$this->addFlash(
'success',
$translator->trans(
"Objednávka %identifier% byla uložena!",
["%identifier%" => $order->getIdentifier()],
'customer'
)
);
return $this->redirectToRoute(
'customer_thank_you_page',
['code' => Tools::formatCode($code)]
);
}
/**
* Metoda validuje formular premustuje prazdnou billing address
*
* @param FormInterface $simpleOrderForm
* @param TranslatorInterface $translator
* @param LoggerInterface $logger
* @return bool
*/
private function simpleOrderTypeValidation(
FormInterface $simpleOrderForm,
TranslatorInterface $translator,
LoggerInterface $logger
): bool {
// true znamena chci i errory potomku
$formErrors = $simpleOrderForm->getErrors(true);
$allowedErrors = [
'data[billing].name',
'data[billing].address1',
'data[billing].city',
'data[billing].zipCode',
'data[billing].phone'
];
$count = 0;
$previousError = null;
while ($formErrors->valid()) {
if ($previousError === $formErrors->current()) {
break;
}
$previousError = $formErrors->current();
try {
//tady nam to padalo kdyz prisla CSRF chyba
$cause = $formErrors->current()->getCause()->getPropertyPath();
$template = $formErrors->current()->getMessageTemplate();
} catch (\Throwable $e) {
if (!($formErrors->current()->getCause() instanceof CsrfToken)) {
$this->addFlash(
'error',
$translator->trans('Došlo k neznámé chybě!', [], 'customer')
. ' '
. $translator->trans('Zkuste formulář znovu odeslat.', [], 'customer')
);
$logger->error(
'Neznama chyba akvizicniho formulare.',
[
'message' => $e->getMessage(),
'trace' => $e->getTrace()
]
);
}
return false;
}
// testuji, jestli je Error na polich rodice entity BillingAddress a jestli je to notBlankError
if (\in_array($cause, $allowedErrors) && str_ends_with($template, 'notBlank')) {
++$count;
} else {
return $simpleOrderForm->isValid();
}
$formErrors->next();
}
if ($simpleOrderForm instanceof Form && \count($allowedErrors) === $count) {
$simpleOrderForm->clearErrors(true);
}
return $simpleOrderForm->isValid();
}
/**
* Nacita stranku ceniku - tabulek s cenou pro kazdou zemi
* @param EntityManagerInterface $em
* @return Response
*/
public function priceList(EntityManagerInterface $em): Response
{
$carrierPricePostmanRepo = $em->getRepository(CarrierPricePostman::class);
$countries = $carrierPricePostmanRepo->getCarrierPrices();
$output = [];
foreach ($countries as $country) {
$output[$country->getCountry()->getName()][$country->getCountry()->getIso()][$country->getWeight(
)] = $country->getPriceSell();
}
return $this->render(
'Customer/Postman/price_list.html.twig',
[
'countries' => $output,
]
);
}
}