src/Diplix/KMGBundle/Controller/Dispatching/DashboardController.php line 683

Open in your IDE?
  1. <?php
  2. namespace Diplix\KMGBundle\Controller\Dispatching;
  3. use Diplix\Commons\DataHandlingBundle\Entity\SysLogEntry;
  4. use Diplix\Commons\DataHandlingBundle\Repository\SysLogRepository;
  5. use Diplix\KMGBundle\Controller\BaseController;
  6. use Diplix\KMGBundle\Controller\Service\Api2Controller;
  7. use Diplix\KMGBundle\DataTables\DataTablesHelper;
  8. use Diplix\KMGBundle\Entity\Accounting\Billing;
  9. use Diplix\KMGBundle\Entity\Accounting\CoopMember;
  10. use Diplix\KMGBundle\Entity\Accounting\Job;
  11. use Diplix\KMGBundle\Entity\Accounting\JobInterestExpression;
  12. use Diplix\KMGBundle\Entity\Address;
  13. use Diplix\KMGBundle\Entity\Availability;
  14. use Diplix\KMGBundle\Entity\Dispatching\ChatMessage;
  15. use Diplix\KMGBundle\Entity\Dispatching\DispatchQueueItem;
  16. use Diplix\KMGBundle\Entity\Note;
  17. use Diplix\KMGBundle\Entity\Order;
  18. use Diplix\KMGBundle\Entity\OrderStatus;
  19. use Diplix\KMGBundle\Entity\Platform\PlatformClient;
  20. use Diplix\KMGBundle\Entity\Role;
  21. use Diplix\KMGBundle\Entity\Setting;
  22. use Diplix\KMGBundle\Entity\User;
  23. use Diplix\KMGBundle\Form\Dispatching\NoteDoneForm;
  24. use Diplix\KMGBundle\Form\Dispatching\NoteForm;
  25. use Diplix\KMGBundle\Form\Dispatching\OrderDispatchStatusForm;
  26. use Diplix\KMGBundle\Helper\ChatMessageNormalizer;
  27. use Diplix\KMGBundle\Helper\SimpleFileNormalizer;
  28. use Diplix\KMGBundle\Repository\OrderRepository;
  29. use Diplix\KMGBundle\Service\ArrayLogWrapper;
  30. use Diplix\KMGBundle\Service\JobAllocator;
  31. use Diplix\KMGBundle\Service\MobileNotifier;
  32. use Diplix\KMGBundle\Service\Notifier;
  33. use Diplix\KMGBundle\Service\OrderHandler;
  34. use Doctrine\ORM\AbstractQuery;
  35. use Doctrine\ORM\EntityRepository;
  36. use Doctrine\ORM\QueryBuilder;
  37. use Doctrine\ORM\Tools\Pagination\Paginator;
  38. use Liip\ImagineBundle\Imagine\Cache\CacheManager;
  39. use PhpImap\Mailbox;
  40. use SecIT\ImapBundle\Service\Imap;
  41. use Symfony\Component\HttpFoundation\JsonResponse;
  42. use Symfony\Component\HttpFoundation\Request;
  43. use Symfony\Component\Serializer\Encoder\JsonEncoder;
  44. use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
  45. use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
  46. use Symfony\Component\Serializer\Serializer;
  47. class DashboardController extends BaseController
  48. {
  49.     protected $chatRepo;
  50.     public function __construct(
  51.         protected MobileNotifier $mobileNotifier,
  52.         protected Notifier $notifier,
  53.         protected OrderHandler $orderHandler,
  54.         protected Imap $imapService,
  55.         protected CacheManager $imgagineCacheManager,
  56.         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
  57.     )
  58.     {
  59.     }
  60.     protected function init()
  61.     {
  62.         $this->ensureUserHasRole(Role::DISPO);
  63.     }
  64.     protected function getMembers()
  65.     {
  66.         $repo $this->managerRegistry->getRepository(CoopMember::class);
  67.         return $repo->findBy(["active"=>true],['shortCode'=>'asc']);
  68.     }
  69.     protected function getUsersWhichAreMember()
  70.     {
  71.         $repo $this->managerRegistry->getRepository(User::class);
  72.         return $repo->findUsersWhichAreMember();
  73.     }
  74.     protected function isDashoardPermitted(?string $dispatchCategory): bool
  75.     {
  76.         $possible $this->getCurrentUser()->getDashboards();
  77.         $categoryToCheck $dispatchCategory ?? PlatformClient::DEFAULT_DASHBOARD;
  78.         return in_array($categoryToCheck$possibletrue);
  79.     }
  80.     public function dashboard2Action(Request $request)
  81.     {
  82.         $this->init();
  83.         $osr $this->managerRegistry->getRepository(OrderStatus::class)->getByIdMap();
  84.         $osr array_map(function($el){ return $el->getName();},$osr);
  85.         $dispatchCategory $request->query->get('category',null);
  86.         if (!$this->isDashoardPermitted($dispatchCategory))
  87.         {
  88.             $this->addFlash('danger','Bitte prüfen Sie Ihre Berechtigungen');
  89.             return $this->redirectToRoute('kmg_home');
  90.         }
  91.         return $this->render('@DiplixKMG/Dispatching/dashboard_react.html.twig',[
  92.             'members' => $this->getMembers(),
  93.             'dispatchCategory' => $dispatchCategory,
  94.             'statusMap' => $osr,
  95.         ]);
  96.     }
  97.     protected function prepareOrderArrayForOutput(&$arr)
  98.     {
  99.         if ($arr === null) return null;
  100.         $arr['orderStatus'] = $arr['orderStatus']['id'];
  101.         $arr['orderTime'] = $arr['orderTime']->format(Api2Controller::DATE_FORMAT);
  102.         $arr['jobPdfRequestedOn'] = $arr['jobPdfRequestedOn'] !==null ?  $arr['jobPdfRequestedOn']->format(Api2Controller::DATE_FORMAT) : null;
  103.         $arr['assignedTo'] = $arr['assignedTo'] !== null $arr['assignedTo']['id'] : null;
  104.         $route = [];
  105.         foreach ($arr['addressList'] as $a)
  106.         {
  107.             $route [] = sprintf('%s'$a['city']);
  108.         }
  109.         $route implode(' > ',$route);
  110.         $arr['route'] = $route;
  111.         return $arr;
  112.     }
  113.     public function dashboardData2Action(Request $request)
  114.     {
  115.         $this->init();
  116.         /** @var EntityRepository $repo */
  117.         $repo $this->managerRegistry->getRepository(Order::class);
  118.         /** @var QueryBuilder $cb */
  119.         $cb $repo->getQb();
  120.         $dispatchCategory $request->query->get('category',null);
  121.         if (!$this->isDashoardPermitted($dispatchCategory))
  122.         {
  123.             return $this->getJsonDataResponse($request,false,null,"Bitte prüfen Sie Ihre Berechtigungen");
  124.         }
  125.         if (!empty($dispatchCategory))
  126.         {
  127.             $cb->andWhere('A.dispatchCategory = :dicat')->setParameter('dicat',$dispatchCategory);
  128.         }
  129.         else
  130.         {
  131.             $cb->andWhere('A.dispatchCategory is null');
  132.         }
  133.         // hide some
  134.         // $cb->andWhere('A.remoteStatus != :rs')->setParameter('rs',Order::REMOTE_PENDING);
  135.         $cb->andWhere$cb->expr()->notIn('A.orderStatus',':hiddenStati'))
  136.             ->setParameter('hiddenStati',[OrderStatus::STATUS_CANCELEDOrderStatus::STATUS_DRAFTOrderStatus::STATUS_INSTANT_ORDER_PENDING]);
  137.         // only rides to x days to the future
  138.         $from = new \DateTime(); $from->setTime(0,0,0); $from->sub(new \DateInterval('P1D'));
  139.         $to = clone $from;
  140.         $to->add(new \DateInterval('P150D')); $to->setTime(23,59,59);
  141.         $cb->andWhere('A.orderTime <= :to')->setParameter('to',$to);
  142. //        $cb->andWhere($cb->expr()->gte("A.orderTime",":from"))
  143. //            ->setParameter('from',$from);
  144.         $fromOlder = clone $from;
  145.         $fromOlder->sub(new \DateInterval('P31D'));
  146.         $cb->andWhere(
  147.             $cb->expr()->orX(
  148.                 // all rides from yesterday, independant of status
  149.                 $cb->expr()->gte("A.orderTime",":from"),
  150.                 // all older open ones
  151.                 $cb->expr()->andX(
  152.                         $cb->expr()->eq("A.orderStatus",OrderStatus::STATUS_OPEN),
  153.                         $cb->expr()->gte("A.orderTime",":from2"),
  154.                         $cb->expr()->lte("A.orderTime",":to2")
  155.                     )
  156.             ));
  157.         $cb->setParameter('from',$from);
  158.         $cb->setParameter('from2',$fromOlder)->setParameter('to2',$to);
  159.         $cb->orderBy('A.orderTime');
  160.         $data =  $cb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);
  161.         $id2i = [];
  162.         for ($i=0;$i<$ic=count($data);$i++) {
  163.             $this->prepareOrderArrayForOutput($data[$i]);
  164.             $data[$i]['hasInterest'] = false;
  165.             $id2i[$data[$i]['orderId']] = $i;
  166.         }
  167.         $fer $this->managerRegistry->getRepository(JobInterestExpression::class);
  168.         $fe $fer->findFutureInterests($from);
  169.         /** @var JobInterestExpression $row */
  170.         foreach ($fe as $row)
  171.         {
  172.             $on $row->getJob()->getKnownOrder()->getOrderId();
  173.             if (array_key_exists($on,$id2i))
  174.             {
  175.                 $data$id2i[$on] ] ['hasInterest'] = true;
  176.             }
  177.         }
  178.         return new JsonResponse([
  179.             'success'   => true,
  180.             'from'      => $from,
  181.             'until'     => $to,
  182.             'data'      => $data
  183.         ]);
  184.     }
  185.     /**
  186.      * @param $id
  187.      * @param int $hydrationMode
  188.      * @return array|Order
  189.      * @throws \Doctrine\ORM\NoResultException
  190.      * @throws \Doctrine\ORM\NonUniqueResultException
  191.      */
  192.     protected function fetchSingle($id$hydrationMode AbstractQuery::HYDRATE_ARRAY)
  193.     {
  194.         /** @var OrderRepository $repo */
  195.         $repo $this->managerRegistry->getRepository(Order::class);
  196.         $cb $repo->getQb()->where('A.id = :id')->setParameter('id',$id);
  197.         return $cb->getQuery()->getSingleResult($hydrationMode);
  198.     }
  199.     public function singleDataAction(Request $request$id)
  200.     {
  201.         try {
  202.             $item $this->fetchSingle($id);
  203.             $this->prepareOrderArrayForOutput($item);
  204.             return $this->getJsonDataResponse($request,true$item);
  205.         }
  206.         catch (\Throwable $ex)
  207.         {
  208.             return $this->getJsonDataResponse($request,false,null,$ex->getMessage());
  209.         }
  210.     }
  211.     public function assignMemberAction(Request $request$orderId$memberId)
  212.     {
  213.         $this->init();
  214.         try {
  215.             $order $this->fetchSingle($orderId,AbstractQuery::HYDRATE_OBJECT);
  216. //            if ($order->getXchgTo()!==null)
  217. //            {
  218. //                throw new \RuntimeException('Fahrt ist Fremdystem zugeordnet.');
  219. //            }
  220.             $oh $this->orderHandler;
  221.             $oh->setTransactionTag($request->query->get('transactionTag',''));
  222.             $order $oh->assignMemberToOrder((int)$orderId$memberId);
  223.             $item $this->fetchSingle($order->getId());
  224.             $this->prepareOrderArrayForOutput($item);
  225.             return $this->getJsonDataResponse($request,true$item);
  226.         }
  227.         catch (\Throwable $ex)
  228.         {
  229.             $item $this->fetchSingle($orderId);
  230.             $this->prepareOrderArrayForOutput($item);
  231.             return $this->getJsonDataResponse($request,false,$item,$ex->getMessage());
  232.         }
  233.     }
  234.     public function triggerMemberNotificationAction(Request $request$orderId)
  235.     {
  236.         $this->init();
  237.         try {
  238.             $order $this->fetchSingle($orderId,AbstractQuery::HYDRATE_OBJECT);
  239.             if ($order->getAssignedTo()===null)
  240.             {
  241.                 throw new \RuntimeException('Kein Mitglied zugewiesen.');
  242.             }
  243.             $this->notifier->triggerOrderUpdateForMember($order,Notifier::M_CONFIRMATION_REQUIRED);
  244.             return $this->getJsonDataResponse($request,true);
  245.         }
  246.         catch (\Throwable $ex)
  247.         {
  248.             return $this->getJsonDataResponse($request,false,null,'Benachrichtigung fehlgeschlagen: ' $ex->getMessage());
  249.         }
  250.     }
  251.     public function contextAction(Request $request)
  252.     {
  253.         $this->init();
  254.         $osr $this->managerRegistry->getRepository(OrderStatus::class)->getByIdMap();
  255.         $statusMap array_map(function($el){ return $el->getName();},$osr);
  256.         $memberList $this->getMembers();
  257.         return $this->getJsonDataResponse($request,true,[
  258.            'memberList' => $memberList,
  259.            'statusMap'  => $statusMap
  260.         ]);
  261.     }
  262.     public function memberUserListAction(Request $request)
  263.     {
  264.         $this->init();
  265.         return $this->getJsonDataResponse($request,true,[
  266.             'userList' => $this->getUsersWhichAreMember(),
  267.         ]);
  268.     }
  269.     public function deviceLogAction(Request $request $orderId)
  270.     {
  271.         $this->init();
  272.         $sysRepo $this->managerRegistry->getRepository(SysLogEntry::class);
  273.         $dqiRepo $this->managerRegistry->getRepository(DispatchQueueItem::class);
  274.         $order $this->managerRegistry->getManager()->find(Order::class,$orderId);
  275.         // queue items
  276.         $items $dqiRepo->findBy([
  277.             'order' => $order,
  278.         ]);
  279.         $idList array_map(function ($o){ return $o->getId(); },$items);
  280.         $shortedList = [];
  281.         $idx 1;
  282.         foreach ($idList as $id)
  283.         {
  284.             $shortedList[$id] = $idx++;
  285.         }
  286.         $sortedItems= [];
  287.         foreach ($items as $i)
  288.         {
  289.             $sortedItems[$i->getId()] = $i;
  290.         }
  291.         //print_r($idList);
  292.         $data $sysRepo->findBy([
  293.             'refStr' => $idList,
  294.             'type' => MobileNotifier::LOG_TYPE
  295.         ],['logTime'=>'desc']);
  296.         $html '<table class="table">';
  297.         /** @var SysLogEntry $one */
  298.         foreach ($data as $one)
  299.         {
  300.             $ref $sortedItems[$one->getRefStr()] ?? null;
  301.             $html .= sprintf('<tr>
  302.                                     <td>%s</td>
  303.                                     <td title="%s">%s</td>
  304.                                     <td>%s</td>
  305.                                     <td>%s</td>
  306.                                     <td>%s</td>
  307.                               </tr>',
  308.                     $one->getLogTime()->format('d.m.y H:i:s'),
  309.                     $one->getRefStr(),
  310.                     $shortedList[$one->getRefStr()],
  311.                     $one->getMessage(),
  312.                     $ref !== null ?  ($ref->getMember() !== null $ref->getMember()->getName() : '-kein-') : 'null',
  313.                     $ref !== null ?  ($ref->getBeOwner() !== null $ref->getBeOwner()->getShortName() : '-kein-Besitzer-') : 'null'
  314.             );
  315.         }
  316.         if (count($data)<1)
  317.         {
  318.             $html.= '<tr><td> - keine Daten -</td></tr>';
  319.         }
  320.         $html .='</table>';
  321.         return $this->getJsonDataResponse($request,true$html);
  322.     }
  323.     public static function checkImapMailStatus(Mailbox $box)
  324.     {
  325.         $result = [
  326.             'mails' => -1,
  327.             'unread' => -1,
  328.             'error' => null
  329.         ];
  330.         try {
  331.             $info $box->getMailboxInfo();
  332.             $result['mails'] = $info->{'Nmsgs'};
  333.             $result['unread'] = $info->{'Unread'};
  334.         }
  335.         catch (\Throwable $ex)
  336.         {
  337.             $result['error'] = $ex->getMessage();
  338.         }
  339.         return $result;
  340.     }
  341.     public function checkImapStatusAction(Request $request)
  342.     {
  343.         $this->init();
  344.         $result self::checkImapMailStatus($this->imapService->get('kmg_dispo'));
  345.         return $this->getJsonDataResponse($request,($result['error']===null), $result);
  346.     }
  347.     protected function sendMessageToSingleMember(User  $u$msg$parent null)
  348.     {
  349.         assert($u->getMember()!==null);
  350.         $message ChatMessage::textTo($u,$msg);
  351.         if ($parent!==null)
  352.         {
  353.             $message->setGroupParent($parent);
  354.         }
  355.         $this->chatRepo->persistFlush($message);
  356.         $message->setQueueItemDispatchQueueItem::createFor(DispatchQueueItem::ACT_MESSAGE,$u->getMember(),$u,$this->getCurrentUser()));
  357.         $this->mobileNotifier->queue($message->getQueueItem());
  358.         $this->chatRepo->persistFlush($message);
  359.         $this->notifier->notifyWebSocketAboutChatMessage($message);
  360.         $this->mobileNotifier->dispatchSingle($message->getQueueItem());
  361.         return $message;
  362.     }
  363.     public function sendMessageToMemberAction(Request $request)
  364.     {
  365.         $this->init();
  366.         $this->chatRepo  $this->managerRegistry->getRepository(ChatMessage::class);
  367.         $userRepo $this->managerRegistry->getRepository(User::class);
  368.         $data \GuzzleHttp\json_decode($request->getContent(),true);
  369.         $u $data['userId'];
  370.         $msg $data['message'];
  371.         if ($u!==0)
  372.         {
  373.             $all = [ $userRepo->findOneBy(['id'=>$u]) ];
  374.         }
  375.         else {
  376.             $all $userRepo->findUsersWhichAreMember();
  377.         }
  378.         $parent null;
  379.         /** @var User $targetUser */
  380.         foreach ($all as $targetUser)
  381.         {
  382.             $m $this->sendMessageToSingleMember($targetUser,$msg,$parent);
  383.             if ($parent===null)
  384.             {
  385.                 $parent $m;
  386.             }
  387.         }
  388.         return $this->getJsonDataResponse($request,true);
  389.     }
  390.     public function getMessagesAction(Request $request$userId)
  391.     {
  392.         $this->init();
  393.         $chatRepo  $this->managerRegistry->getRepository(ChatMessage::class);
  394.         $userRepo $this->managerRegistry->getRepository(User::class);
  395.         $all $chatRepo->findFor((int)$userId);
  396.         $serializer ChatMessage::getSerializer$this->imgagineCacheManager );
  397.         $data =  $serializer->serialize($all'json',["filterName"=>"avatar_small"]);
  398.         return $this->getJsonDataResponse($request,true,json_decode($data,false));
  399.     }
  400.     public function getNotesAction(Request $request)
  401.     {
  402.         $this->init();
  403.         $noteRepo  $this->managerRegistry->getRepository(Note::class);
  404.         $all $noteRepo->findMyNotes($this->getCurrentUser(),false);
  405.         $data array_map(function(Note $el){
  406.             return [
  407.                 'id' => $el->getId(),
  408.                 'text' => $el->getText(),
  409.                 'startTime' => $el->getStartTime()->format('d.m.y H:i:s'),
  410.                 'done' => $el->getDone() !== null,
  411.                 'type' => $el->getType(),
  412.                 'audience' => $el->getAudience() !== null $el->getAudience()->getLastName() : null,
  413.                 'customer' => $el->getCustomer() !== null $el->getCustomer()->getName() : null
  414.             ];
  415.         },$all);
  416.         return $this->getJsonDataResponse($request,true,$data);
  417.     }
  418.     public function memberAvailabilityAction(Request $request$orderId)
  419.     {
  420.         $this->init();
  421.         $orderRepo $this->managerRegistry->getRepository(Order::class);
  422.         $userRepo $this->managerRegistry->getRepository(User::class);
  423.         $memberRepo $this->managerRegistry->getRepository(CoopMember::class);
  424.         $avRepo $this->managerRegistry->getRepository(Availability::class);
  425.         $jexRepo $this->managerRegistry->getRepository(JobInterestExpression::class);
  426.         $order $orderRepo->findOneBy(['id'=>$orderId]);
  427.         $orderTime $order->getOrderTime();
  428.         // $memberUser = $userRepo->findUsersWhichAreMember();
  429.         $memberList $memberRepo->findBy(['showInCalendar'=>true]);
  430.         // a) alle termine im zielzeitraum
  431.         $now = new \DateTime();
  432.         $avsByMember $avRepo->findForRange($orderTime,$orderTime,true,null,false);
  433.         // b) bereitschaftsmeldungen für den aktuellen tag
  434.         $readyByMember $avRepo->findForRange($now,$now,true,null,false,null,null,[Availability::READY]);
  435.         // c) fahrten heutiger tag
  436.         $ridesToday $orderRepo->findAssignedToAnyMemberForDay($now);
  437.         // d) Interessenmeldungungen
  438.         $interests $jexRepo->findFutureInterests(null,[$order->getJob()->getId()]);
  439.         $interestMemberIds = [];
  440.         /** @var JobInterestExpression $i */
  441.         foreach ($interests as $i)
  442.         {
  443.             $interestMemberIds[]= $i->getMember()->getId();
  444.         }
  445.             // [..]
  446.         /*
  447.          *
  448.          * -> Anzahl Fahrten heutiger Tag pro Mitglied in der Wertigkeit Ihrer Ready-Meldung (die je nach Zeitabstand unterschiedlich hoch ist - erste Meldung am
  449.          * höchsten, spätere Meldung niederwertiger)
  450.          *
  451.          * Tag fängt um 0 Uhr an.
  452.          * Fahrt um 12. Erste Anmeldung am Tag kommt dran (z.B. Anmeldungen um 9 ,11, 11:55) --> Anmeldung von 9 kommt zum Zug
  453.          *
  454.          * falls bereits Fahrt an dem Tag -1, wieder Bereit gemeldet + 1 , Reihenfolge der Anmeldezeiten bleibt gewichtet (zuerst gemeldet = erster)
  455.          *
  456.          *
  457.          * Leute ohne Einträge -> wer die wenigsten Fahrten am Tag hat kommt zum Zug
  458.          *
  459.          *
  460.          *
  461.          * + Fahrten am Tag anzeigen
  462.          * + anzeigee Shortcode
  463.          *
  464.          *
  465.          *
  466.          */
  467.         // gewichten
  468.         $weightsByMember = [];
  469.         $commentsByMember = [];
  470.         $members = [];
  471.         /** @var CoopMember $m */
  472.         foreach ($memberList as $m)
  473.         {
  474.             if (!array_key_exists($m->getId(),$weightsByMember))
  475.             {
  476.                 $memberId $m->getId();
  477.                 $weightsByMember$memberId ] = 100;
  478.                 $commentsByMember$memberId ] = [];
  479.                 if ($m->isCoopPartner())
  480.                 {
  481.                     $weightsByMember$memberId ]  -= 30;
  482.                     $commentsByMember[$memberId] []= '-30 Kooperationspartner';
  483.                 }
  484.                 if (in_array($m->getId(),$interestMemberIds))
  485.                 {
  486.                     $weightsByMember$memberId ]  += 10;
  487.                     $commentsByMember[$memberId] []= '+10 Interesse bekundet';
  488.                 }
  489.             }
  490.             $members[$m->getId()] = $m;
  491.         }
  492.         // auf orderTime liegende Termine führen zu Abzug
  493.         foreach ($avsByMember as $memberId => $avList)
  494.         {
  495.             /** @var Availability $av */
  496.             foreach ($avList as $av)
  497.             {
  498.                 if (!array_key_exists($memberId,$weightsByMember))
  499.                 {
  500.                     continue; // throw new \RuntimeException(sprintf('member#%d unknown in availability#%d',$memberId,$av->getId()));
  501.                 }
  502.                 if ($av->getAvailType()=== Availability::NOT_AVAILABLE)
  503.                 {
  504.                     if (in_array($memberId,$interestMemberIds))
  505.                     {
  506.                         // falls intessiert zählt N/V nicht
  507.                         $commentsByMember[$memberId] []= '-0 Nicht verfügbar (nicht gezählt da Interesse bekundet)';
  508.                     }
  509.                     else
  510.                     {
  511.                         $weightsByMember[$memberId] -= 50;
  512.                         $commentsByMember[$memberId] []= '-50 Nicht verfügbar';
  513.                     }
  514.                 }
  515.                 else
  516.                 if ($av->getAvailType()!== Availability::READY)
  517.                 {
  518.                     $weightsByMember[$memberId] -= 1;
  519.                     $commentsByMember[$memberId] []= '-1 Kalendereintrag';
  520.                 }
  521.             }
  522.         }
  523.         // Ready-Meldungen
  524.         foreach ($readyByMember as $memberId => $avList)
  525.         {
  526.             $rides array_key_exists($memberId,$ridesToday) ? $ridesToday[$memberId] : [];
  527.             $lastRide count($rides)>end($rides) : null;
  528.             $lastAv count($avList)>end($avList) : null;
  529.             if (($lastRide!==null)&&($lastAv!==null))
  530.             {
  531.                 if ($lastAv->getAvailFrom() > $lastRide->getOrderTime())
  532.                 {
  533.                     $minutes = ($now->getTimestamp() - $av->getAvailFrom()->getTimestamp())/60;
  534.                     $faktor max(floor($minutes/30),1);
  535.                     $weightsByMember[$memberId] += $faktor;
  536.                     $commentsByMember[$memberId] [] = sprintf("+%d Verfügbar nach Fahrt",$faktor);
  537.                 }
  538.             }
  539.             else
  540.             if ($lastAv!==null)
  541.             {   // an dieser Stelle Verfügbar ber bisher keine Fahrt heute
  542.                 $minutes = ($now->getTimestamp() - $av->getAvailFrom()->getTimestamp())/60;
  543.                 $faktor max(ceil($minutes/30),1);
  544.                 $weightsByMember[$memberId] += $faktor;
  545.                 $commentsByMember[$memberId] [] = sprintf("+%d Verfügbar heute",$faktor);
  546.             }
  547.             else
  548.             if ($lastRide!==null)
  549.             {
  550.                 // an diesr Stelle Fahrt aber keine Verfügbarkeitsmeldung
  551.                 $weightsByMember[$memberId] -= 5;
  552.                 $commentsByMember[$memberId] [] = "-5 Fahrt um".$lastRide->getOrderTime()->format("H:i");
  553.             }
  554.         }
  555.         arsort($weightsByMember); // sort by descending weight
  556.         return $this->getJsonDataResponse(
  557.             $request,
  558.             true,
  559.             [
  560.                 'html' =>
  561.                         $this->renderView('@DiplixKMG/Dispatching/member-availabilites-for-order.html.twig',[
  562.                             'weight'=>$weightsByMember,
  563.                             'members'=>$members,
  564.                             'avByMember'=>$avsByMember,
  565.                             'readyByMember'=>$readyByMember,
  566.                             'avDisplayMap' => Availability::$displayMap,
  567.                             'avColorMap'=> Availability::$colorMap
  568.                         ]),
  569.                 'weight'=>$weightsByMember,
  570.                 'members'=>$members,
  571.                 'avByMember'=>$avsByMember,
  572.                 'readyByMember'=>$readyByMember,
  573.                 'avDisplayMap' => Availability::$displayMap,
  574.                 'avColorMap'=> Availability::$colorMap,
  575.                 'comments'=>$commentsByMember
  576.             ]
  577.         );
  578.     }
  579.     public function testAllocatorAction(Request $request)
  580.     {
  581.         $this->init();
  582.         $orderRepo $this->managerRegistry->getRepository(Order::class);
  583.         // Bestellungen
  584.         $start = (new \DateTime())->modify('+ 1 day')->setTime(0,0,0,0);
  585.         $end = clone $start;
  586.         $end->setTime(8,59,59);
  587.         $orders $orderRepo->findNotAssignedToAnyMemberForDateRange($start,$end);
  588.         $memberRepo $this->managerRegistry->getRepository(CoopMember::class);
  589.         $members $memberRepo->findBy(['showInCalendar'=>true]);
  590.         $membersById = [];
  591.         foreach ($members as $m)
  592.         {
  593.             $membersById[$m->getId()] = $m;
  594.         }
  595.         $setRepo $this->managerRegistry->getRepository(Setting::class);
  596.         $allocEnabled $setRepo->getOptional(Setting::AUTOMATIC_RIDE_ASSIGNMENT,false);
  597.         // assignment
  598.         $arrLog = new ArrayLogWrapper();
  599.         $jac = new JobAllocator($this->managerRegistry->getManager(),$arrLog);
  600.         $results $jac->assignForNextDayProposal();
  601.         return $this->render('@DiplixKMG/Dispatching/alloc.html.twig',[
  602.             'orders'=>$orders,
  603.             'allocEnabled'=>$allocEnabled,
  604.             'members' => $membersById,
  605.             'map'=>$results,
  606.             'log'=> $arrLog->get()
  607.         ]);
  608.     }
  609.     public function setAutomaticAction(Request $request,$mode)
  610.     {
  611.         $this->init();
  612.         $setRepo $this->managerRegistry->getRepository(Setting::class);
  613.         $setRepo->set(Setting::AUTOMATIC_RIDE_ASSIGNMENT,$mode==='on'?true:false);
  614.         return $this->redirectToRoute('dispatch-preview-allocator');
  615.     }
  616.     public function checkLoggedInAction(Request $request)
  617.     {
  618.         // actually this is not working in combination with the configured firewall/access_control
  619.         // as long as we can reach this point and send any response we are still logged in
  620.         // otherwise symfony triggers a redirect
  621.         if(!$this->isGranted(Role::USER))
  622.         {
  623.             return $this->getJsonDataResponse($request,false);
  624.         }
  625.         return $this->getJsonDataResponse($request,true);
  626.     }
  627.     public function editNoteAction(Request $request $noteId)
  628.     {
  629.         $this->init();
  630.         $em $this->managerRegistry->getManager();
  631.         $noteRepo $em->getRepository(Note::class);
  632.         if ($noteId==0)
  633.         {
  634.             $row = new Note();
  635.             $row->setStartTime(new \DateTimeImmutable());
  636.         }
  637.         else
  638.         {
  639.             $row $noteRepo->find($noteId);
  640.             if ($row===null)
  641.             {
  642.                 return $this->getJsonDataResponse($request,false,null,'Notiz nicht gefunden');
  643.             }
  644.         }
  645.         try {
  646.             $form $this->createForm(NoteForm::class,$row,[]);
  647.             $form->handleRequest($request);
  648.             if ($form->isSubmitted())
  649.             {
  650.                 if ($form->isValid())
  651.                 {
  652.                     $em->persist($row);
  653.                     $em->flush();
  654.                     return $this->getJsonDataResponse($request,true);
  655.                 }
  656.                 return $this->getJsonDataResponse($request,false,
  657.                     $this->renderView('@DiplixKMG/Default/dialog_edit.html.twig',[
  658.                         'form' =>$form->createView(),
  659.                         'row' =>$row,
  660.                     ]),
  661.                     'Bitte überprüfen Sie Ihre Eingabe. '.$form->getErrors()->__toString());
  662.             }
  663.             return $this->getJsonDataResponse(
  664.                 $request,
  665.                 true,
  666.                 $this->renderView('@DiplixKMG/Default/dialog_edit.html.twig',[
  667.                     'form' =>$form->createView(),
  668.                     'row' =>$row,
  669.                 ])
  670.             );
  671.         }
  672.         catch (\Exception $ex)
  673.         {
  674.             //throw $ex;
  675.             return $this->getJsonDataResponse($request,false,$ex->getMessage(),$ex->getMessage());
  676.         }
  677.     }
  678.     public function doneNoteAction(Request $request $noteId)
  679.     {
  680.         $this->init();
  681.         $em $this->managerRegistry->getManager();
  682.         $noteRepo $em->getRepository(Note::class);
  683.               $row $noteRepo->find($noteId);
  684.         if ($row===null)
  685.         {
  686.             return $this->getJsonDataResponse($request,false,null,'Notiz nicht gefunden');
  687.         }
  688.         try {
  689.             $form $this->createForm(NoteDoneForm::class,$row,[]);
  690.             $form->handleRequest($request);
  691.             if ($form->isSubmitted())
  692.             {
  693.                 if ($form->isValid())
  694.                 {
  695.                     $row->setDone(new \DateTimeImmutable());
  696.                     $em->flush();
  697.                     return $this->getJsonDataResponse($request,true);
  698.                 }
  699.                 return $this->getJsonDataResponse($request,false,
  700.                     $this->renderView('@DiplixKMG/Default/dialog_edit.html.twig',[
  701.                         'form' =>$form->createView(),
  702.                         'row' =>$row,
  703.                     ]),
  704.                     'Bitte überprüfen Sie Ihre Eingabe. '.$form->getErrors()->__toString());
  705.             }
  706.             return $this->getJsonDataResponse(
  707.                 $request,
  708.                 true,
  709.                 $this->renderView('@DiplixKMG/Default/dialog_edit.html.twig',[
  710.                     'form' =>$form->createView(),
  711.                     'row' =>$row,
  712.                 ])
  713.             );
  714.         }
  715.         catch (\Exception $ex)
  716.         {
  717.             //throw $ex;
  718.             return $this->getJsonDataResponse($request,false,$ex->getMessage(),$ex->getMessage());
  719.         }
  720.     }
  721. }