src/Diplix/KMGBundle/Controller/OrderController.php line 641

Open in your IDE?
  1. <?php
  2. namespace Diplix\KMGBundle\Controller;
  3. use Cocur\Slugify\Slugify;
  4. use Diplix\Commons\DataHandlingBundle\Entity\SysLogEntry;
  5. use Diplix\KMGBundle\DataTables\DataTablesHelper;
  6. use Diplix\KMGBundle\DataTables\Expr\ExprStub;
  7. use Diplix\KMGBundle\Entity\Accounting\CoopMember;
  8. use Diplix\KMGBundle\Entity\Address;
  9. use Diplix\KMGBundle\Entity\Customer;
  10. use Diplix\KMGBundle\Entity\Order;
  11. use Diplix\KMGBundle\Entity\OrderStatus;
  12. use Diplix\KMGBundle\Entity\Platform\PlatformClient;
  13. use Diplix\KMGBundle\Entity\PriceList;
  14. use Diplix\KMGBundle\Entity\Role;
  15. use Diplix\KMGBundle\Entity\User;
  16. use Diplix\KMGBundle\Exception\OrderValidationException;
  17. use Diplix\KMGBundle\Form\Admin\ChangeOrderCustomerForm;
  18. use Diplix\KMGBundle\Form\CancelOrderForm;
  19. use Diplix\KMGBundle\Form\Contraints\ValidOrder;
  20. use Diplix\KMGBundle\Form\OrderForm;
  21. use Diplix\KMGBundle\Form\OrderMinimalForm;
  22. use Diplix\KMGBundle\Form\OrderStatusFilterForm;
  23. use Diplix\KMGBundle\Helper\ClientConfigProvider;
  24. use Diplix\KMGBundle\PdfGeneration\JobPdf;
  25. use Diplix\KMGBundle\PriceCalculator\CalculatorService;
  26. use Diplix\KMGBundle\Service\OrderHandler;
  27. use Diplix\KMGBundle\Util\ExcelExportHelper;
  28. use Diplix\KMGBundle\PriceCalculator\AbstractPriceCalculator;
  29. use Diplix\KMGBundle\Repository\OrderRepository;
  30. use Diplix\KMGBundle\Repository\UserRepository;
  31. use Doctrine\Common\Collections\ArrayCollection;
  32. use Doctrine\ORM\AbstractQuery;
  33. use Doctrine\ORM\EntityRepository;
  34. use Doctrine\ORM\Tools\Pagination\Paginator;
  35. use Google\Service\VersionHistory\Platform;
  36. use Symfony\Component\Form\FormError;
  37. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  38. use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
  39. use Symfony\Component\HttpFoundation\JsonResponse;
  40. use Symfony\Component\HttpFoundation\Request;
  41. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  42. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  43. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  44. use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
  45. use Symfony\Contracts\Translation\TranslatorInterface;
  46. class OrderController extends BaseController
  47. {
  48.     public static $days = ["So","Mo","Di","Mi","Do","Fr","Sa"];
  49.     public static $columnSetup = [
  50.         0=>array('fieldName' => 'ord.id''caption' => '#''headStyle' => 'width:16px;''type' =>DataTablesHelper::T_SELECTOR),
  51.         1=>array('fieldName' => 'ord.orderTime' 'caption'=>'Datum'),
  52.         2=>array('fieldName' => '_orderToD' 'caption'=>'Uhrzeit''virtual'=>true'sortable'=>false),
  53.         3=>array('fieldName' => 'ord.orderStatus''caption'=>'Status''virtual'=>true),
  54.         4=>array('fieldName' => '_route''caption'=>'Adresse(n)''virtual'=>true,'sortable'=>false),
  55.         5=>array('fieldName' => 'ord.beOwner','caption'=>'Besteller','virtual'=>true),
  56.         6=>array('fieldName' => 'ord.orderId''caption' => 'Auftrag''searchable'=>true,'visible'=>false),
  57.         7=>array('fieldName' => '_orderStatusId''caption'=>'Status''visible'=>false,'searchable'=>false),
  58.         8=>array('fieldName' => '_actions''headStyle'=>'width:92px;''type'=> DataTablesHelper::T_BUTTONS'caption' => ''),
  59.         9=>array('fieldName' => 'ord.isVip''caption'=>'VIP''visible'=>false),
  60.         10=>array('fieldName' => '_parentOrderId''caption'=>'ParentOrderId''visible'=>false,'searchable'=>false),
  61.         11=>array('fieldName' => '_visual''caption'=>'''virtual'=>true,'visible'=>false,'searchable'=>false),
  62.     ];
  63.     protected function getDth()
  64.     {
  65.         return new DataTablesHelper(self::$columnSetup,
  66.             [
  67.                 'ajaxUrl' => $this->get('router')->generate('orders-data'),
  68.                 'ajaxData' => 'requestData',
  69.                 'searching' => true,
  70.                 //'deferLoading'  => true,
  71.                 'defaultSorting' => array(=> 'desc')
  72.             ]);
  73.     }
  74.     /**
  75.      * @var OrderRepository
  76.      */
  77.     protected $orderRepo;
  78.     /**
  79.      * @var UserRepository
  80.      */
  81.     protected $userRepo;
  82.     /** @var  Customer */
  83.     protected $customerContext;
  84.     public function __construct(
  85.         protected ClientConfigProvider $ccp,
  86.         protected OrderHandler $orderHandler,
  87.         protected CalculatorService $calculatorService,
  88.         protected TranslatorInterface $translator,
  89.         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
  90.     )
  91.     {
  92.     }
  93.     protected function init(Request $request)
  94.     {
  95.         $this->orderRepo $this->managerRegistry->getRepository(Order::class);
  96.         $this->userRepo $this->managerRegistry->getRepository(User::class);
  97.         if (!is_object($this->getCurrentUser()->getCustomer())) throw new \Exception("User has no assigned customer");
  98.         $this->customerContext $this->getCustomerContextFromSession();
  99.         // check pw age
  100.         $this->checkPasswordAge();
  101.     }
  102.     /**
  103.      * @return \Diplix\KMGBundle\Entity\Customer
  104.      */
  105.     protected function getCustomerContext()
  106.     {
  107.         return $this->customerContext;
  108.     }
  109.     public static function addMonths($date$count)
  110.     {
  111.         $m $date->format("m");
  112.         $y $date->format("Y");
  113.         $m += $count;
  114.         if ($m 1// move into last year (only works for max 12 months into the past
  115.         {
  116.             $m 12 $m;
  117.             $y--;
  118.         }
  119.         $y += ($m 12);
  120.         $m max(1, ($m 12));
  121.         return new \DateTime(sprintf("%d-%d-01"$y$m));
  122.     }
  123.     protected function export($list)
  124.     {
  125.         $tempFn $this->getParameter("dx.temp_dir") . uniqid("export") . ".xlsx";
  126.         $xh = new ExcelExportHelper();
  127.         $xh->setSingleSheetMode("Bestellungen");
  128.         /** @var array $row */
  129.         foreach ($list as $row) {
  130.             $status = ($row["remoteStatus"] == Order::REMOTE_PENDING "Entwurf" $row["orderStatus"]["name"]);
  131.             $data = array(
  132.                 "AuftragsNr" => array($row["orderId"], ExcelExportHelper::TYPE_STRING),
  133.                 "Zeitpunkt" => array($row["orderTime"], ExcelExportHelper::XTYPE_DATE),
  134.                 "Personen" => array($row["personCount"], ExcelExportHelper::TYPE_NUMERIC),
  135.                 "Status" => array($statusExcelExportHelper::TYPE_STRING),
  136.                 "KSt." => array($row["costCenter"], ExcelExportHelper::TYPE_STRING),
  137.                 "Kommentar" => array($row["comment"], ExcelExportHelper::TYPE_STRING),
  138.                 "Besteller" => array($row["ordererForename"] . " " $row["ordererName"], ExcelExportHelper::TYPE_STRING),
  139.                 "Best.Email" => array($row["ordererMail"], ExcelExportHelper::TYPE_STRING),
  140.                 "Best.Tel." => array($row["ordererPhone"], ExcelExportHelper::TYPE_STRING),
  141.                 "Kindersitze" => array($row["childSeatCount"], ExcelExportHelper::TYPE_NUMERIC),
  142.                 "KiSiInfo" => array($row["childSeatInfo"], ExcelExportHelper::TYPE_STRING),
  143.                 "Fhzg" => array($row["carType"], ExcelExportHelper::TYPE_STRING),
  144.                 "GeschätzteDistanz" => array($row["lastEstimatedDistance"], ExcelExportHelper::TYPE_NUMERIC),
  145.                 "GeschätzerPreis" => array($row["lastEstimatedPrice"], ExcelExportHelper::TYPE_NUMERIC),
  146.                 "PlId" => array($row["lastPriceListId"], ExcelExportHelper::TYPE_NUMERIC),
  147.             );
  148.             $cnt 0;
  149.             foreach ($row["addressList"] as $a) {
  150.                 $cnt++;
  151.                 $data["A" $cnt " Name"] = array($a["name"], ExcelExportHelper::TYPE_STRING);
  152.                 $data["A" $cnt " Fahrgast"] = array($a["passenger"], ExcelExportHelper::TYPE_STRING);
  153.                 $data["A" $cnt " Strasse"] = array($a["street"], ExcelExportHelper::TYPE_STRING);
  154.                 $data["A" $cnt " HNr"] = array($a["number"], ExcelExportHelper::TYPE_STRING);
  155.                 $data["A" $cnt " PLZ"] = array($a["zipCode"], ExcelExportHelper::TYPE_STRING);
  156.                 $data["A" $cnt " Stadt"] = array($a["city"], ExcelExportHelper::TYPE_STRING);
  157.             }
  158.             $xh->addRow($data);
  159.         }
  160.         $outFn "bestellungen_" date("Y-m-d_H-i") . "." $xh->getExtension();
  161.         $xh->saveTo($tempFn);
  162.         $resp = new BinaryFileResponse($tempFn);
  163.         $resp->setContentDisposition(
  164.             ResponseHeaderBag::DISPOSITION_ATTACHMENT,
  165.             $outFn
  166.         );
  167.         return $resp;
  168.     }
  169.     public function indexAction(Request $request)
  170.     {
  171.         $this->init($request);
  172.         $printView $request->query->get("print"false);
  173.         $from $this->addMonths(new \DateTime(), -3);
  174.         $to $this->addMonths($from6);
  175.         $filter = (object)array("filterStatus" => $request->query->get("filterStatus"0),
  176.             "filterFrom" => $from,
  177.             "filterTo" => $to,
  178.         );
  179.         $form $this->createForm(OrderStatusFilterForm::class, $filter, [
  180.             OrderStatusFilterForm::OPT_EM => $this->managerRegistry->getManager()]);
  181.         $form->handleRequest($request);
  182.         $list null;
  183.         if ($form->get('exportBtn')->isClicked() || $printView)
  184.         {
  185.             [$filterUserList$filterCustomerId] = $this->getUserAndCustomerFilter();
  186.             $realFilterStatus $filter->filterStatus;
  187.             /** @noinspection TypeUnsafeComparisonInspection */
  188.             if ($realFilterStatus==OrderStatus::STATUS_OPEN// always show drafts along the open rides
  189.             {
  190.                 $realFilterStatus = [OrderStatus::STATUS_OPENOrderStatus::STATUS_DRAFT];
  191.             }
  192.             $list $this->orderRepo->findAllForOverview(
  193.             $filterUserList,
  194.             $filterCustomerId,
  195.             $realFilterStatus,
  196.             $filter->filterFrom,
  197.             $filter->filterTo);
  198.         }
  199.         if ($form->get('exportBtn')->isClicked())
  200.         {
  201.             return $this->export($list);
  202.         }
  203.         return $this->render(
  204.             ($printView "@DiplixKMG/Orders/list-print.html.twig" "@DiplixKMG/Orders/list.html.twig"),
  205.             [
  206.             "config" => $this->ccp,
  207.             "list" => $list,
  208.             "form" => $form->createView(),
  209.             "calendar" => $this->fetchCalendarInfo(new \DateTime("-2 year"), new \DateTime("+1 year")),
  210.             'columnSetup' =>$this->getDth()->getColumnSetup()
  211.         ]);
  212.     }
  213.     protected function g2i($v)
  214.     {
  215.         $parts explode("."$v);
  216.         return sprintf("%4d-%02d-%02d"$parts[2], $parts[1], $parts[0]);
  217.     }
  218.     protected function getUserAndCustomerFilter()
  219.     {
  220.         $filterUserList $this->userRepo->findUsersISubstitute($this->getCurrentUser());
  221.         $filterUserList[] = $this->getCurrentUser();
  222.         $filterCustomerId 0;
  223.         // super-admin is allowed to see all - but only sees what has been selected as context
  224.         if (
  225.             ($this->hasUserRole(Role::SUPER_ADMIN))
  226.             ||
  227.             ($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN))
  228.         )
  229.         {
  230.             $filterUserList null;
  231.             $filterCustomerId $this->getCustomerContext()->getId();
  232.         }
  233.         else
  234.         {
  235.             // an order admin may see all orders from the same customer - his customer is stored in the session
  236.             if ($this->hasUserRole(Role::SUB_ORDER_ADMIN)) {
  237.                 $filterUserList null;
  238.                 $filterCustomerId $this->getCustomerContext()->getId();
  239.             }
  240.         }
  241.         return [$filterUserList$filterCustomerId];
  242.     }
  243.     protected function fetchDataQueryPlain(Request $request)
  244.     {
  245.         $req $request->query->all();
  246.         [$filterUserList$filterCustomerId] = $this->getUserAndCustomerFilter();
  247.         $realFilterStatus = (int)$req['filterStatus'];
  248.         /** @noinspection TypeUnsafeComparisonInspection */
  249.         if ($realFilterStatus==OrderStatus::STATUS_OPEN// always show drafts along the open rides
  250.         {
  251.             $realFilterStatus = [OrderStatus::STATUS_OPENOrderStatus::STATUS_DRAFT];
  252.         }
  253.         else if ($realFilterStatus!==0)
  254.         {
  255.             $realFilterStatus = [$realFilterStatus];
  256.         }
  257.         else
  258.         {
  259.             $realFilterStatus null;
  260.         }
  261.         $from = new \DateTime($req["filterFrom"] . ' 00:00');
  262.         $to = new \DateTime($req["filterTo"]. ' 23:59');
  263.         /////////////////////////////////////////////
  264.         $dth $this->getDth();
  265.         /** @var EntityRepository $repo */
  266.         $repo $this->managerRegistry->getRepository(Order::class);
  267.         $cb $repo->createQueryBuilder('ord')
  268.             ->select('ord','cust','status','address','payment','assignedto')
  269.             ->leftJoin('ord.customer','cust')
  270.             ->leftJoin('ord.orderStatus','status')
  271.             ->leftJoin('ord.addressList','address')
  272.             ->leftJoin('ord.paymentReference','payment')
  273.             ->leftJoin('ord.assignedTo','assignedto')
  274.             ;
  275.         // process filtering,ordering,paging
  276.         $dth->setQueryBuilder($cb);
  277.         $rawOrder $dth->getRawColumnOrder($req);
  278.         $dth->addSimpleFilter(1,$req,$rawOrder,[ ExprStub::like('ord.orderTime') ], 'ord.orderTime',true);
  279.         //$dth->addSimpleFilter(3,$req,$rawOrder,[ExprStub::like('status.name')], 'status.name',true);
  280.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.name') ], 'address.name',true);
  281.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.passenger') ], 'address.passenger',true);
  282.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.street') ], 'address.street',true);
  283.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.zipCode') ], 'address.zipCode',true);
  284.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.city') ], 'address.city',true);
  285.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.mobileNumber') ], 'address.mobileNumber',true);
  286.         $dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.email') ], 'address.email',true);
  287.         if ($request->query->has('columns'))
  288.         {
  289.             // if there is no 'columns' key we suspect a call made outside of  datatables.js
  290.             $dth->processRequest($request);
  291.         }
  292.         // additional filters
  293.         if ( (is_array($filterUserList)) && (count($filterUserList)>0) )
  294.         {
  295.             $cb->andWhere($cb->expr()->in('ord.beOwner',':oid'))
  296.                 ->setParameter('oid',$filterUserList);
  297.         }
  298.         if ($filterCustomerId 0)
  299.         {
  300.             $cb->andWhere('ord.customer = :cid')->setParameter('cid',$filterCustomerId);
  301.         }
  302.         if($realFilterStatus!==null)
  303.         {
  304.             $cb->andWhere($cb->expr()->in('ord.orderStatus',':status'))
  305.                 ->setParameter('status',$realFilterStatus);
  306.         }
  307.         $cb->andWhere('ord.orderTime > :from')->setParameter('from',$from);
  308.         $cb->andWhere('ord.orderTime <= :to')->setParameter('to',$to);
  309.         // query and compile paged data
  310.         $query $cb->getQuery();
  311.         $query->setHydrationMode(AbstractQuery::HYDRATE_OBJECT);
  312.         return $query;
  313.     }
  314.     protected function fetchCalendarInfo($from,$until)
  315.     {
  316.         [$filterUserList$filterCustomerId] = $this->getUserAndCustomerFilter();
  317.         $repo $this->managerRegistry->getRepository(Order::class);
  318.         $calendar = array(
  319.             "start" => $from->format("Y-m-d"),
  320.             "end" => $until->format("Y-m-d"),
  321.             "data" => $repo->countByDay($from,$until,$filterUserList,$filterCustomerId)
  322.         );
  323.         return $calendar;
  324.     }
  325.     public function jsonDataAction(Request $request)
  326.     {
  327.         $this->init($request);
  328.         $trans $this->translator;
  329.         $repo $this->managerRegistry->getRepository(Order::class);
  330.         $query $this->fetchDataQueryPlain($request);
  331.         $paginator = new Paginator($query,true);
  332.         $filteredCount $paginator->count();
  333.         $filteredAndPaged = array();
  334.         $dit $paginator->getIterator();
  335.         /** @var Order  $row */
  336.         foreach ($dit as $row)
  337.         {
  338.             $allowEdit = ($row->getOrderStatus()->getId() == OrderStatus::STATUS_DRAFT)
  339.                 ||
  340.                 ($row->getRemoteStatus() == Order::REMOTE_PENDING)
  341.                 ||
  342.                 (
  343.                     (!$this->IsLagTooLow($row->getOrderTime()))
  344.                     &&
  345.                     ($row->getOrderStatus()->getId() == OrderStatus::STATUS_OPEN)
  346.                 );
  347.             $allowEdit $allowEdit && (is_null($row->getReferencedParentOrder()));
  348.             $actions = [];
  349.             if ($allowEdit)
  350.             {
  351.                 $actions[]= [
  352.                     'route' => 'orders-edit',
  353.                     'title' => 'Bearbeiten',
  354.                     'icon' => 'fa fa-pencil',
  355.                     'class' => 'btn-sm',
  356.                     'parameters' =>["id"=>$row->getId()]
  357.                 ];
  358.                 $actions[]= [
  359.                     'route' => 'orders-cancel',
  360.                     'title' => 'Stornieren',
  361.                     'icon' => 'fa fa-times',
  362.                     'class' => 'btn-sm',
  363.                     'parameters' =>["id"=>$row->getId()]
  364.                 ];
  365.             }
  366.             else
  367.             {
  368.                 $actions[]= [
  369.                     'route' => 'orders-edit',
  370.                     'title' => 'Bearbeiten',
  371.                     'icon' => 'fa fa-eye',
  372.                     'class' => 'btn-sm',
  373.                     'parameters' =>["id"=>$row->getId()]
  374.                 ];
  375.             }
  376.             if ($row->getReferencedParentOrder()!==null)
  377.             {
  378.                 $actions[]= [
  379.                     'route' => 'orders-edit',
  380.                     'title' => 'Zu grunde liegende Bestellung',
  381.                     'icon' => 'fa fa-link',
  382.                     'class' => 'btn-sm',
  383.                     'parameters' =>["id"=>$row->getReferencedParentOrder()->getId()]
  384.                 ];
  385.             }
  386.             $actions[]= [
  387.                     'route' => 'orders-new',
  388.                     'title' => 'Als Vorlage verwenden',
  389.                     'icon' => 'fa fa-clone',
  390.                     'class' => 'btn-sm',
  391.                     'parameters' =>array("template"=>$row->getId())
  392.                 ];
  393.             $route = [];
  394.             /** @var Address $a */
  395.             foreach ($row->getAddressList() as $a)
  396.             {
  397.                 $r '';
  398.                 if ($a->getPassenger()!=='')
  399.                 {
  400.                     $r .= sprintf('<i>%s</i><br>',$a->getPassenger());
  401.                 }
  402.                 $r .= sprintf('%s, %s %s, %s',$a->getName(),$a->getStreet(),$a->getNumber(),$a->getCity());
  403.                 $route[]=$r;
  404.             }
  405.             $route implode('<br>',$route);
  406.             $visualOptions =[];
  407.             if ($row->getIsVip())
  408.             {
  409.                 $visualOptions[]=[
  410.                     'icon'=> 'fa fa-star',
  411.                     'title'=> $trans->trans("VIP")
  412.                 ];
  413.             }
  414.             $wd $row->getOrderTime() !== null self::$days$row->getOrderTime()->format('w')  ] : '';
  415.             $one = [
  416.                 => $row->getId(),
  417.                 => $wd.' '.$row->getOrderTime()->format('d.m.Y'),
  418.                 => $row->getOrderTime()->format('H:i'),
  419.                 => $trans->trans($row->getOrderStatus()->getName(),[],'entities'),
  420.                 => $route,
  421.                 => $row->getBeOwner()->getUsername(),
  422.                 => $row->getOrderId(),
  423.                 => $row->getOrderStatus()->getId(),
  424.                 => $actions,
  425.                 => $row->getIsVip(),
  426.                 10 => $row->getReferencedParentOrder()!== null $row->getReferencedParentOrder()->getId() : null,
  427.                 11 => $visualOptions
  428.             ];
  429.             $filteredAndPaged[]= $one;
  430.         }
  431.         // get total unfiltered count
  432.         $query $repo->createQueryBuilder('ord')
  433.             ->select('COUNT(ord.id)')
  434.             ->getQuery();
  435.         $res $query->getResult();
  436.         $totalRecords = (int)$res[0][1];
  437.         // compile output
  438.         $output = array(
  439.             'draw' => $req['draw'] ?? 0// 'draw' only available if request is made through datatables.js
  440.             'recordsTotal' =>$totalRecords,
  441.             'recordsFiltered' =>$filteredCount,
  442.             'data' => $filteredAndPaged
  443.         );
  444.         return $this->getJsonResponse($request,$output);
  445.     }
  446.     protected function IsLagTooLow(\DateTime $dt)
  447.     {
  448.         return false// TODO: re-enable temporarily removed check
  449.         // a user without time restriction is not bound to normal lag restrictions or any time restriction
  450.         if ($this->hasUserRole(Role::NO_TIME_RESTRICTIONtrue)) return false;
  451.         // all other users are limited
  452.         $earliest = new \DateTime();
  453.         $earliest->add(new \DateInterval(sprintf("PT%dH"Order::TIME_LAG_HOURS)));
  454.         return ($dt $earliest);
  455.     }
  456.     public function editTimeOnlyAction(Request $request$idOrderHandler $oh)
  457.     {
  458.         throw new AccessDeniedException("not yet enbaled");
  459.         $this->init($request);
  460.         $this->denyAccessUnlessGranted(Role::DISPO);
  461.         $returnUrl $request->get('returnTo',$this->generateUrl('orders'));
  462.         // record
  463.         /** @var Order $row */
  464.         $row $this->orderRepo->findOneBy(array("id" => $id));
  465.         if ($row===null) {
  466.             $this->addFlash('warning''order.invalid-id');
  467.             return $this->redirect($returnUrl);
  468.         }
  469.         // context
  470.         if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId())
  471.         {
  472.             if (($this->hasUserRole(Role::DISPO))&&($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) )
  473.             {
  474.                 $this->addFlash('warning','Benutzerkontext wurde automatisch gewechselt.');
  475.                 return $this->redirectToRoute('admin-switch-customer',[
  476.                     'customerId'=>$row->getCustomer()->getId(),
  477.                     'referTo'=>$this->generateUrl('orders-edit',['id'=>$row->getId()])]);
  478.             }
  479.             if (!$this->hasUserRole(Role::SUPER_ADMIN))
  480.             {
  481.                 $this->addFlash('warning''order.invalid-id');
  482.                 return $this->redirect($returnUrl);
  483.             }
  484.             $this->addFlash("warning"sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
  485.                 $row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
  486.         }
  487.         $editable true;
  488.         if ($row->getOrderStatus()->getId()!== OrderStatus::STATUS_OPEN)
  489.         {
  490.             if (!$this->hasUserRole(Role::EDIT_CLOSED_ORDERStrue))
  491.             {
  492.                 $editable false;
  493.             }
  494.             $this->addFlash("info"sprintf("Hinweis: Sie bearbeiten einen  Auftrag der bereits %s ist",$row->getOrderStatus()->getName()));
  495.         }
  496.         else // if open
  497.         {
  498.             if ($this->IsLagTooLow($row->getOrderTime())) { // also does permission check
  499.                 $this->addFlash('warning''order.edit-not-possible-lag');
  500.                 $editable false;
  501.             }
  502.         }
  503.         if ($row->getReferencedParentOrder() === null)
  504.         {
  505.             $this->addFlash('warning''Diese Funktion steht für Hauptaufträge nicht zur Verfügung.');
  506.             $editable false;
  507.         }
  508.         if ($row->getJob()!== null && $row->getJob()->isApprovedByAccounting())
  509.         {
  510.             $this->addFlash('warning','Diese Fahrt wurde bereits von der Buchhaltung freigegeben.');
  511.             $editable false;
  512.         }
  513.         // form
  514.         $opts = array();
  515.         if (!$editable) {
  516.             $opts["disabled"] = true;
  517.         }
  518.         $opts[OrderForm::OPT_CUSTOMER] = $row->getCustomer();
  519.         $opts[OrderForm::additionalBlumFields] = $this->getCustomerContext()->getShowAdditionalBlumFields();
  520.         $opts[OrderForm::SHOW_INTERNAL_COMMENT] = $this->hasUserRole(Role::DISPO);
  521.         $opts['constraints'] = [ new ValidOrder([
  522.             'lagTimeInHours' => $this->hasUserRole(Role::NO_TIME_RESTRICTIONtrue) ? Order::TIME_LAG_HOURS
  523.         ])];
  524.         $form $this->createForm(OrderMinimalForm::class, $row$opts);
  525.         if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
  526.             //$form->get("autoTime")->setData(true);
  527.         }
  528.         $form->handleRequest($request);
  529.         if ($form->isSubmitted())
  530.         {
  531.             if (!$editable) {
  532.                 $this->addFlash("danger""message.access-denied");
  533.                 return $this->redirectToRoute("orders-edit", array("id" => $id'returnTo' => $returnUrl));
  534.             }
  535.             if ($form->isValid())
  536.             {
  537.                 try
  538.                 {
  539.                     $oh->storeOrUpdate($row);
  540.                     $oh->initiateOrder($row,true);
  541.                 }
  542.                 catch (OrderValidationException $ex) {
  543.                     $form->addError(new FormError($ex->getMessage()));
  544.                 }
  545.                 if (($form->isValid()) && ($form->getErrors()->count() < 1)) {
  546.                     if ($row->getOrderTime()->format('Y-m-d H:i:s') < (new \DateTime())->format('Y-m-d H:i:s')) {
  547.                         $this->addFlash("warning""Achtung. Fahrt liegt in der Vergangenheit");
  548.                     }
  549.                     $this->addFlash("success""order.saved");
  550.                     return $this->redirect($returnUrl);
  551.                 }
  552.             }
  553.             $this->addFlash("warning""order.please-check-input");
  554.         }
  555.         return $this->render("@DiplixKMG/Orders/edit-time-only.html.twig", [
  556.             "form" => $form->createView(),
  557.             "order" => $row,
  558.             "returnTo" => $returnUrl
  559.         ]);
  560.     }
  561.     public function editAction(Request $request$id)
  562.     {
  563.         $this->init($request);
  564.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  565.         $oh $this->orderHandler;
  566.         $returnUrl $request->get('returnTo',$this->generateUrl('orders'));
  567.         $editable true;
  568.         $userList $this->userRepo->findUsersISubstitute($this->getCurrentUser());
  569.         $userList[] = $this->getCurrentUser();
  570.         if ($this->hasUserRole(Role::ORDER_CHANGE_OWNERtrue)) {
  571.             $alreadyInList = array();
  572.             foreach ($userList as $one$alreadyInList[] = $one->getId();
  573.             $cu $this->userRepo->findUsersOfCustomer($this->getCustomerContext());
  574.             foreach ($cu as $one) {
  575.                 if (!in_array($one->getId(), $alreadyInList))
  576.                     $userList[] = $one;
  577.             }
  578.             // todo: add current owner to list to prevent change by accident (e.g. vertretung eingerichtet)
  579.         }
  580.         $sortedPriceLists = array();
  581.         $customerPriceLists $this->getCustomerContext()->getPriceLists()->toArray();
  582.         $customerPriceLists array_filter($customerPriceLists,function (PriceList $pl){
  583.             return !$pl->isHidden();
  584.         });
  585.         $all "PKW/VAN";
  586.         if (!is_null($this->getCustomerContext()->getDefaultPriceList())) {
  587.             if (count($customerPriceLists) > 1) {
  588.                 // move the default pricelist to the top of the list
  589.                 $pl $this->getCustomerContext()->getDefaultPriceList();
  590.                 /*
  591.                 $key = array_search($pl, $customerPriceLists, true);
  592.                 $customerPriceLists = array_merge(array($key=>$customerPriceLists[$key]), $customerPriceLists);
  593.                 */
  594.                 $temp $customerPriceLists;
  595.                 $customerPriceLists = [];
  596.                 $customerPriceLists[] = $pl;
  597.                 $plId $pl->getId();
  598.                 foreach ($temp as $t) {
  599.                     if ($t->getId() != $plId$customerPriceLists[] = $t;
  600.                 }
  601.             }
  602.         }
  603.         /** @var PriceList $pl */
  604.         foreach ($customerPriceLists as $pl) {
  605.             $ct $pl->getCarTypeFilter();
  606.             if (count($ct??[]) < 1) {
  607.                 $sortedPriceLists[$all] [] = $pl;
  608.             } else {
  609.                 $key join("/"$ct);
  610.                 $sortedPriceLists[$key] [] = $pl;
  611.             }
  612.         }
  613.         $opts = array();
  614.         /** @var $row Order */
  615.         if ($id 0) {
  616.             $row $this->orderRepo->findOneBy(array("id" => $id));
  617.             if (!is_object($row)) {
  618.                 $this->addFlash('warning''order.invalid-id');
  619.                 return $this->redirect($returnUrl);
  620.             }
  621.             if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId()) {
  622.                 if (($this->hasUserRole(Role::DISPO))&&($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) )
  623.                 {
  624.                     $this->addFlash('warning','Benutzerkontext wurde automatisch gewechselt.');
  625.                     return $this->redirectToRoute('admin-switch-customer',[
  626.                         'customerId'=>$row->getCustomer()->getId(),
  627.                         'referTo'=>$this->generateUrl('orders-edit',['id'=>$row->getId()])]);
  628.                 }
  629.                 else
  630.                 if (!$this->hasUserRole(Role::SUPER_ADMIN))
  631.                 {
  632.                     $this->addFlash('warning''order.invalid-id');
  633.                     return $this->redirect($returnUrl);
  634.                 }
  635.                 else
  636.                 {
  637.                     $this->addFlash("warning"sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
  638.                         $row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
  639.                 }
  640.             }
  641.             if (!is_null($row->getReferencedParentOrder())) {
  642.                 $this->addFlash("info"sprintf("Diese Fahrt wurde automatisch angelegt."));
  643.                 $editable false;
  644.             }
  645.             if ($row->getOrderStatus()->getId() === OrderStatus::STATUS_DRAFT)
  646.             {
  647.                 $this->addFlash('info','Sie bearbeiten einen Entwurf');
  648.             }
  649.             else
  650.             if ($row->getOrderStatus()->getId()!== OrderStatus::STATUS_OPEN)
  651.             {
  652.                 if (!$this->hasUserRole(Role::EDIT_CLOSED_ORDERStrue))
  653.                 {
  654.                     $editable false;
  655.                 }
  656.                 $this->addFlash("info"sprintf("Hinweis: Sie bearbeiten einen  Auftrag der bereits %s ist",$row->getOrderStatus()->getName()));
  657.             }
  658.             else // if open
  659.             {
  660.                 if ($this->IsLagTooLow($row->getOrderTime())) { // also does permission check
  661.                     $this->addFlash('warning''order.edit-not-possible-lag');
  662.                     $editable false;
  663.                 }
  664.             }
  665.             if ($row->getJob()!== null && $row->getJob()->isApprovedByAccounting())
  666.             {
  667.                 $this->addFlash('warning','Diese Fahrt wurde bereits von der Buchhaltung freigegeben.');
  668.                 $editable false;
  669.             }
  670.         }
  671.         else
  672.         {
  673.             $opts[OrderForm::OPT_IS_NEW] = true;
  674.             $u $this->managerRegistry->getManager()->find(User::class, $this->getCurrentUser()->getId());
  675.             assert($u!==null);
  676.             $customer $this->managerRegistry->getManager()->getReference(Customer::class, $this->getCustomerContext()->getId());
  677.             //$childRow = null;
  678.             $tmplId $request->query->get('template'0);
  679.             $row null;
  680.             if ($tmplId 0)
  681.             {
  682.                 $original $this->orderRepo->findOneBy(array('id' => $tmplId));
  683.                 $this->managerRegistry->getManager()->detach($original);
  684.                 foreach ($original->getAddressList() as $a)
  685.                 {
  686.                     $this->managerRegistry->getManager()->detach($a);
  687.                 }
  688.                 $row $original!==null ? clone $original null;
  689.             }
  690.             if ($row === null)
  691.             {
  692.                 $row $oh->getNewOrder($u,$customer,OrderStatus::STATUS_DRAFT,null,$u->getAutoFillOrdererDetailsInNewOrder());
  693.                 // BLUM extras
  694.                 if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
  695.                     // remove second address from empty order
  696.                     $row->removeAddress($row->getAddressList()->last());
  697.                     $row->setDestination("FRA");
  698.                     // enforce that fields are filled out consciously
  699.                     $row->setChildSeatCount(null);
  700.                     $row->setPersonAsChildrenCount(null);
  701.                     $row->setPersonCount(null);
  702.                     $row->setExtraLuggage(null);
  703.                 }
  704.             }
  705.             $row->setBeOwner($u);
  706.             $row->setOrderStatus($this->managerRegistry->getManager()->getReference(OrderStatus::class, OrderStatus::STATUS_DRAFT));
  707.         }
  708.         $history $this->orderRepo->findHistoryFor($row);
  709.         // keep original addresses to correctly remove them from the database if the user had removed them in the form
  710.         $originalAddresses = new ArrayCollection();
  711.         foreach ($row->getAddressList() as $a) {
  712.             $originalAddresses->add($a);
  713.         }
  714.         if (!$editable) {
  715.             $opts["disabled"] = true;
  716.         }
  717.         if (count($userList) > 1) {   // only show selection field when there are more users to select than only me
  718.             $opts[OrderForm::OPT_SELECTABLE_OWNER_LIST] = $userList;
  719.         }
  720.         $opts[OrderForm::OPT_CUSTOMER] = $row->getCustomer();
  721.         $opts[OrderForm::allowFixedPrice] = $this->hasUserRole(Role::GLOBAL_ORDER_ADMIN);
  722.         $opts[OrderForm::additionalBlumFields] = $this->getCustomerContext()->getShowAdditionalBlumFields();
  723.         $opts[OrderForm::em] = $this->managerRegistry->getManager();
  724.         $opts[OrderForm::allowInstantPayment] = $this->getCustomerContext()->getAllowInstantPayment();
  725.         $opts[OrderForm::ALLOW_SUPPRESSION_OF_MAIL_CONFIRMATION] = $this->hasUserRole(Role::GLOBAL_ORDER_ADMIN);
  726.         $opts[OrderForm::SHOW_INTERNAL_COMMENT] = $this->hasUserRole(Role::DISPO);
  727.         $opts['constraints'] = [ new ValidOrder([
  728.             'lagTimeInHours' => $this->hasUserRole(Role::NO_TIME_RESTRICTIONtrue) ? Order::TIME_LAG_HOURS
  729.         ])];
  730.         $availCarTypes Order::$carTypes;
  731.         foreach ($this->ccp->getCarTypePersons() as $type=>$max)
  732.         {
  733.             if ((isset($availCarTypes[$type])) && ($max<1) )
  734.             {
  735.                 unset($availCarTypes[$type]);
  736.             }
  737.         }
  738.         $opts[OrderForm::OPT_CAR_TYPES] = $availCarTypes;
  739.         $opts[OrderForm::OPT_DISCOUNT_OPTIONS] = $this->getCustomerContext()->getDiscountOptions();
  740.         $allowedCategories $this->getCurrentUser()->getDashboards();
  741.         // if no dashboard is configured for the user -> default is allowed
  742.         if (empty($allowedCategories))
  743.         {
  744.             $allowedCategories[] = PlatformClient::DEFAULT_DASHBOARD;
  745.         }
  746.         $opts[OrderForm::OPT_DASHBOARDS] = $allowedCategories;
  747.         $form $this->createForm(OrderForm::class, $row$opts);
  748.         if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
  749.             $form->get("autoTime")->setData(true);
  750.         }
  751.         if (($opts[OrderForm::OPT_IS_NEW]??false) && count($allowedCategories) === 1)
  752.         {
  753.             $form->get('dashboard_category')->setData(reset($allowedCategories));
  754.         }
  755.         $form->handleRequest($request);
  756.         if ($form->isSubmitted())
  757.         {
  758.             if (!$editable) {
  759.                 $this->addFlash("danger""message.access-denied");
  760.                 return $this->redirectToRoute("orders-edit", array("id" => $id'returnTo'=>$returnUrl));
  761.             }
  762.             if ($form->has('merkblattGelesen') && ($form->get('merkblattGelesen')->getData() !== true)) {
  763.                 $form->get('merkblattGelesen')->addError(new FormError('Bitte bestätigen Sie, dass Sie das Merkblatt gelesen haben.'));
  764.             }
  765.             if ($form->has('dashboard_category'))
  766.             {
  767.                 $cat =  $form->get('dashboard_category')->getData();
  768.                 if (!in_array($cat$allowedCategoriestrue))
  769.                 {
  770.                     $form->get('dashboard_category')->addError(new FormError("Die wählen Sie nachfolgend eine Kategorie. Erlaubt:".implode(",",$allowedCategories)));
  771.                 }
  772.                 else
  773.                 {
  774.                     if ($cat === PlatformClient::DEFAULT_DASHBOARD)
  775.                     {
  776.                         $cat null;
  777.                     }
  778.                     $row->setDispatchCategory($cat);
  779.                 }
  780.             }
  781.             /** @noinspection MissingOrEmptyGroupStatementInspection */
  782.             else
  783.             {
  784.                 // otherwise we assume default=null when creating a new ride
  785.                 // or do not change it when editing
  786.             }
  787.             // proceed
  788.             if ($form->isValid()) {
  789.                 // clear order reference for removed addresses
  790.                 /** @var Address $a */
  791.                 foreach ($originalAddresses as $a) {
  792.                     if (false === $row->getAddressList()->contains($a)) {
  793.                         $a->setOwningOrder(null);
  794.                     }
  795.                 }
  796.                 // try to store the order
  797.                 try {
  798.                     $oh->storeOrUpdate($row);
  799.                 } catch (OrderValidationException $ex) {
  800.                     $form->addError(new FormError($ex->getMessage()));
  801.                 }
  802.             }
  803. //            //// test
  804. //            $al = $row->getAddressList()->toArray();
  805. //            reset($al);
  806. //            /**
  807. //             * @var  Address $e
  808. //             */
  809. //            foreach ($al as $k=>$e)
  810. //            {
  811. //                $this->addFlash('notice',implode(',',[
  812. //                $k,
  813. //                $e->getName(),
  814. //                $e->getZipCode()
  815. //                    ]));
  816. //            }
  817. //
  818. //            /// Test
  819.                 try {
  820.                     // since errors may be emitted above we check again
  821.                     if (($form->isValid()) && ($form->getErrors()->count() < 1)) {
  822.                         if ($form->get('submit_order')->isClicked()) {
  823.                             $suppresssMail $form->has('suppress_mail') && ($form->get('suppress_mail')->getData() == true);
  824.                             $oh->initiateOrder($row,$suppresssMail);
  825.                             if ($row->getChildOrder() !== null) {
  826.                                 $oh->initiateOrder($row->getChildOrder(),$suppresssMail);
  827.                             }
  828.                             if ($row->getOrderTime()->format('Y-m-d H:i:s') < (new \DateTime())->format('Y-m-d H:i:s')) {
  829.                                 $this->addFlash("warning""Achtung. Fahrt liegt in der Vergangenheit");
  830.                             }
  831.                             $this->addFlash('success''Bestellung wurde übermittelt.');
  832.                             if ($suppresssMail)
  833.                             {
  834.                                 $this->addFlash('warning''Es wurde keine eMail-Bestätigung versandt.');
  835.                             }
  836.                             if (($form->has('payNow')) && ($form->get('payNow')->getData() === true)) {
  837.                                 return $this->redirectToRoute("payment-start", ["orderId" => $row->getId()]);
  838.                             }
  839.                         } else {
  840.                             $this->addFlash("success""order.saved");
  841.                         }
  842.                         return $this->redirect($returnUrl);
  843.                     }
  844.                 }
  845.                 catch (\Throwable $ex)
  846.                 {
  847.                     $this->addFlash("danger"$ex->getMessage() .':' $ex->getLine().':'$ex->getFile().':'$ex->getTraceAsString());
  848.                 }
  849.                 $this->addFlash("warning""order.please-check-input");
  850.         }
  851.         $this->ccp->getCarTypePersons();
  852.         $carPersonsMax $this->ccp->getCarTypePersons();
  853.         if ($this->getCustomerContext()->getShowAdditionalBlumFields())
  854.         {
  855.             $carPersonsMax = array(
  856.                 Order::CARTYPE_PKW => 3,
  857.                 Order::CARTYPE_KOMBI => 4,
  858.                 Order::CARTYPE_MINIVAN =>6,
  859.                 Order::CARTYPE_BUS=>54);
  860.         }
  861.         return $this->render("@DiplixKMG/Orders/edit.html.twig", array(
  862.             "row" => $row,
  863.             "config" => $this->ccp,
  864.             "returnUrl"=>$returnUrl,
  865.             "history" => $history,
  866.             "historyMessageMap" => SysLogEntry::$messageMap,
  867.             "form" => $form->createView(),
  868.             "editable" => $editable,
  869.             "sortedPriceLists" => $sortedPriceLists,
  870.             "carTypePersonsMax" => $carPersonsMax,
  871.             "user" => $this->getCurrentUser(),
  872.             "customerContext" => $this->getCustomerContext()
  873.         ));
  874.     }
  875.     public function cancelAction(Request $request$id)
  876.     {
  877.         $returnUrl $request->get('returnTo',$this->generateUrl('orders'));
  878.         $this->init($request);
  879.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  880.         $oh $this->orderHandler;
  881.         try {
  882.             /** @var Order $row */
  883.             $row $this->orderRepo->findOneBy(array("id" => $id));
  884.             // check context
  885.             if ($row->getCustomer()->getId() !== $this->getCustomerContext()->getId())
  886.             {
  887.                 if (!$this->hasUserRole(Role::SUPER_ADMIN))
  888.                 {
  889.                     throw new OrderValidationException('order.invalid-id');
  890.                 }
  891.                 $this->addFlash("warning"sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
  892.                         $row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
  893.             }
  894.             $oh->ensureThatOrderCanBeCancelled($row);
  895.             $form $this->createForm(CancelOrderForm::class, array("id" => $id));
  896.             $form->handleRequest($request);
  897.             if ( ($form->isSubmitted()) && ($form->get('commit')->getData() == 1) ) {
  898.                 $oh->cancelOrder($row);
  899.                 $this->addFlash('success''order.canceled');
  900.                 return $this->redirect($returnUrl);
  901.             }
  902.             return $this->render('@DiplixKMG/Orders/cancel.html.twig', array(
  903.                 "returnUrl"=>$returnUrl,
  904.                 "row" => $row,
  905.                 "form" => $form->createView())
  906.             );
  907.         }
  908.         catch (\Throwable $ex)
  909.         {
  910.             // throw $ex;
  911.             $this->addFlash('warning'$ex->getMessage());
  912.             return $this->redirect($returnUrl);
  913.         }
  914.     }
  915.     public function estimateDistanceAction(Request $request)
  916.     {
  917.         $this->init($request);
  918.         $ll = [];
  919.         try {
  920.             $req array_merge($request->request->all(), $request->query->all());
  921.             $priceList null;
  922.             if ($this->hasUserRole(Role::SUPER_ADMIN)) {
  923.                 // admin may see all
  924.                 $priceList $this->managerRegistry->getManager()->find(PriceList::class, (int)$req['priceListId']);
  925.             } else {
  926.                 // other users, filter only for lists available to the specific customer
  927.                 $pl $this->getCustomerContext()->getPriceLists();
  928.                 foreach ($pl as $p) {
  929.                     $ll[] = $p->getId();
  930.                     /** @noinspection TypeUnsafeComparisonInspection */
  931.                     if ($p->getId() == $req['priceListId']) {
  932.                         $priceList $p;
  933.                         break;
  934.                     }
  935.                 }
  936.             }
  937.             if (!is_object($priceList)) {
  938.                 throw new \Exception(sprintf('Pricelist #%d is not available ! (%s)'$req['priceListId'], implode(','$ll)));
  939.             }
  940.             if (!isset($req['order']['addressList'])) throw new \Exception('No addresses');
  941.             $calc $this->calculatorService;
  942.             $out = [
  943.                 'status' => 'OK',
  944.                 'message' => ''
  945.             ];
  946.             $out['price'] = $calc->estimatePrice(
  947.                 $priceList,
  948.                 $req['order']['addressList'],
  949.                 $req['order'],
  950.                 null,
  951.                 $this->getCustomerContext());
  952.             $out['distances'] = $calc->getDistances();
  953.             $out['distanceSum'] = $calc->getDistanceSumInKm($calc->getDistances());
  954.             $out['priceListId'] = $priceList->getId();
  955.             $out['priceMatrix'] = $calc->getPricingTableForDisplay();
  956.             $out['priceDisplayIndex'] = $calc->getDisplayIndex();
  957.             if ($this->ccp->isHidePricesForCustomer())
  958.             {
  959.                 $out['price'] = 0;
  960.                 $out['priceMatrix'] = [];
  961.             }
  962.             // and we're done
  963.             return $this->getJsonResponse($request$out);
  964.         } catch (MissingOptionsException $ex)
  965.         {
  966.             return $this->getJsonResponse($request, array('status' => 'Fehlende Angaben''message' => 'Bitte korrigieren Sie den Auftrag oder wählen Sie eine unterstützte Preisliste''trace' => $ex->getTraceAsString()));
  967.         }
  968.         catch (\Exception $ex)
  969.         {
  970.             return $this->getJsonResponse($request, array(
  971.                 'context' => isset($calc) ? $calc->getContext() : [],
  972.                 'status' => 'EXCEPTION',
  973.                 'message' => $ex->getMessage(),
  974.                 "trace" => $ex->getTraceAsString()));
  975.         }
  976.     }
  977.     public function specialAction(Request $request)
  978.     {
  979.         return $this->render("@DiplixKMG/Orders/special.html.twig");
  980.     }
  981.     public function addressSuggestionsAction(Request $request)
  982.     {
  983.         $this->init($request);
  984.         $rep $this->managerRegistry->getManager()->getRepository(Address::class);
  985.         $all $rep->findForUser($this->getCurrentUser(), $this->getCustomerContext());
  986.         return $this->getJsonResponse($request$all);
  987.     }
  988.     public function pickAddressAction(Request $request$category$target)
  989.     {
  990.         $this->init($request);
  991.         $rep $this->managerRegistry->getManager()->getRepository(Address::class);
  992.         $all $rep->findForUser($this->getCurrentUser(), $this->getCustomerContext(), $category);
  993.         return $this->render("@DiplixKMG/Orders/pick-address.html.twig", array("list" => $all"target" => $target));
  994.     }
  995.     public function updateStatusFromServerAction(Request $request$id)
  996.     {
  997.         $this->init($request);
  998.         /** @var Order $row */
  999.         $row $this->orderRepo->findOneBy(array("id" => $id));
  1000.         if (!is_object($row)) {
  1001.             return $this->getJsonResultResponse($request"Invalid ID");
  1002.         }
  1003.         if (!$this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) {
  1004.             if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId()) {
  1005.                 return $this->getJsonResultResponse($request"Permission denied");
  1006.             }
  1007.         }
  1008.         if ($row->getRemoteStatus() != Order::REMOTE_SUCCESS) {
  1009.             return $this->getJsonResultResponse($request"Not yet submitted to remote system");
  1010.         }
  1011.         if ($row->getOrderId() == "") {
  1012.             return $this->getJsonResultResponse($request"Order has no order-no.");
  1013.         }
  1014.         return $this->getJsonResultResponse($request"Tami disabled.");
  1015. //        $tami = $this->get("diplix.tamiconnector");
  1016. //        $ok = $tami->getOrderStatus($row);
  1017. //        if (!$ok) return $this->getJsonResultResponse($request, "Remote operation failed.");
  1018. //        return $this->getJsonResponse($request, array("success" => true, "status" => $row->getOrderStatus()->getName()));
  1019.     }
  1020.     public function changeOrderCustomerAction(Request $request$id)
  1021.     {
  1022.         $this->init($request);
  1023.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  1024.         $this->ensureUserHasRole(Role::DISPO);
  1025.         /** @var Order $row */
  1026.         $row $this->orderRepo->findOneBy(["id" => $id]);
  1027.         if ($row === null) throw new AccessDeniedHttpException("Invalid ID");
  1028.         // do not check :: if ($row->getRemoteStatus() != Order::REMOTE_PENDING) throw new AccessDeniedHttpException("Order has already been submitted and cannot be changed anymore.");
  1029.         $form $this->createForm(ChangeOrderCustomerForm::class, $row, []);
  1030.         $form->handleRequest($request);
  1031.         if ($form->isSubmitted()) {
  1032.             if ($form->isValid()) {
  1033.                 $this->orderRepo->flush($row);
  1034.                 $this->addFlash('success''Änderungen gespeichert.');
  1035.             }
  1036.         }
  1037.         return $this->render("@DiplixKMG/Orders/change-customer.html.twig", [
  1038.             'form' => $form->createView(),
  1039.             'row' => $row
  1040.         ]);
  1041.     }
  1042.     public function usersForCustomerAction(Request $request$customerId)
  1043.     {
  1044.         $this->init($request);
  1045.         $customer $this->managerRegistry
  1046.             ->getRepository(Customer::class)
  1047.             ->findOneBy(array("id" => $customerId));
  1048.         /** @var UserRepository $uRepo */
  1049.         $uRepo $this->managerRegistry->getRepository(User::class);
  1050.         $list $uRepo->findUsersOfCustomer($customer);
  1051.         $resultList = array();
  1052.         foreach ($list as $user) {
  1053.             $resultList[$user->getId()] = $user->getLastName().', '.$user->getFirstName();
  1054.         }
  1055.         return new JsonResponse([
  1056.             "users" => $resultList
  1057.         ]);
  1058.     }
  1059.     public function pinAction(Request $request$id)
  1060.     {
  1061.         $this->init($request);
  1062.         $this->denyAccessUnlessGranted(Role::GLOBAL_ORDER_ADMIN);
  1063.         /** @var Order $row */
  1064.         $row $this->orderRepo->findOneBy(array('id' => $id));
  1065.         if (!is_object($row))
  1066.         {
  1067.             throw new NotFoundHttpException('Invalid id');
  1068.         }
  1069.         return $this->getJsonDataResponse($requesttrue, ['pin' =>  $row->getConfirmationPin()]);
  1070.     }
  1071.     public function jobPdfAction(Request $request$id)
  1072.     {
  1073.         $this->init($request);
  1074.         $this->denyAccessUnlessGranted(Role::GLOBAL_ORDER_ADMIN);
  1075.         /** @var Order $row */
  1076.         $row $this->orderRepo->findOneBy(array('id' => $id));
  1077.         if (!is_object($row))
  1078.         {
  1079.             throw new NotFoundHttpException('Invalid id');
  1080.         }
  1081.         $pdfFile $this->orderHandler->getJobPdf($row,$this->getCurrentUser(),'oc');
  1082.         return $this->getDownloadResponse($pdfFile,basename($pdfFile));
  1083.     }
  1084. }