Lines 88.63% 156 / 176
Methods 63.63% 7 / 11
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 100.00% 80 / 80 100.00% 1 / 1 1
 resolveSelectedDateTime 100.00% 2 / 2 100.00% 1 / 1 3
 readProcessListForDateIfAllowed 100.00% 6 / 6 100.00% 1 / 1 2
 readChangedProcess 40.00% 2 / 5 0.00% 0 / 1 2.86
 readWaitingClientsEffective 15.38% 2 / 13 0.00% 0 / 1 8.45
 readCalledQueueList 96.15% 25 / 26 0.00% 0 / 1 6
 getQueueListByPermission 100.00% 3 / 3 100.00% 1 / 1 2
 [BO\Zmsadmin\BaseController] __invoke 100.00% 3 / 3 100.00% 1 / 1 1
 [BO\Zmsadmin\BaseController] getSchemaConstraintList 100.00% 8 / 8 100.00% 1 / 1 4
 [BO\Zmsadmin\BaseController] transformValidationErrors 61.53% 8 / 13 0.00% 0 / 1 15.69
 [BO\Zmsadmin\BaseController] handleEntityWrite 100.00% 17 / 17 100.00% 1 / 1 5
15class QueueTable extends BaseController
16{
17    protected $processStatusList = ['preconfirmed', 'confirmed', 'queued', 'reserved', 'deleted'];
18
19    private const array QUEUE_VIEW_PERMISSIONS = [
20        'waitingqueue',
21        'parkedqueue',
22        'missedqueue',
23        'finishedqueue',
24    ];
25
26    /**
27     * @SuppressWarnings(Param)
28     * @return \Psr\Http\Message\ResponseInterface
29     */
30    #[\Override]
31    public function readResponse(
32        \Psr\Http\Message\RequestInterface $request,
33        \Psr\Http\Message\ResponseInterface $response,
34        array $args
35    ): \Psr\Http\Message\ResponseInterface {
36        $validator = $request->getAttribute('validator');
37        $success = $validator->getParameter('success')->isString()->getValue();
38        $withCalledList = $validator->getParameter('withCalled')->isBool()->getValue();
39        $includeWaitingClientsEffective = $validator
40            ->getParameter('includeWaitingClientsEffective')
41            ->isBool()
42            ->getValue();
43        $selectedDateTime = $this->resolveSelectedDateTime(
44            $validator->getParameter('selecteddate')->isString()->getValue()
45        );
46        $selectedProcessId = $validator->getParameter('selectedprocess')->isNumber()->getValue();
47
48        $workstation = \App::$http->readGetResult('/workstation/', [
49            'resolveReferences' => 1,
50            'gql' => Helper\GraphDefaults::getWorkstation()
51        ])->getEntity();
52        $workstationRequest = new WorkstationRequests(\App::$http, $workstation);
53        $department = $workstationRequest->readDepartment();
54        $useraccount = $workstation->getUseraccount();
55
56        $processList = $this->readProcessListForDateIfAllowed(
57            $workstationRequest,
58            $useraccount,
59            $selectedDateTime
60        );
61        $changedProcess = $this->readChangedProcess($selectedProcessId);
62        $queueList = $processList->toQueueList(\App::$now);
63        $waitingClientsEffective = $this->readWaitingClientsEffective(
64            $includeWaitingClientsEffective,
65            $queueList,
66            $workstationRequest,
67            $useraccount,
68            $selectedDateTime
69        );
70
71        $queueListVisible = $this->getQueueListByPermission(
72            $queueList,
73            $useraccount,
74            'waitingqueue',
75            $this->processStatusList
76        );
77        $queueListMissed = $this->getQueueListByPermission(
78            $queueList,
79            $useraccount,
80            'missedqueue',
81            ['missed']
82        );
83        $queueListParked = $this->getQueueListByPermission(
84            $queueList,
85            $useraccount,
86            'parkedqueue',
87            ['parked']
88        );
89        $queueListFinished = $this->getQueueListByPermission(
90            $queueList,
91            $useraccount,
92            'finishedqueue',
93            ['finished']
94        );
95        $queueListCalled = $this->readCalledQueueList($withCalledList, $useraccount);
96
97        return \BO\Slim\Render::withHtml(
98            $response,
99            'block/queue/table.twig',
100            array(
101                'workstation' => $workstation->getArrayCopy(),
102                'department' => $department,
103                'source' => $workstation->getVariantName(),
104                'selectedDate' => $selectedDateTime->format('Y-m-d'),
105                'cluster' => $workstationRequest->readCluster(),
106                'clusterEnabled' => $workstation->isClusterEnabled(),
107                'processList' => $queueListVisible->toProcessList(),
108                'waitingClientsEffective' => $waitingClientsEffective,
109                'processListMissed' => $queueListMissed->toProcessList(),
110                'processListParked' => $queueListParked->toProcessList(),
111                'processListFinished' => $queueListFinished->toProcessList(),
112                'showCalledList' => $withCalledList,
113                'queueListCalled' => $queueListCalled,
114                'changedProcess' => $changedProcess,
115                'success' => $success,
116                'debug' => \App::DEBUG,
117                'allowClusterWideCall' => \App::$allowClusterWideCall
118            )
119        );
120    }
121
122    private function resolveSelectedDateTime(?string $selectedDate): \DateTimeImmutable
123    {
124        $selectedDateTime = $selectedDate ? new \DateTimeImmutable($selectedDate) : \App::$now;
125        return ($selectedDateTime < \App::$now) ? \App::$now : $selectedDateTime;
126    }
127
128    private function readProcessListForDateIfAllowed(
129        WorkstationRequests $workstationRequest,
130        Useraccount $useraccount,
131        \DateTimeInterface $selectedDateTime
132    ): ProcessList {
133        if (! $useraccount->hasAnyPermission(self::QUEUE_VIEW_PERMISSIONS)) {
134            return new ProcessList();
135        }
136
137        return $workstationRequest->readProcessListByDate(
138            $selectedDateTime,
139            Helper\GraphDefaults::getProcess()
140        );
141    }
142
143    private function readChangedProcess(?int $selectedProcessId)
144    {
145        if (! $selectedProcessId) {
146            return null;
147        }
148
149        return \App::$http->readGetResult('/process/' . $selectedProcessId . '/', [
150            'gql' => Helper\GraphDefaults::getProcess()
151        ])->getEntity();
152    }
153
154    private function readWaitingClientsEffective(
155        ?bool $includeWaitingClientsEffective,
156        QueueList $queueList,
157        WorkstationRequests $workstationRequest,
158        Useraccount $useraccount,
159        \DateTimeInterface $selectedDateTime
160    ): ?int {
161        if (! $includeWaitingClientsEffective) {
162            return null;
163        }
164
165        $waitingClientsQueueList = $queueList;
166        if ($selectedDateTime->format('Y-m-d') !== \App::$now->format('Y-m-d')) {
167            $waitingClientsQueueList = $this->readProcessListForDateIfAllowed(
168                $workstationRequest,
169                $useraccount,
170                \App::$now
171            )->toQueueList(\App::$now);
172        }
173
174        return $waitingClientsQueueList
175            ->withStatus($this->processStatusList)
176            ->getCountWithWaitingTime(\App::$now)
177            ->count();
178    }
179
180    /**
181     * @return QueueList|array
182     */
183    private function readCalledQueueList(?bool $withCalledList, Useraccount $useraccount)
184    {
185        if (! $withCalledList || ! $useraccount->hasPermissions(['openqueue'])) {
186            return [];
187        }
188
189        try {
190            $queueListCalled = \App::$http
191                ->readGetResult(
192                    '/useraccount/queue/',
193                    [
194                        'resolveReferences' => 2,
195                        'status' => 'called,processing',
196                    ]
197                )
198                ->getCollection() ?? [];
199        } catch (\BO\Zmsclient\Exception $exception) {
200            \App::$log->error('Failed to load called queue list', [
201                'error' => $exception->getMessage(),
202            ]);
203            return [];
204        }
205
206        if (! ($queueListCalled instanceof QueueList)) {
207            return [];
208        }
209
210        $queueListCalled->uasort(function ($queueA, $queueB) {
211            $statusOrder = ['called' => 0, 'processing' => 1];
212
213            $statusValueA = $statusOrder[$queueA->status] ?? PHP_INT_MAX;
214            $statusValueB = $statusOrder[$queueB->status] ?? PHP_INT_MAX;
215
216            $cmp = $statusValueA <=> $statusValueB;
217            return $cmp !== 0 ? $cmp : $queueB->callTime <=> $queueA->callTime;
218        });
219
220        return $queueListCalled;
221    }
222
223    private function getQueueListByPermission(
224        QueueList $queueList,
225        Useraccount $useraccount,
226        string $permission,
227        array $statuses
228    ): QueueList {
229        if (! $useraccount->hasPermissions([$permission])) {
230            return new QueueList();
231        }
232
233        return $queueList->withStatus($statuses);
234    }
235}

Inherited from BO\Zmsadmin\BaseController

21    public function __invoke(RequestInterface $request, ResponseInterface $response, array $args)
22    {
23        $request = $this->initRequest($request);
24        $noCacheResponse = \BO\Slim\Render::withLastModified($response, time(), '0');
25        return $this->readResponse($request, $noCacheResponse, $args);
26    }
41    public function getSchemaConstraintList($schema): array
42    {
43        $list = [];
44        $locale = \App::$language->getLocale();
45        foreach ($schema->properties as $key => $property) {
46            if (isset($property['x-locale'])) {
47                $constraints = $property['x-locale'][$locale];
48                if ($constraints) {
49                    $list[$key]['description'] = $constraints['messages'];
50                }
51            }
52        }
53        return $list;
54    }
65    protected function transformValidationErrors($errorData)
66    {
67        if (!is_array($errorData) && !($errorData instanceof \Traversable)) {
68            return [];
69        }
70        $transformed = [];
71        foreach ($errorData as $pointer => $item) {
72            // Extract field name from JSON pointer (e.g., "/id" -> "id", "/contact/email" -> "contact/email")
73            // If the key doesn't start with "/", it's already a field name, so use it as-is
74            $fieldName = (strpos($pointer, '/') === 0) ? ltrim($pointer, '/') : $pointer;
75            // Handle root level errors
76            if ($fieldName === '' || $fieldName === null) {
77                $fieldName = '_root';
78            }
79            // Ensure the item structure is correct (has 'messages' array)
80            if (is_array($item) && isset($item['messages'])) {
81                $transformed[$fieldName] = $item;
82            } elseif (is_array($item)) {
83                // If item is an array but doesn't have 'messages', wrap it
84                $transformed[$fieldName] = $item;
85            } else {
86                $transformed[$fieldName] = $item;
87            }
88        }
89        return $transformed;
90    }
99    protected function handleEntityWrite(callable $httpCall)
100    {
101        try {
102            return $httpCall();
103        } catch (\BO\Zmsclient\Exception $exception) {
104            if ('BO\Zmsentities\Exception\SchemaValidation' == $exception->template) {
105                return [
106                    'template' => 'exception/bo/zmsentities/exception/schemavalidation.twig',
107                    'include' => true,
108                    'data' => $this->transformValidationErrors($exception->data)
109                ];
110            }
111
112            $template = TwigExceptionHandler::getExceptionTemplate($exception);
113            if (
114                '' != $exception->template
115                && \App::$slim->getContainer()->get('view')->getLoader()->exists($template)
116            ) {
117                return [
118                    'template' => $template,
119                    'include' => true,
120                    'data' => $this->transformValidationErrors($exception->data)
121                ];
122            }
123
124            throw $exception;
125        }
126    }