Lines 96.21% 127 / 132
Methods 90.00% 9 / 10
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 100.00% 5 / 5 100.00% 1 / 1 1
 [BO\Zmsadmin\ScopeAvailabilityDay] getScope 100.00% 3 / 3 100.00% 1 / 1 1
 [BO\Zmsadmin\ScopeAvailabilityDay] getSlotBuckets 100.00% 27 / 27 100.00% 1 / 1 7
 [BO\Zmsadmin\ScopeAvailabilityDay] getAvailabilityData 100.00% 31 / 31 100.00% 1 / 1 3
 [BO\Zmsadmin\ScopeAvailabilityDay] readConflictList 100.00% 12 / 12 100.00% 1 / 1 2
 [BO\Zmsadmin\ScopeAvailabilityDay] readAvailabilityList 100.00% 13 / 13 100.00% 1 / 1 3
 [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
12class ScopeAvailabilityDayConflicts extends ScopeAvailabilityDay
13{
14    /**
15     * @SuppressWarnings(Param)
16     * @return \Psr\Http\Message\ResponseInterface
17     */
18    #[\Override]
19    public function readResponse(
20        \Psr\Http\Message\RequestInterface $request,
21        \Psr\Http\Message\ResponseInterface $response,
22        array $args
23    ): \Psr\Http\Message\ResponseInterface {
24        $data = static::getAvailabilityData($args['id'], $args['date']);
25        return Render::withJson(
26            $response,
27            $data
28        );
29    }
30}

Inherited from BO\Zmsadmin\ScopeAvailabilityDay

46    protected static function getScope($scopeId)
47    {
48        return \App::$http->readGetResult('/scope/' . $scopeId . '/', [
49            'resolveReferences' => 3
50        ])->getEntity();
51    }
57    protected static function getSlotBuckets($availabilityList, $processList): array
58    {
59        $availability = $availabilityList->getFirst();
60
61        if (!$availability) {
62            return [];
63        }
64
65        $buckets = [];
66
67        $slotTimeInMinutes = $availability->getSlotTimeInMinutes();
68
69        foreach ($availabilityList->getSlotListByType('appointment') as $slot) {
70            $time = $slot->time;
71            $buckets[$time] = [
72                'time' => $time,
73                'timeString' => $slot->getTimeString(),
74                'public' => $slot->public,
75                'intern' => $slot->intern,
76                'occupiedCount' => 0,
77            ];
78        }
79
80        foreach ($processList as $process) {
81            $startTime = $process->getAppointments()->getFirst()->getStartTime()->format('H:i');
82            $endTime = $process->getAppointments()->getFirst()->getEndTimeWithCustomSlotTime($slotTimeInMinutes)->format('H:i');
83
84            $startDateTime = new \DateTime($startTime);
85            $endDateTime = new \DateTime($endTime);
86
87            foreach (array_keys($buckets) as $time) {
88                $slotDateTime = new \DateTime($time);
89                if ($slotDateTime >= $startDateTime && $slotDateTime < $endDateTime) {
90                    $buckets[$time]['occupiedCount']++;
91                }
92            }
93        }
94
95        uksort($buckets, function ($time1, $time2) {
96            return strtotime($time1) <=> strtotime($time2);
97        });
98
99        return $buckets;
100    }
106    protected static function getAvailabilityData(int $scopeId, $dateString): array
107    {
108        $scope = static::getScope($scopeId);
109        $dateTime = new DateTime($dateString);
110        $dateWithTime = $dateTime->setTime(\App::$now->format('H'), \App::$now->format('i'));
111        $availabilityList = static::readAvailabilityList($scopeId, $dateWithTime);
112        $processList = \App::$http
113            ->readGetResult('/scope/' . $scopeId . '/process/' . $dateWithTime->format('Y-m-d') . '/')
114                ->getCollection()
115                ->toQueueList($dateWithTime)
116                ->withoutStatus(['fake'])
117                ->toProcessList();
118        if (!$processList->count()) {
119            $processList = new ProcessList();
120        }
121
122
123        $conflictList = static::readConflictList($scopeId, $dateWithTime);
124        $maxSlots = $availabilityList->getSummerizedSlotCount();
125        $busySlots = $availabilityList->getCalculatedSlotCount($processList);
126
127        return [
128            'slotBuckets' => static::getSlotBuckets($availabilityList, $processList),
129            'scope' => $scope,
130            'availabilityList' => $availabilityList->getArrayCopy(),
131            'conflicts' => ($conflictList) ? $conflictList
132                ->setConflictAmendment()
133                ->getArrayCopy() : [],
134            'processList' => $processList->getArrayCopy(),
135            'dateString' => $dateString,
136            'timestamp' => $dateWithTime->getTimestamp(),
137            'menuActive' => 'availability',
138            'maxWorkstationCount' => $availabilityList->getMaxWorkstationCount(),
139            'maxSlotsForAvailabilities' => $maxSlots,
140            'busySlotsForAvailabilities' => $busySlots,
141            'today' => \App::$now->getTimestamp()
142        ];
143    }
145    public static function readConflictList($scopeId, \DateTimeInterface $dateTime)
146    {
147        $processConflictList = \App::$http
148            ->readGetResult('/scope/' . $scopeId . '/conflict/', [
149                'startDate' => $dateTime->format('Y-m-d'),
150                'endDate' => $dateTime->format('Y-m-d')
151            ])
152            ->getCollection();
153        return ($processConflictList) ? $processConflictList
154            ->sortByAppointmentDate()
155            ->withoutDublicatedConflicts()
156            ->toQueueList($dateTime)
157            ->withoutStatus(['fake', 'queued'])
158            ->toProcessList() : null;
159    }
161    public static function readAvailabilityList($scopeId, \DateTimeInterface $dateTime)
162    {
163        try {
164            $availabilityList = \App::$http
165                ->readGetResult(
166                    '/scope/' . $scopeId . '/availability/',
167                    [
168                        'startDate' => $dateTime->format('Y-m-d'), //for skipping old availabilities
169                    ]
170                )
171                ->getCollection()->sortByCustomKey('startDate');
172        } catch (\BO\Zmsclient\Exception $exception) {
173            if ($exception->template != 'BO\Zmsbackend\Availability\Exception\AvailabilityNotFound') {
174                throw $exception;
175            }
176            $availabilityList = new AvailabilityList();
177        }
178        return $availabilityList->withDateTime($dateTime); //withDateTime to check if opened
179    }

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    }