src/Diplix/KMGBundle/Controller/Service/Api2Controller.php line 189

Open in your IDE?
  1. <?php
  2. namespace Diplix\KMGBundle\Controller\Service;
  3. use Cocur\Slugify\Slugify;
  4. use Diplix\Commons\DataHandlingBundle\Entity\SysLogEntry;
  5. use Diplix\Commons\DataHandlingBundle\Repository\SysLogRepository;
  6. use Diplix\KMGBundle\Controller\Availability\AvailabilityController;
  7. use Diplix\KMGBundle\Entity\Accounting\Job;
  8. use Diplix\KMGBundle\Entity\Address;
  9. use Diplix\KMGBundle\Entity\Availability;
  10. use Diplix\KMGBundle\Entity\Dispatching\ChatMessage;
  11. use Diplix\KMGBundle\Entity\Dispatching\DispatchQueueItem;
  12. use Diplix\KMGBundle\Entity\File;
  13. use Diplix\KMGBundle\Entity\Order;
  14. use Diplix\KMGBundle\Entity\OrderStatus;
  15. use Diplix\KMGBundle\Entity\PaymentType;
  16. use Diplix\KMGBundle\Entity\PriceList;
  17. use Diplix\KMGBundle\Entity\Role;
  18. use Diplix\KMGBundle\Entity\User;
  19. use Diplix\KMGBundle\Exception\OrderValidationException;
  20. use Diplix\KMGBundle\Form\OrderForm;
  21. use Diplix\KMGBundle\Form\Util\FormErrorsSerializer;
  22. use Diplix\KMGBundle\Helper\ClientConfigProvider;
  23. use Diplix\KMGBundle\Message\GenerateQuittung;
  24. use Diplix\KMGBundle\PdfGeneration\JobPdf;
  25. use Diplix\KMGBundle\PriceCalculator\AbstractPriceCalculator;
  26. use Diplix\KMGBundle\Repository\AvailabilityRepository;
  27. use Diplix\KMGBundle\Repository\BasicRepository;
  28. use Diplix\KMGBundle\Repository\FileRepository;
  29. use Diplix\KMGBundle\Service\MobileNotifier;
  30. use Diplix\KMGBundle\Service\Notifier;
  31. use Diplix\KMGBundle\Service\OrderHandler;
  32. use Diplix\KMGBundle\Service\TaMiConnector;
  33. use Doctrine\ORM\EntityManagerInterface;
  34. use League\Flysystem\FilesystemOperator;
  35. use Symfony\Component\Form\FormError;
  36. use Symfony\Component\HttpFoundation\JsonResponse;
  37. use Symfony\Component\HttpFoundation\Request;
  38. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  39. use Symfony\Component\Messenger\MessageBusInterface;
  40. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  41. use Symfony\Component\PropertyAccess\PropertyAccess;
  42. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  43. use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
  44. class Api2Controller extends ServiceBaseController
  45. {
  46.     public const LOG_TYPE 'Api2';
  47.     protected function hasRole($role)
  48.     {
  49.         $R $this->user->getRoles(true);
  50.         /** @var Role $RR */
  51.         foreach ($R as $RR)
  52.         {
  53.             /** @noinspection TypeUnsafeComparisonInspection */
  54.             if ($RR->getRole() == $role) return true;
  55.         }
  56.         return false;
  57.     }
  58.     protected function parseAndAuth(Request $request)
  59.     {
  60.         $data $this->parseRequest($request);
  61.         $this->loadUser($data['login'],$data['password'],false);
  62.         // do not detach user -> deprecated and kills further ops which rely on objects with an association with that user (e.g. deviceTokens)
  63.         return $data;
  64.     }
  65.     protected function storeToken(array $req$source='')
  66.     {
  67.         SysLogRepository::logMessage(
  68.             $this->managerRegistry->getConnection(),
  69.             SysLogEntry::SYS_INFO,
  70.             sprintf('User#%d reported token "%s" for platform "%s" (%s)',$this->user->getId(), $req['fcm_token']??'-',$req['platform']??'-',$source)
  71.         );
  72.         if ( (isset($req['fcm_token']) && (strlen($req['fcm_token'])>1)) )
  73.         {
  74.             $token $req['fcm_token'];
  75.             $platform = isset($req['platform']) ? $req['platform'] : 'unknown platform';
  76.             $isNewToken $this->userRepo->addFcmTokenToUser($this->user->getId(), $token,$platform);
  77.             $logToken substr($token,0,4).'(...)'.substr($token,-4);
  78.             if ($isNewToken)
  79.                 SysLogRepository::logMessage(
  80.                     $this->managerRegistry->getConnection(),
  81.                     SysLogEntry::SYS_INFO,
  82.                     sprintf('User#%d stored a new FCM token "%s" for platform "%s"',$this->user->getId(),$logToken,$platform)
  83.                 );
  84.         }
  85.     }
  86.     public function storeFcmTokenAction(Request $request)
  87.     {
  88.         $req $this->parseAndAuth($request);
  89.         // auth successful at this point
  90.         $this->storeToken($req,"storeFcmTokenAction");
  91.         return new JsonResponse(['success' => true]);
  92.     }
  93.     public function __construct(
  94.         protected FileRepository $fileRepository,
  95.         protected OrderHandler $orderHandler,
  96.         protected Notifier $notifier,
  97.         protected MobileNotifier $mobileNotifier,
  98.         protected TaMiConnector $taMiConnector,
  99.         protected FilesystemOperator $uploadsFilesystem,
  100.         protected UserPasswordHasherInterface $hasher,
  101.         protected MessageBusInterface $bus,
  102.         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
  103.     )
  104.     {
  105.         parent::__construct($this->hasher,$this->managerRegistry);
  106.     }
  107.     public function indexAction(Request $request)
  108.     {
  109.         $req $this->parseAndAuth($request);
  110.         // auth successful at this point
  111.         $this->storeToken($req,"indexAction");
  112.         $res = [
  113.             'success' => true,
  114.             'user' => [
  115.                 'firstName' =>$this->user->getFirstName(),
  116.                 'lastName' =>$this->user->getLastName(),
  117.                 'email' =>$this->user->getEmail(),
  118.                 'id' =>$this->user->getId(),
  119.             ]
  120.         ];
  121.         $m $this->user->getMember();
  122.         if ($m !== null)
  123.         {
  124.             $res['member'] = [
  125.                 'name' => $m->getName(),
  126.                 'number' =>$m->getNumber(),
  127.                 'code' =>$m->getShortCode(),
  128.                 'id' =>$m->getId()
  129.             ];
  130.         }
  131.         $c $this->user->getCustomer();
  132.         if ($c !== null)
  133.         {
  134.             $res['customer'] = [
  135.                 'name' =>$c->getName(),
  136.                 'number' =>$c->getCustomerNo(),
  137.                 'id' =>$c->getId()
  138.             ];
  139.         }
  140.         return new JsonResponse($res);
  141.     }
  142.     public static function prepare4json(&$data,$humanReadable=true)
  143.     {
  144.         $data['customer'] =  $data['customer']['id'];
  145.         $data['beOwner'] =   $data['beOwner']['id'];
  146.         $data['assignedTo'] =   $data['assignedTo'] !== null $data['assignedTo']['id'] : 0;
  147.         $data['orderStatus'] = $data['orderStatus']['id'];
  148.         $data['orderTime'] = $data['orderTime']->format(self::DATE_FORMAT);
  149.         $data['paymentType'] = $humanReadable $data['paymentType']['name'] : $data['paymentType']['id'];;
  150.         $ft $data['flightTime'];
  151.         $data['flightTime'] = $ft != null $ft->format('H:i') : null;
  152.         for ($a=0$a$ac=count($data['addressList']); $a++)
  153.         {
  154.             $data['addressList'][$a]['beCreated'] = $data['addressList'][$a]['beCreated']->format(self::DATE_FORMAT);
  155.             $data['addressList'][$a]['beEdited'] = $data['addressList'][$a]['beEdited']  !== null $data['addressList'][$a]['beEdited']->format(self::DATE_FORMAT) : null;
  156.             unset($data['addressList'][$a]['beDeleted']);
  157.             unset($data['addressList'][$a]['creditCardNo']);
  158.             unset($data['addressList'][$a]['creditCardMonth']);
  159.             unset($data['addressList'][$a]['creditCardYear']);
  160.         }
  161.     }
  162.     public function singleOrderAction(Request $request)
  163.     {
  164.         $req $this->parseAndAuth($request);
  165.         $repo $this->managerRegistry->getRepository(Order::class);
  166.         //if ($this->user->getMember()===null) throw new \Exception('Fetching a single order is not enabled for your account.');
  167.         $data = [];
  168.         $item $repo->getSingleAsArray($req['id']);
  169.         if ($item!=null)
  170.         {
  171.             self::prepare4json($item);
  172.             $data[]= $item;
  173.         }
  174.         return new JsonResponse([   'success'   => true,
  175.             'data'      => $data
  176.         ]);
  177.     }
  178.     public function ordersAction(Request $request)
  179.     {
  180.         $req $this->parseAndAuth($request);
  181.         // get filters owned orders
  182.         $filterStatus array_key_exists('status',$req) ? $req['status'] : 0;
  183.         $from = (new \DateTime())->sub(new \DateInterval('P3M'));
  184.         $to = (new \DateTime())->add(new \DateInterval('P3M'));
  185.         $from array_key_exists('from',$req) ? new \DateTime($req['from']) : $from ;
  186.         $to array_key_exists('until',$req) ? new \DateTime($req['until']) : $to ;
  187.         $repo $this->managerRegistry->getRepository(Order::class);
  188.         if ($this->user->getMember()!==null)
  189.         {
  190.             $data $repo->findAllForOverview(
  191.                 null,
  192.                 0,
  193.                 $filterStatus,
  194.                 $from,
  195.                 $to,
  196.                 $this->user->getMember()->getId());
  197.         }
  198.         else
  199.         {
  200.             $filterUserList $this->userRepo->findUsersISubstitute($this->user);
  201.             $filterUserList[] = $this->user;
  202.             $filterCustomerId 0// 0 = no additional filtering - already done through userlist
  203.             // no superadmin or orderadmin filters at the moment
  204.             $data $repo->findAllForOverview(
  205.                 $filterUserList,
  206.                 $filterCustomerId,
  207.                 $filterStatus,
  208.                 $from,
  209.                 $to);
  210.         }
  211.         // pre-process joined table fields
  212.         $ic count($data);
  213.         for ($i=0;$i<$ic;$i++)
  214.         {
  215.             $this->prepare4json($data[$i]);
  216.         }
  217.         return new JsonResponse([   'success'   => true,
  218.             'from'      => $from,
  219.             'until'        => $to,
  220.             'data'      => $data
  221.         ]);
  222.     }
  223.     const CODE_MINE 1;
  224.     const CODE_NOT_MINE 2;
  225.     protected function mnLog($it,$msg,$isError=false,$details=[])
  226.     {
  227.         $itid = ($it instanceof DispatchQueueItem $it->getId() : ($it !== null ? (string)$it 'null'));
  228.         $this->managerRegistry
  229.             ->getRepository(SysLogEntry::class)
  230.             ->log(($isError SysLogEntry::SYS_ERROR SysLogEntry::SYS_INFO), MobileNotifier::LOG_TYPE$msg$details00$itid);
  231.     }
  232.     public function acceptOrDenyOrderAction(Request $request)
  233.     {
  234.         $repo $this->managerRegistry->getRepository(Order::class);
  235.         $dqi $this->managerRegistry->getRepository(DispatchQueueItem::class);
  236.         try {
  237.             $code self::CODE_NOT_MINE;
  238.             $req $this->parseAndAuth($request);
  239.             if (!in_array($req['action'],['accept','decline']))
  240.             {
  241.                 throw new \Exception('Unbekannte action.');
  242.             }
  243.             $this->managerRegistry->getManager()->transactional(function(EntityManagerInterface $em) use ($repo,$dqi,$req,&$code) {
  244.                 $order null;
  245.                 if (isset($req['id']))
  246.                 {
  247.                     $order $repo->findOneBy(['id'=>$req['id']]);
  248.                 }
  249.                 else
  250.                 {
  251.                     $order $repo->findOneBy(['orderId'=>$req['orderId']]);
  252.                 }
  253.                 $refMsg $dqi->findOneBy(['id'=>$req['reference'] ?? 'null']);
  254.                 if (($order->getAssignedTo()===null) || ($order->getAssignedTo()->getId() != $this->user->getMember()->getId()) )
  255.                 {
  256.                     throw new \Exception(/*var_export($req,true).*/sprintf('Diese Fahrt(#%d:%s) ist Ihnen(%s) nicht zugeordnet.',$order->getId(),$order->getOrderId(),$this->user->getMember()->getId()),self::CODE_NOT_MINE);
  257.                 }
  258.                 if ($req['action']==='accept')
  259.                 {
  260.                     $order->setAssignmentConfirmed(true$this->user);
  261.                     $order->setAssignmentStatus(Order::AS_NONE);
  262.                     $code self::CODE_MINE;
  263.                     if ($refMsg!==null)
  264.                     {
  265.                         $refMsg->setResponse(DispatchQueueItem::R_CONFIRMED);
  266.                     }
  267.                     $this->mnLog($refMsg,'Fahrt angenommen');
  268.                 }
  269.                 else
  270.                 if ($req['action']==='decline')
  271.                 {
  272.                     if ($order->isAssignmentConfirmed())
  273.                     {
  274.                         throw new \Exception('Fahrt wurde bereits bestätigt. Bitte rufen Sie an um Ă„nderungen vorzunehmen.',self::CODE_MINE);
  275.                     }
  276.                     if ($order->getOrderStatus()->getId()==OrderStatus::STATUS_VERMITTELT)
  277.                     {
  278.                         $osOffen $em->find(OrderStatus::class,OrderStatus::STATUS_OPEN);
  279.                         $order->setOrderStatus($osOffen);
  280.                     }
  281.                     $order->setAssignedTo(null);
  282.                     if ($order->getXchgStatus() !== Order::XCHG_RECEIVED_FROM_OTHER// falls Fahrt von Fremdsystem darf der Status nicht geändert werden
  283.                     {
  284.                         $order->setXchgStatus(Order::XCHG_NONE);
  285.                     }
  286.                     $order->setXchgTo(null);
  287.                     $order->setAssignmentStatus(Order::AS_LAST_ASSIGNMENT_DENIED);
  288.                     if ($refMsg!==null)
  289.                     {
  290.                         $refMsg->setResponse(DispatchQueueItem::R_REFUSED);
  291.                     }
  292.                     $this->mnLog($refMsg,'Fahrt abgelehnt');
  293.                 }
  294.             });
  295.             // TODO: notifications/orderhandler??
  296.             $order null;
  297.             if (isset($req['id']))
  298.             {
  299.                 $order $repo->findOneBy(['id'=>$req['id']]);
  300.             }
  301.             else
  302.             {
  303.                 $order $repo->findOneBy(['orderId'=>$req['orderId']]);
  304.             }
  305.             $this->notifier->triggerOrderUpdateForMember($order); // having this line in finally() crashes since entity manager is already closed when an error occurs above
  306.             return new JsonResponse([   'success'   => true'mine'=>($code == self::CODE_MINE) ]);
  307.         }
  308.         catch (\Throwable $ex)
  309.         {
  310.             SysLogRepository::logMessage($this->managerRegistry->getConnection(),SysLogEntry::SYS_ERROR'acceptOrDenyOrderAction():'.$ex->getMessage());
  311.             return new JsonResponse([   'success'   => false'mine'=>($ex->getCode()==self::CODE_MINE), 'message'=>$ex->getMessage()??'' ]);
  312.         }
  313.     }
  314.     public function confirmMessageReceivedAction(Request $request)
  315.     {
  316.         try {
  317.             $req $this->parseAndAuth($request);
  318.             $mn $this->mobileNotifier;
  319.             $ok $mn->confirmReceived($req['guid'],true);
  320.             return new JsonResponse([ 'success'   => $ok ]);
  321.         }
  322.         catch (\Exception $ex)
  323.         {
  324.             SysLogRepository::logMessage($this->managerRegistry->getConnection(),SysLogEntry::SYS_ERROR'confirmMessageReceivedAction():'.$ex->getMessage());
  325.             return new JsonResponse([ 'success'   => false'message'=>$ex->getMessage() ]);
  326.         }
  327.     }
  328.     /**
  329.      * @param $id
  330.      * @return Order
  331.      */
  332.     protected function fetchOrderAndCheckAssignment($id)
  333.     {
  334.         return $this->fetchOrderAndCheckAssignmentByQuery(array('id' => $id));
  335.     }
  336.     /**
  337.      * @param $id
  338.      * @return Order
  339.      */
  340.     protected function fetchOrderAndCheckAssignmentByQuery(array $predicates)
  341.     {
  342.         $repo $this->managerRegistry->getRepository(Order::class);
  343.         /** @var Order $row */
  344.         $row $repo->findOneBy($predicates);
  345.         if ($row === null)
  346.         {
  347.             throw new NotFoundHttpException('Invalid id: '.var_export($predicates,true));
  348.         }
  349.         if ( ($row->getAssignedTo() === null) ||($row->getAssignedTo()->getId()!= $this->user->getMember()->getId()) )
  350.         {
  351.             throw $this->createAccessDeniedException('Fahrt ist Ihnen nicht zugeordnet.');
  352.         }
  353.         return $row;
  354.     }
  355.     public function jobPdfAction(Request $request$id)
  356.     {
  357.         $this->parseAndAuth($request);
  358.         $row $this->fetchOrderAndCheckAssignment($id);
  359.         $pdfFile $this->orderHandler->getJobPdf($row,$this->user,'App',true);
  360.         return $this->getDownloadResponse($pdfFile,basename($pdfFile));
  361.     }
  362.     public function updateJobByOrderIdAction(Request $request)
  363.     {
  364.         // todo merge with updateJobInfoAction()
  365.         $jr $this->managerRegistry->getRepository(Job::class);
  366.         $fr $this->fileRepository;
  367.         $req $this->parseAndAuth($request);
  368.         $row $this->fetchOrderAndCheckAssignmentByQuery(['orderId'=>$req['orderId']]);
  369.         $job $jr->findOrCreateForOrder($row);
  370.         $pp PropertyAccess::createPropertyAccessor();
  371.         // transform
  372.         foreach (['flightOnPositionTime',
  373.                      'departureAtOriginTime',
  374.                      'arrivalAtDestinationTime',] as $k)
  375.         {
  376.             if (isset($req[$k]) && $req[$k]!=null$req[$k] = new \DateTime($req[$k]);
  377.         }
  378.         foreach (["memberExtraKm"] as $k)
  379.         {
  380.             if (isset($req[$k])&& $req[$k]!=null)  $req[$k] = (int)$req[$k];
  381.         }
  382.         foreach ([  'flightOnPositionTime',
  383.                      'departureAtOriginTime',
  384.                      'arrivalAtDestinationTime',
  385.                      'passengerNotMet',
  386.                      'taxameter',
  387.                      'taxameterVat',
  388.                      'extraCostName',
  389.                      'extraCostPrice',
  390.                      'memberExtraKm',
  391.                      'confirmedByCustomerThrough',
  392.                      'approvedByDriver',
  393.                      'werksRundfahrt',
  394.                     'extraWaitingTimeRide',
  395.                     'extraWaitingTime',
  396.                     'instantRideAdditionalWaitingTime',
  397.                     // not allowed to set: instantRide
  398.                     // not allowed to set: hasMemberExtraKm
  399.                  ] as $k)
  400.         {
  401.             if (array_key_exists($k,$req))
  402.             {
  403.                 $pp->setValue($job,$k,$req[$k]);
  404.             }
  405.         }
  406.         if ($job->getConfirmedByCustomerThrough()>0)
  407.         {
  408.             $job->setApprovedByPassenger(true);
  409.         }
  410.         if ((array_key_exists('customerSignature',$req)) && ($req['customerSignature']!=null))
  411.         {
  412.             $stream '';
  413.             foreach ($req['customerSignature'] as $char)
  414.             {
  415.                 $stream.= chr($char);
  416.             }
  417.             $F File::fromPsr7StreamOrString($streamsprintf('signature_%s_%d.png',$row->getOrderId(),(new \DateTime())->getTimestamp()), $this->uploadsFilesystem,'signatures');
  418.             $fr->persistFlush($F);
  419.             $job->setCustomerSignature($F);
  420.         }
  421.         if ($job->isApprovedByDriver())
  422.         {
  423.             $jr->setApprovedByDriver($job);
  424.             $this->orderHandler->triggerJobCalculation($job);
  425.             $this->bus->dispatch(
  426.                 new GenerateQuittung($job->getId())
  427.             );
  428.         }
  429.         $job->setLastChangeByMember(new \DateTime());
  430.         $jr->flush();
  431.         return new JsonResponse(['success' => true]);
  432.     }
  433.     public function updateJobInfoAction(Request $request$id)
  434.     {
  435.         $jr $this->managerRegistry->getRepository(Job::class);
  436.         $fr $this->fileRepository;
  437.         $req $this->parseAndAuth($request);
  438.         $row $this->fetchOrderAndCheckAssignment($id);
  439.         $job $jr->findOrCreateForOrder($row);
  440.         $pp PropertyAccess::createPropertyAccessor();
  441.         // transform
  442.         foreach (['flightOnPositionTime',
  443.                      'departureAtOriginTime',
  444.                      'arrivalAtDestinationTime',] as $k)
  445.         {
  446.             if ($req[$k]!=null$req[$k] = new \DateTime($req[$k]);
  447.         }
  448.         foreach ([  'flightOnPositionTime',
  449.                     'departureAtOriginTime',
  450.                     'arrivalAtDestinationTime',
  451.                     'passengerNotMet',
  452.                     'taxameter',
  453.                     'taxameterVat',
  454.                     'extraCostName',
  455.                     'extraCostPrice',
  456.                     'memberExtraKm',
  457.                     'confirmedByCustomerThrough',
  458.                     'approvedByDriver'
  459.                      ] as $k)
  460.         {
  461.             if ($k==="memberExtraKm"$req[$k] = (int)$req[$k];
  462.             $pp->setValue($job,$k,$req[$k]);
  463.         }
  464.         if ($job->getConfirmedByCustomerThrough()>0)
  465.         {
  466.             $job->setApprovedByPassenger(true);
  467.         }
  468.         if ((array_key_exists('customerSignature',$req)) && ($req['customerSignature']!=null))
  469.         {
  470.             $stream '';
  471.             foreach ($req['customerSignature'] as $char)
  472.             {
  473.                 $stream.= chr($char);
  474.             }
  475.             $F File::fromPsr7StreamOrString($streamsprintf('signature_%s_%d.png',$row->getOrderId(),(new \DateTime())->getTimestamp()), $this->uploadsFilesystem,'signatures');
  476.             $fr->persistFlush($F);
  477.             $job->setCustomerSignature($F);
  478.         }
  479.         if ($job->isApprovedByDriver())
  480.         {
  481.             $jr->setApprovedByDriver($job);
  482.             $this->orderHandler->triggerJobCalculation($job);
  483.             $this->bus->dispatch(
  484.                 new GenerateQuittung($job->getId())
  485.             );
  486.         }
  487.         $job->setLastChangeByMember(new \DateTime());
  488.         $jr->flush();
  489.         return new JsonResponse(['success' => true]);
  490.     }
  491.     public function chatMessageAction(Request $request)
  492.     {
  493.         $req $this->parseAndAuth($request);
  494.         $notifier $this->notifier;
  495.         $repo $this->managerRegistry->getRepository(ChatMessage::class);
  496.         $msg ChatMessage::textFrom($this->user,$req['message']);
  497.         $repo->persistFlush($msg);
  498.         $notifier->notifyWebSocketAboutChatMessage($msg);
  499.         return new JsonResponse(['success' => true'ref'=>$msg->getId() ]);
  500.     }
  501.     /**
  502.      * @return PriceList
  503.      * @throws \Exception
  504.      * todo : remove redudancy
  505.      */
  506.     protected function getDefaultPriceList()
  507.     {
  508.         $pl $this->user->getCustomer()->getDefaultPriceList();
  509.         if ($pl === null)
  510.         {
  511.             $pl $this->user->getCustomer()->getPriceLists()->get(0);
  512.             if ($pl === null) throw new \Exception("No priceList set");
  513.         }
  514.         return $pl;
  515.     }
  516.     protected function getDefaultPaymentType(User $user)
  517.     {
  518.         $pt $user->getCustomer()->getDefaultPaymentType();
  519.         if ($pt === null)
  520.         {
  521.             $pt $user->getCustomer()->getPaymentTypes()->get(0);
  522.             if ($pt === null) throw new \Exception("No paymentType set");
  523.         }
  524.         return $pt;
  525.     }
  526.     public function placeOrderAction(Request $request)
  527.     {
  528.         // TODO refactor order process (mail,tami,...) into service
  529.         $req $this->parseAndAuth($request);
  530.         if ($request->getMethod()!==Request::METHOD_POST)
  531.         {
  532.             return new JsonResponse(['success' => false'errorList' => ["Use POST !"]]);
  533.         }
  534.         $orderRepo $this->managerRegistry->getRepository(Order::class);
  535.         $tami $this->taMiConnector;
  536.         $oh $this->orderHandler;
  537.         // check request
  538.         $user $this->managerRegistry->getRepository(User::class)->findOneBy(['id'=>$this->user->getId()]);
  539.         $row =   $oh->getNewOrder($user,null,OrderStatus::STATUS_DRAFT);
  540.         $form $this->createForm(OrderForm::class,$row, [
  541.             OrderForm::OPT_CUSTOMER => $this->user->getCustomer(),
  542.             OrderForm::em => $this->managerRegistry->getManager(),
  543.             OrderForm::fromService => false,
  544.             'allow_extra_fields'=>true// omit extra fields warnings
  545.             'csrf_protection' => false
  546.         ]);
  547.         // auto-inject defaults
  548.         $req["ordererName"] = $this->user->getLastName();
  549.         $req["ordererMail"] = $this->user->getEmail();
  550.         $req["ordererPhone"] = $this->user->getPhone();
  551.         $req["paymentType"] = $this->getDefaultPaymentType($this->user)->getId();
  552.         $req["priceList"] = $this->getDefaultPriceList()->getId();
  553.         $req['lastEstimatedDistance'] = 0;
  554.         $req['lastEstimatedPrice'] = 0;
  555.         // tranform origin/destination to our internal address structure
  556.         if (isset($req['origin']) && isset($req['destination']))
  557.         {
  558.             // try to parse address
  559.             /** @var Address $a1 */
  560.             $a1 $row->getAddressList()->get(0);
  561.             /** @var Address $a2 */
  562.             $a2 $row->getAddressList()->get(1);
  563.             if (is_array($req['origin']))
  564.                 $a1->fromJson($req['origin']);
  565.             else
  566.                 CustomerEndpointController::tryParseAddress($a1,$req['origin']);
  567.             if (is_array($req['destination']))
  568.                 $a2->fromJson($req['destination']);
  569.             else
  570.                 CustomerEndpointController::tryParseAddress($a2,$req['destination']);
  571.             $a1->setSortOrder(0);
  572.             $a2->setSortOrder(1);
  573.             $req['addressList'] = [];
  574.             $req['addressList'][0] = $a1->jsonSerialize();
  575.             $req['addressList'][1] = $a2->jsonSerialize();
  576.             $req array_merge($req,$a1->jsonSerialize('addressList_0_'));
  577.             $req array_merge($req,$a2->jsonSerialize('addressList_1_'));
  578.         }
  579.         else
  580.         {
  581.             // the address is supposed to be transmitted in the correct structure like it has been submitted via form
  582.             // done via prepare4json which is also used in OrderHandler::sendToPlatform
  583.         }
  584.         $xchg = isset($req['xchg'])&&($req['xchg']);
  585.         $fes = new FormErrorsSerializer();
  586.         $form->submit($req);
  587.         if ($form->isValid())
  588.         {
  589.             if ($xchg)
  590.             {
  591.                 $row->setXchgStatus(Order::XCHG_RECEIVED_FROM_OTHER);
  592.                 $row->setXchgOrderId(  $req['original_order_id'] );
  593.                 $row->setOriginOrderId$req['original_order_id'] );
  594.             }
  595.             if ( (isset($req['commit']))&&($req['commit']== true) )
  596.             {
  597.                 try {
  598.                     $oh-> storeOrUpdate($row);
  599.                     $oh->initiateOrder($row);
  600.                 } catch (OrderValidationException $ex) {
  601.                     $form->addError(new FormError($ex->getMessage()));
  602.                 }
  603.                 // success
  604.                 if ( ($form->isValid()) && ($form->getErrors()->count() < 1))
  605.                 {
  606.                     SysLogRepository::logMessage(
  607.                         $this->managerRegistry->getConnection(),
  608.                         SysLogEntry::SYS_INFO,
  609.                         sprintf('API: Order %s placed.',$row->getOrderId())
  610.                     );
  611.                     return new JsonResponse(['success' => true'data' => $row->getOrderId()]);
  612.                 }
  613.                 // failed
  614.                 $errors $fes->serializeFormErrors($formtruetrue);
  615.                 SysLogRepository::logMessage(
  616.                     $this->managerRegistry->getConnection(),
  617.                     SysLogEntry::SYS_INFO,
  618.                     sprintf('API: Order rejected: %s',var_export($errors,true))
  619.                 );
  620.                 return new JsonResponse(['success' => false'errorList' => $errors]);
  621.             }
  622.             // simulation active
  623.             return new JsonResponse(['success' => true'data' => 'not committed']);
  624.         }
  625.         // form is not valid
  626.         $errors $fes->serializeFormErrors($formtruetrue);
  627.         return new JsonResponse(['success' => false'errorList' => $errors]);
  628.     }
  629.     public function announceReadyAction(Request $request)
  630.     {
  631.         $req $this->parseAndAuth($request);
  632.         $tomorrow $req['tomorrow'] === 'true';
  633.         /** @var AvailabilityRepository $repo */
  634.         $repo $this->managerRegistry->getRepository(Availability::class);
  635.         $ret $repo->announceReady($this->user->getMember(),$tomorrow);
  636.         return new JsonResponse(['success' => $ret'data' =>[]]);
  637.     }
  638.     public function cancelOrderAction(Request $request)
  639.     {
  640.         $req $this->parseAndAuth($request);
  641.         $repo $this->managerRegistry->getRepository(Order::class);
  642.         try
  643.         {
  644.             if ($request->getMethod()!==Request::METHOD_POST)
  645.             {
  646.                 throw new MethodNotAllowedException([Request::METHOD_POST],"Use POST !");
  647.             }
  648.             if (!isset($req['orderId']))
  649.             {
  650.                 throw new \RuntimeException("No orderId");
  651.             }
  652.             $row $repo->findOneBy(["orderId"=>$req['orderId'], 'customer'=> $this->user->getCustomer()->getId()]);
  653.             if ($row === null)
  654.             {
  655.                 throw new \RuntimeException('Unbekannte oder nicht zugehörige OrderId');
  656.             }
  657.             // bereits storniert -> erfolgsfall melden
  658.             if ($row->getOrderStatus()->getId() === OrderStatus::STATUS_CANCELED)
  659.             {
  660.                 return new JsonResponse(['success' => true'data' => $row->getOrderId() . " storniert"]);
  661.             }
  662.             $oh $this->orderHandler;
  663.             $oh->ensureThatOrderCanBeCancelled($row);
  664.             if ( (isset($req['commit']))&&($req['commit']== true) )
  665.             {
  666.                 $oh->cancelOrder($row);
  667.                 SysLogRepository::logMessage(
  668.                     $this->managerRegistry->getConnection(),
  669.                     SysLogEntry::SYS_INFO,
  670.                     sprintf('API: Order %s cancelled.',$row->getOrderId())
  671.                 );
  672.                 return new JsonResponse(['success' => true'data' => $row->getOrderId() . " storniert"]);
  673.             }
  674.             else
  675.             {
  676.                 return new JsonResponse(['success' => true'data' => 'not committed']);
  677.             }
  678.         }
  679.         catch (\Throwable $ex)
  680.         {
  681.             return new JsonResponse(['success' => false'errorList' => [ $ex->getMessage() ]]);
  682.         }
  683.     }
  684. }