<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Controller\TourOperator;
use App\Ecommerce\OrderManager\CancellationMethodEnum;
use App\Ecommerce\OrderManager\CancellationRefundHandler;
use App\Model\DataObject\TourOperatorUser;
use App\Model\Shop\Cancel\Result;
use App\Model\Shop\Event\EventProduct;
use App\Model\Shop\Merchandise\MerchandiseProduct;
use App\Model\Shop\Order;
use App\Model\Shop\OrderItem;
use App\Model\Shop\Ticket\TicketProduct;
use App\Model\Type\TenantType;
use App\Order\SalesReport\Exporter\Excel;
use App\Order\SalesReport\SalesReport;
use App\Service\B2B\ShopService;
use App\Service\BackOffice\OrderNoteListService;
use App\Service\BackOffice\SalesListService;
use App\Service\CDMUserManagerService;
use App\Service\Profile\ProfileOrdersService;
use App\Service\Shop\MailService;
use Carbon\Carbon;
use Elements\Bundle\TicketShopFrameworkBundle\Service\CartService;
use Knp\Component\Pager\PaginatorInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\OrderListInterface;
use Pimcore\Model\DataObject\AbstractObject;
use Pimcore\Model\DataObject\Concrete;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
/**
* @Route("/{_locale}", name="tour-operator_")
*
* @package App\Controller\TourOperator
*/
class ProfileController extends AbstractTourOperatorController
{
public function __construct(
CartService $cartService,
Environment $twig,
private MailService $mailService,
private TranslatorInterface $translator
) {
parent::__construct($cartService, $twig);
}
/**
* @Route( "/hub", name="hub")
*/
public function hubAction(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
return $this->render('tour-operator/profile/hub.html.twig');
}
/**
* @Route( "/data", name="data")
*/
public function dataAction(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
/** @var TourOperatorUser $user */
$user = $this->getUser();
if ($request->getMethod() == 'POST') {
$params = $request->request->all();
$user->setName($params['name']);
$user->setPhone($params['phone']);
$user->setStreet($params['street']);
$user->setCountry($params['country']);
$user->setCity($params['city']);
$user->setZip($params['zip']);
$user->setPreferredPaymentMethod($params['payment-method']);
try {
$user->save();
$this->addFlash('success', $this->translator->trans('tour-operator.data-save-success'));
} catch (\Exception $e) {
$this->addFlash('error', $this->translator->trans('tour-operator.data-save-error.' . $e->getMessage()));
}
}
return $this->render('tour-operator/profile/data.html.twig', [
'paymentMethods' => $user->getAllowedPaymentMethods(),
]);
}
/**
* @Route( "/password", name="password")
*/
public function passwordAction(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
return $this->render('tour-operator/profile/password.html.twig');
}
/**
* @Route("/password-change", name="change_password")
*/
public function passwordChangeAction(Request $request, CDMUserManagerService $userManagerService): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
/** @var TourOperatorUser $user */
$user = $this->getUser();
if ($request->getMethod() == 'POST' && $request->get('pwold')) {
$success = false;
$errors = [];
$returnData = $userManagerService->handleChangePasswordPreRequest($request);
if (empty($returnData['errors'])) {
$pwClassDef = $user->getClass()->getFieldDefinition('password');
/** @phpstan-ignore-next-line */
if ($user->getPassword() == '' || password_verify($request->get('pwold'), $user->getPassword()) || $pwClassDef->calculateHash($request->get('pwold')) == $user->getPassword()) {
$user->setPassword($request->get('pwnew'));
$user->setPasswordChangeDate(Carbon::now());
try {
$user->save();
$success = true;
} catch (\Exception $e) {
$errors[] = $e->getMessage();
}
} else {
$errors[] = 'old password is incorrect';
}
} else {
$errors = $returnData['errors'];
}
if ($request->get('profileUrl')) {
if (!empty($errors)) {
foreach ($errors as $error) {
$this->addFlash('error', $this->translator->trans('tour-operator.' . $error));
}
} else {
$this->addFlash('success', $this->translator->trans('tour-operator.password-change-success'));
}
return $this->redirectToRoute('tour-operator_password');
} else {
$template = $this->renderTemplate('@ElementsCDMUserManager/UserManager/partials/edit-password-box.html.twig', [
'customer' => $user,
'errors' => $errors,
'success' => $success,
'pwChangeUrl' => $this->generateUrl('tour-operator_change_password'),
]);
return $this->json(['success' => true, 'html' => $template->getContent()]);
}
}
$template = $this->renderTemplate('@ElementsCDMUserManager/Includes/overlays/edit-password-content.html.twig', [
'customer' => $user,
'editPasswordModalUrl' => $this->generateUrl('tour-operator_change_password'),
]);
return $this->json(['success' => true, 'html' => $template->getContent()]);
}
/**
* @Route("/orders", name="orders")
*/
public function ordersAction(
Request $request,
ProfileOrdersService $profileOrdersService,
PaginatorInterface $paginator
): Response {
$this->denyAccessUnlessGranted('ROLE_USER');
AbstractObject::setHideUnpublished(false);
/** @var TourOperatorUser $customer */
$customer = $this->getUser();
$returnArray = [];
$ordersTotalPrice = 0;
$ordersCancelledTotalPrice = 0;
$productOptions = [];
//orders
$orderList = $profileOrdersService->getOrders($customer, $request);
$orderItemList = $profileOrdersService->getOrderItemsPriceList($customer, $request);
$cancelledOrderItemList = $profileOrdersService->getOrderItemsPriceList($customer, $request, true);
/** @var Order $order */
foreach ($orderItemList as $order) {
$ordersTotalPrice += $order->getTotalPrice();
}
/** @var Order $cancelledOrder */
foreach ($cancelledOrderItemList as $cancelledOrder) {
$ordersCancelledTotalPrice += $cancelledOrder->getTotalPrice();
}
//min and max date
if ($minMaxOrderDates = $profileOrdersService->getMinMaxDateOrders($customer->getId())) {
$returnArray = array_merge($returnArray, $minMaxOrderDates);
}
$orderItemDataList = $profileOrdersService->getOrderProducts($customer);
//product options
if ($orderItemDataList->current()) {
$productOptions[][] = [
'label' => $this->translator->trans('tour-operator.orders.choose-product'),
'value' => '',
'disabled' => false,
'selected' => true,
];
$catalogOptions = [];
$ticketOptions = [];
$eventOptions = [];
foreach ($orderItemDataList as $orderListItem) {
/** @var TicketProduct|EventProduct|MerchandiseProduct $product */
/** @phpstan-ignore-next-line */
$product = AbstractObject::getById($orderListItem->getProductId());
if ($product instanceof TicketProduct) {
if ($catalog = $product->getCatalogObject()) {
$catalogOptions[] = [
'label' => $catalog->getOSName(),
'value' => $catalog->getId(),
'selected' => $request->get('product') == $catalog->getId(),
];
} else {
$parentProduct = $product->getReference();
$ticketOptions[] = [
'label' => $parentProduct->getOSName(),
'value' => $parentProduct->getId(),
'selected' => $request->get('product') == $parentProduct->getId(),
];
}
} else {
/** @var EventProduct|MerchandiseProduct $parentProduct */
$parentProduct = $product->getReference();
$eventOptions[] = [
'label' => $parentProduct->getOSName(),
'value' => $parentProduct->getId(),
'selected' => $request->get('product') == $parentProduct->getId(),
];
}
}
$this->sortOptions($catalogOptions);
$this->sortOptions($ticketOptions);
$this->sortOptions($eventOptions);
$productOptions['catalog'] = $catalogOptions;
$productOptions['ticket'] = $ticketOptions;
$productOptions['event'] = $eventOptions;
}
$productOptions = array_filter($productOptions);
$returnArray['ordersTotalPrice'] = $ordersTotalPrice;
$returnArray['ordersCancelledTotalPrice'] = $ordersCancelledTotalPrice;
$returnArray['productOptions'] = $productOptions;
$returnArray['paginator'] = $paginator->paginate($orderList, (int)$request->get('page', 1), 20);
if ($request->isMethod('POST') || $request->isXmlHttpRequest()) {
$template = $this->renderTemplate('tour-operator/includes/profile/order-result.html.twig', $returnArray);
return $this->json(['success' => true, 'html' => $template->getContent()]);
}
return $this->render('tour-operator/profile/orders.html.twig', $returnArray);
}
/**
* @param array<mixed >$array
*
* @return void
*/
private function sortOptions(array &$array): void
{
$array = array_unique($array, SORT_REGULAR);
usort($array, function ($a, $b) {
return $a['label'] <=> $b['label'];
});
}
/**
* @Route("/order/{id}", name="order_detail")
*/
public function orderDetailAction(int $id, OrderNoteListService $orderNoteService): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
AbstractObject::setHideUnpublished(false);
/** @var Order $order */
$order = Order::getById($id);
$backofficeBrick = $order->getOrCreateBackofficeBrick();
$notes = $orderNoteService->getOrderNoteList($order, [
'order-confirmation-mail-sent',
'order-completion-mail-sent',
'order-finished',
'tour-operator-order-cancelled',
]);
return $this->render('tour-operator/profile/order-detail.html.twig', [
'order' => $order,
'comment' => $backofficeBrick->getB2bComment(),
'notes' => $notes,
]);
}
/**
* @Route("/order/comment/{id}", name="order_comment")
*/
public function orderCommentAction(Request $request, int $id): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
$order = Order::getById($id);
$backofficeBrick = $order->getOrCreateBackofficeBrick();
$backofficeBrick->setB2bComment(htmlspecialchars(trim($request->get('comment'))));
$order->save(['versionNote' => 'Add tour-operator comment to order.']);
$template = $this->renderTemplate('tour-operator/profile/comment-result.html.twig', [
'order' => $order,
'value' => $backofficeBrick->getB2bComment(),
]);
return $this->json([
'success' => true,
'html' => $template->getContent(),
]);
}
/**
* @Route( "/cancel/order/{order}", name="order_cancel" )
*
* @param Request $request
* @param Order $order
*
* @return Response
*
* @throws \Exception
*/
public function cancelOrder(Request $request, Order $order): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
// somehow the locale is overridden after the cancel
$locale = $request->getLocale();
$form = $this->createFormBuilder([])->getForm();
$form->handleRequest($request);
if ($form->isSubmitted()) {
$this->cancel($order, $order->getCancelableItems(), true);
return $this->redirectToRoute('tour-operator_order_detail', ['id' => $order->getId(), '_locale' => $locale]);
}
$html = $this->renderTemplate('shop/includes/cancel-modal.html.twig', [
'form' => $form->createView(),
'title' => 'tour-operator.cancel-full-order',
'url' => $this->generateUrl('tour-operator_order_cancel', ['order' => $order->getId()]),
])->getContent();
return $this->json([
'success' => true,
'html' => $html,
]);
}
/**
* @Route( "/cancel/order-item/{orderItem}", name="order_item_cancel")
*
* @param Request $request
* @param OrderItem $orderItem
*
* @return Response
*
* @throws \Exception
*/
public function cancelOrderItem(Request $request, OrderItem $orderItem): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
// somehow the locale is overridden after the cancel
$locale = $request->getLocale();
$form = $this->createFormBuilder([])->getForm();
$form->handleRequest($request);
if ($form->isSubmitted()) {
$order = $orderItem->getOrder();
$this->cancel($order, [$orderItem]);
return $this->redirectToRoute('tour-operator_order_detail', ['id' => $order->getId(), '_locale' => $locale]);
}
$html = $this->renderTemplate('shop/includes/cancel-modal.html.twig', [
'form' => $form->createView(),
'title' => 'tour-operator.cancel-order-item',
'url' => $this->generateUrl('tour-operator_order_item_cancel', ['orderItem' => $orderItem->getId()]),
])->getContent();
return $this->json([
'success' => true,
'html' => $html,
]);
}
/**
* @param Order $order
* @param OrderItem[] $items
* @param bool $isWholeOrderCancelled
*
* @return void
*
* @throws \Exception
*/
private function cancel(Order $order, array $items, bool $isWholeOrderCancelled = false): void
{
$cancelHandler = new CancellationRefundHandler($order);
$cancelHandler->setCancellationMethod(CancellationMethodEnum::REFUND);
$cancelHandler->setCancelledItemObjects($items);
$result = $cancelHandler->executeCancellationAndRefund();
if ($result->getCancelStatus() == Result::SUCCESS) {
/** @var TourOperatorUser $user */
$user = $this->getUser();
if ($isWholeOrderCancelled) {
$order->addNote('tour-operator-order-cancelled', 'Order cancelled in profile', '', [
'tour-operator-user' => $user->getEmail(),
]);
} else {
$order->addNote('tour-operator-order-cancelled', 'OrderItem cancelled in profile', '', [
'tour-operator-user' => $user->getEmail(),
'oderItemId' => $items[0]->getId(),
]);
}
//user cancel-mail
$this->mailService->sendB2bEmails($order, MailService::B2B_MAIL_TYPE_ORDER_CANCEL, $result->getCancelledItems());
$this->addFlash('success', 'tour-operator.cancel.success');
if ($result->getRefundStatus() == Result::SUCCESS) {
$this->addFlash('success', 'tour-operator.refund.success');
} elseif ($result->getRefundStatus() == Result::FAILURE) {
$this->addFlash('error', 'tour-operator.refund.error');
}
} else {
$this->addFlash('error', 'tour-operator.cancel.error-while-cancel');
}
}
/**
* @param Request $request
* @param SalesListService $salesListService
* @param ProfileOrdersService $profileOrdersService
*
* @return Response
*
* @Route("/export" , name="order_export")
*/
public function exportAction(
Request $request,
SalesListService $salesListService,
ProfileOrdersService $profileOrdersService
): Response {
/** @var TourOperatorUser $user */
$user = $this->getUser();
$listOptions['filter_customerId'] = $user->getId();
$listOptions['filter_groups'] = ['product'];
if ($request->get('dateRangeFrom') && $request->get('dateRangeTo')) {
$from = Carbon::parse($request->get('dateRangeFrom'));
$to = Carbon::parse($request->get('dateRangeTo'));
$listOptions['filter_daterange'] = $from->format('d.m.Y') . ' - ' . $to->format('d.m.Y');
}
if ($request->get('email')) {
$listOptions['filter_customerEmail'] = $request->get('email');
}
if ($request->get('product')) {
$product = Concrete::getById((int)$request->get('product'));
$listOptions['filter_product'] = $profileOrdersService->getProductFilterIds($product);
}
if ($request->get('invoicenumber')) {
$listOptions['filter_ordernumber'] = $request->get('invoicenumber');
}
$objLists = $salesListService->createSalesList(OrderListInterface::LIST_TYPE_ORDER_ITEM, $listOptions, TenantType::TOUR_OPERATOR);
$report = new SalesReport($objLists, true, $salesListService->getDateFrom(), $salesListService->getDateTo(), null, true);
$exportDir = PIMCORE_PRIVATE_VAR . '/tmp/export/accounting-list/';
\Pimcore\File::mkdir($exportDir);
$exportFile = 'export_' . $user->getId() . Carbon::now()->getTimestamp() . '.xlsx';
$exporter = new Excel($report, $exportDir . $exportFile, null, $request->getLocale());
$exporter->export();
$response = new Response($exporter->getRawData());
$response->headers->set('Content-Type', $exporter->getMimeType());
$response->headers->set('Content-Length', (string)$exporter->getFilesize());
$response->setMaxAge(0);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
sprintf('%s-sales-export.xlsx', Carbon::now()->format('YmdHis'))
);
$response->headers->set('Content-Disposition', $disposition);
$exporter->close();
return $response;
}
/**
* @Route( "/warnings", name="warnings")
*/
public function warningsAction(Request $request, ShopService $shopService): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
return $this->render('tour-operator/profile/warnings.html.twig');
}
}