Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.00% covered (success)
90.00%
99 / 110
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReportHelper
90.00% covered (success)
90.00%
99 / 110
72.73% covered (warning)
72.73%
8 / 11
62.48
0.00% covered (danger)
0.00%
0 / 1
 withMaxAndAverage
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
11
 withTotalCustomers
90.48% covered (success)
90.48%
19 / 21
0.00% covered (danger)
0.00%
0 / 1
7.04
 withGlobalMaxAndAverage
77.78% covered (warning)
77.78%
21 / 27
0.00% covered (danger)
0.00%
0 / 1
17.47
 formatTimeValue
62.50% covered (warning)
62.50%
5 / 8
0.00% covered (danger)
0.00%
0 / 1
3.47
 extractSelectedScopes
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getWorkstationScopeId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 resolveScopeIdParam
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 extractDateRange
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 isValidDateFormat
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getYearsForDateRange
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getYearDateBounds
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace BO\Zmsstatistic\Helper;
4
5use DateTime;
6use DateTimeImmutable;
7
8class ReportHelper
9{
10    public static function withMaxAndAverage($entity, $targetKey)
11    {
12        foreach ($entity->data as $date => $dateItems) {
13            $maxima = 0;
14            $total = 0;
15            $count = 0;
16            foreach ($dateItems as $hourItems) {
17                if (is_array($hourItems)) { // Check if $hourItems is an array
18                    foreach ($hourItems as $key => $value) {
19                        if (is_numeric($value) && $targetKey == $key && 0 < $value) {
20                            $total += $value;
21                            $count += 1;
22                            $maxima = ($maxima > $value) ? $maxima : $value;
23                        }
24                    }
25                }
26            }
27            $entity->data[$date]['max_' . $targetKey] = $maxima;
28            $entity->data[$date]['average_' . $targetKey] = (! $total || ! $count) ? 0 : $total / $count;
29        }
30        return $entity;
31    }
32
33    public static function withTotalCustomers($entity)
34    {
35        foreach ($entity->data as $dateKey => $dateItems) {
36            if (!is_array($dateItems)) {
37                continue;
38            }
39
40            foreach ($dateItems as $hour => $hourItems) {
41                if (!is_array($hourItems)) {
42                    continue;
43                }
44
45                $countSpontan = (int) ($hourItems['waitingcount'] ?? 0);
46                $countTermin  = (int) ($hourItems['waitingcount_termin'] ?? 0);
47                $countTotal   = $countSpontan + $countTermin;
48
49                $waitSpontan = (float) ($hourItems['waitingtime'] ?? 0);
50                $waitTermin  = (float) ($hourItems['waitingtime_termin'] ?? 0);
51
52                $waySpontan  = (float) ($hourItems['waytime'] ?? 0);
53                $wayTermin   = (float) ($hourItems['waytime_termin'] ?? 0);
54
55                $entity->data[$dateKey][$hour]['waitingcount_total'] = $countTotal;
56
57                $entity->data[$dateKey][$hour]['waitingtime_total'] = ($countTotal > 0)
58                    ? (($waitSpontan * $countSpontan) + ($waitTermin * $countTermin)) / $countTotal
59                    : 0;
60
61                $entity->data[$dateKey][$hour]['waytime_total'] = ($countTotal > 0)
62                    ? (($waySpontan * $countSpontan) + ($wayTermin * $countTermin)) / $countTotal
63                    : 0;
64            }
65        }
66
67        return $entity;
68    }
69
70    public static function withGlobalMaxAndAverage($entity, string $targetKey)
71    {
72        $maxima = 0;
73        $total  = 0;
74        $count  = 0;
75
76        foreach ($entity->data as $dateItems) {
77            if (!is_array($dateItems)) {
78                continue;
79            }
80            foreach ($dateItems as $hourItems) {
81                if (!is_array($hourItems)) {
82                    continue;
83                }
84                $value = $hourItems[$targetKey] ?? null;
85                if (is_numeric($value) && $value > 0) {
86                    $value  = (float) $value;
87                    $maxima = ($maxima > $value) ? $maxima : $value;
88                    $total += $value;
89                    $count++;
90                }
91            }
92        }
93
94        $average = ($count > 0) ? ($total / $count) : 0;
95
96        if (is_object($entity->data)) {
97            if (!isset($entity->data->max) || !is_array($entity->data->max)) {
98                $entity->data->max = [];
99            }
100            $entity->data->max['max_' . $targetKey] = $maxima;
101            $entity->data->max['average_' . $targetKey] = $average;
102        } elseif (is_array($entity->data)) {
103            if (!isset($entity->data['max']) || !is_array($entity->data['max'])) {
104                $entity->data['max'] = [];
105            }
106            $entity->data['max']['max_' . $targetKey] = $maxima;
107            $entity->data['max']['average_' . $targetKey] = $average;
108        }
109
110        return $entity;
111    }
112
113    public static function formatTimeValue($value)
114    {
115        if (!is_numeric($value)) {
116            return $value;
117        }
118        $minutes = floor($value);
119        $seconds = round(($value - $minutes) * 60);
120        if ($seconds >= 60) {
121            $minutes += 1;
122            $seconds = 0;
123        }
124        return sprintf('%02d:%02d', $minutes, $seconds);
125    }
126
127    /**
128     * Extract selected scope IDs from request parameters
129     */
130    public function extractSelectedScopes(array $scopes): array
131    {
132        if (!empty($scopes)) {
133            $validScopes = array_filter($scopes, function ($scopeId) {
134                return is_numeric($scopeId) && $scopeId > 0;
135            });
136
137            if (!empty($validScopes)) {
138                return array_map('intval', $validScopes);
139            }
140        }
141
142        return [];
143    }
144
145    /**
146     * Workstation scope id when the user has selected a default location, otherwise null.
147     */
148    public function getWorkstationScopeId($workstation): ?int
149    {
150        $scopeId = (int) ($workstation->scope['id'] ?? 0);
151
152        return $scopeId > 0 ? $scopeId : null;
153    }
154
155    /**
156     * Scope id(s) for warehouse report queries from form selection or workstation default.
157     */
158    public function resolveScopeIdParam(array $selectedScopes, ?int $workstationScopeId): string
159    {
160        if (!empty($selectedScopes)) {
161            return implode(',', $selectedScopes);
162        }
163
164        return $workstationScopeId !== null ? (string) $workstationScopeId : '';
165    }
166
167    /**
168     * Extract and validate date range from request parameters
169     */
170    public function extractDateRange(?string $fromDate, ?string $toDate): ?array
171    {
172        if ($fromDate && $toDate && $this->isValidDateFormat($fromDate) && $this->isValidDateFormat($toDate)) {
173            return [
174                'from' => $fromDate,
175                'to' => $toDate
176            ];
177        }
178
179        return null;
180    }
181
182    /**
183     * Validate if the given string is a valid date format (YYYY-MM-DD)
184     */
185    public function isValidDateFormat(string $date): bool
186    {
187        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
188            return false;
189        }
190
191        $dateTime = DateTime::createFromFormat('Y-m-d', $date);
192        return $dateTime && $dateTime->format('Y-m-d') === $date;
193    }
194
195    /**
196     * Get all years that need to be fetched for a date range
197     */
198    public function getYearsForDateRange(string $fromDate, string $toDate): array
199    {
200        $fromYear = (int) substr($fromDate, 0, 4);
201        $toYear = (int) substr($toDate, 0, 4);
202
203        $years = [];
204        for ($year = $fromYear; $year <= $toYear; $year++) {
205            $years[] = $year;
206        }
207
208        return $years;
209    }
210    public function getYearDateBounds(int|string $year, string $fromDate, string $toDate): ?array
211    {
212        $requestedFrom = new DateTimeImmutable($fromDate);
213        $requestedTo = new DateTimeImmutable($toDate);
214        $yearStart = new DateTimeImmutable($year . '-01-01');
215        $yearEnd = new DateTimeImmutable($year . '-12-31');
216
217        $yearFrom = $requestedFrom > $yearStart ? $requestedFrom : $yearStart;
218        $yearTo = $requestedTo < $yearEnd ? $requestedTo : $yearEnd;
219
220        if ($yearFrom > $yearTo) {
221            return null;
222        }
223
224        return [
225            'from' => $yearFrom->format('Y-m-d'),
226            'to' => $yearTo->format('Y-m-d'),
227        ];
228    }
229}