Lines 91.12% 113 / 124
Methods 71.42% 5 / 7
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 readResponse 91.30% 63 / 69 0.00% 0 / 1 10.07
 escapeSpreadsheetFormula 100.00% 5 / 5 100.00% 1 / 1 4
 convertspecialchars 100.00% 9 / 9 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
18class ScopeAppointmentsByDayXlsExport extends BaseController
19{
20    /**
21     *
22     * @return \Psr\Http\Message\ResponseInterface
23     */
24    #[\Override]
25    public function readResponse(
26        \Psr\Http\Message\RequestInterface $request,
27        \Psr\Http\Message\ResponseInterface $response,
28        array $args
29    ): \Psr\Http\Message\ResponseInterface {
30        $workstation = \App::$http->readGetResult('/workstation/', [
31            'resolveReferences' => 1,
32            'gql' => Helper\GraphDefaults::getWorkstation()
33        ])->getEntity();
34        $workstationRequest = new \BO\Zmsclient\WorkstationRequests(\App::$http, $workstation);
35        $selectedDateTime = ScopeAppointmentsByDay::readSelectedDateTime($args['date']);
36        $scope = ScopeAppointmentsByDay::readSelectedScope($workstation, $workstationRequest, $args['id']);
37        $processList = ScopeAppointmentsByDay::readProcessList($workstationRequest, $selectedDateTime);
38
39        $xlsSheetTitle = $selectedDateTime->format('d.m.Y');
40        $clusterColumn = $workstation->isClusterEnabled() ? 'Kürzel' : 'Lfd. Nummer';
41        $customTextfieldActivated = (int) $scope->getCustomTextfieldActivated() === 1;
42        $customTextfield2Activated = (int) $scope->getCustomTextfield2Activated() === 1;
43
44        $xlsHeaders = [
45            $clusterColumn,
46            'Uhrzeit/Ankunftszeit',
47            'Nr.',
48            'Name',
49            'Telefon',
50            'Email',
51            'Dienstleistung',
52            'Anmerkungen',
53        ];
54
55        if ($customTextfieldActivated) {
56            $label = trim((string) $scope->getCustomTextfieldLabel());
57            $xlsHeaders[] = $label !== '' ? $label : 'Freitextfeld 1';
58        }
59        if ($customTextfield2Activated) {
60            $label = trim((string) $scope->getCustomTextfield2Label());
61            $xlsHeaders[] = $label !== '' ? $label : 'Freitextfeld 2';
62        }
63
64        $rows = [];
65        $key = 1;
66        foreach ($processList as $queueItem) {
67            $client = $queueItem->getFirstClient();
68            $row = [
69                $workstation->isClusterEnabled() ? $queueItem->getCurrentScope()->shortName : $key++,
70                $queueItem->getArrivalTime()->setTimezone(\App::$now->getTimezone())->format('H:i:s'),
71                $queueItem->queue['number'],
72                $client['familyName'],
73                $client['telephone'],
74                $client['email'],
75                $queueItem->requests->getCsvForProperty('name'),
76                $queueItem->amendment,
77            ];
78            if ($customTextfieldActivated) {
79                $row[] = $queueItem->customTextfield;
80            }
81            if ($customTextfield2Activated) {
82                $row[] = $queueItem->customTextfield2;
83            }
84            $rows[] = array_map([$this, 'escapeSpreadsheetFormula'], $row);
85        }
86
87        $spreadsheet = new Spreadsheet();
88        $spreadsheet->getProperties()
89            ->setCreator('eappointment')
90            ->setTitle('Tagesübersicht ' . $xlsSheetTitle);
91        $spreadsheet->getActiveSheet()->fromArray(
92            array_merge([$xlsHeaders], $rows),
93            null,
94            'A1'
95        );
96
97        $resource = fopen('php://temp', 'r+');
98        IOFactory::createWriter($spreadsheet, 'Xlsx')->save($resource);
99        rewind($resource);
100        $response->getBody()->write(stream_get_contents($resource));
101        fclose($resource);
102
103        $fileName = sprintf("Tagesübersicht_%s_%s.xlsx", $scope->contact['name'], $xlsSheetTitle);
104        return $response
105            ->withHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
106            ->withHeader('Content-Description', 'File Transfer')
107            ->withHeader(
108                'Content-Disposition',
109                sprintf('attachment; filename="%s"', $this->convertspecialChars($fileName))
110            );
111    }
112
113    protected function escapeSpreadsheetFormula($value)
114    {
115        if (!is_string($value) || $value === '') {
116            return $value;
117        }
118        if (preg_match('/^[=\-+@\t\r]/u', $value)) {
119            return "'" . $value;
120        }
121        return $value;
122    }
123
124    /**
125     * @psalm-api
126     *
127     * @return string|string[]
128     *
129     */
130    protected function convertspecialchars(string $string): array|string
131    {
132
133        $convert = array (
134            array ('ä','ae',),
135            array ('ö','oe',),
136            array ('ü','ue',),
137            array ('ß','ss',),
138        );
139
140
141        foreach ($convert as $array) {
142            $string = str_replace($array[0], $array[1], $string);
143        }
144        return $string;
145    }
146}

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    }