Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
KPIApiController
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 8
1332
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getTodayKPIs
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 getDetailedKPIs
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 getRawKPIs
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
20
 getConfig
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 updateConfig
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
90
 resetConfig
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
56
 clearKPICache
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace BuyerKiosk\Workbook\Controllers;
4
5use BuyerKiosk\Core\Controllers\BaseController;
6use BuyerKiosk\Workbook\KPIService;
7use BuyerKiosk\Workbook\KPIConfig;
8use BuyerKiosk\Workbook\WorkbookAbly;
9use Exception;
10
11/**
12 * KPIApiController - Handles KPI API endpoints
13 */
14class KPIApiController extends BaseController
15{
16    protected $store;
17
18    /**
19     * Constructor
20     *
21     * @param object $app UserFrosting app instance
22     * @param \Store $store Store object
23     */
24    public function __construct($app, \Store $store)
25    {
26        $this->app = $app;
27        $this->store = $store;
28    }
29
30    /**
31     * Get today's KPIs (formatted for display)
32     * GET /api/:typeNum/workbook/kpi/
33     *
34     * @param string $typeNum Store type number
35     */
36    public function getTodayKPIs($typeNum)
37    {
38        try {
39            // Check access
40            if (!$this->app->user->checkStoreGroup($typeNum)) {
41                $this->app->halt(403, json_encode(['error' => 'Access denied']));
42            }
43
44            $kpiService = new KPIService($this->store);
45            $result = $kpiService->getDisplayKPIs();
46
47            $this->app->response->headers->set('Content-Type', 'application/json');
48            echo json_encode($result);
49        } catch (Exception $e) {
50            error_log("KPIApiController: Error in getTodayKPIs - " . $e->getMessage());
51            $this->app->halt(500, json_encode([
52                'error' => 'Failed to fetch KPIs',
53                'message' => $e->getMessage()
54            ]));
55        }
56    }
57
58    /**
59     * Get detailed KPIs for expanded view
60     * GET /api/:typeNum/workbook/kpi/detailed/
61     *
62     * @param string $typeNum Store type number
63     */
64    public function getDetailedKPIs($typeNum)
65    {
66        try {
67            // Check access
68            if (!$this->app->user->checkStoreGroup($typeNum)) {
69                $this->app->halt(403, json_encode(['error' => 'Access denied']));
70            }
71
72            $kpiService = new KPIService($this->store);
73            $result = $kpiService->getDetailedKPIs();
74
75            $this->app->response->headers->set('Content-Type', 'application/json');
76            echo json_encode($result);
77        } catch (Exception $e) {
78            error_log("KPIApiController: Error in getDetailedKPIs - " . $e->getMessage());
79            $this->app->halt(500, json_encode([
80                'error' => 'Failed to fetch detailed KPIs',
81                'message' => $e->getMessage()
82            ]));
83        }
84    }
85
86    /**
87     * Get raw KPI data (for debugging)
88     * GET /api/:typeNum/workbook/kpi/raw/
89     *
90     * @param string $typeNum Store type number
91     */
92    public function getRawKPIs($typeNum)
93    {
94        try {
95            // Check access - require store settings permission for raw data
96            if (!$this->app->user->checkStoreGroup($typeNum)) {
97                $this->app->halt(403, json_encode(['error' => 'Access denied']));
98            }
99
100            if (!$this->app->user->checkAccess('store_settings')) {
101                $this->app->halt(403, json_encode(['error' => 'Permission denied']));
102            }
103
104            $kpiService = new KPIService($this->store);
105            $rawData = $kpiService->getTodayKPIs();
106
107            $date = new \DateTime('now', new \DateTimeZone($this->store->getTimezone()));
108
109            $result = [
110                'success' => true,
111                'date' => $date->format('Y-m-d'),
112                'raw' => $rawData
113            ];
114
115            $this->app->response->headers->set('Content-Type', 'application/json');
116            echo json_encode($result);
117        } catch (Exception $e) {
118            error_log("KPIApiController: Error in getRawKPIs - " . $e->getMessage());
119            $this->app->halt(500, json_encode([
120                'error' => 'Failed to fetch raw KPIs',
121                'message' => $e->getMessage()
122            ]));
123        }
124    }
125
126    /**
127     * Get KPI configuration
128     * GET /api/:typeNum/workbook/kpi/config/
129     *
130     * @param string $typeNum Store type number
131     */
132    public function getConfig($typeNum)
133    {
134        try {
135            // Check access
136            if (!$this->app->user->checkStoreGroup($typeNum)) {
137                $this->app->halt(403, json_encode(['error' => 'Access denied']));
138            }
139
140            $kpiConfig = new KPIConfig($this->store);
141            $config = $kpiConfig->getAll();
142
143            $result = [
144                'success' => true,
145                'config' => $config
146            ];
147
148            $this->app->response->headers->set('Content-Type', 'application/json');
149            echo json_encode($result);
150        } catch (Exception $e) {
151            error_log("KPIApiController: Error in getConfig - " . $e->getMessage());
152            $this->app->halt(500, json_encode([
153                'error' => 'Failed to fetch KPI configuration',
154                'message' => $e->getMessage()
155            ]));
156        }
157    }
158
159    /**
160     * Update KPI configuration
161     * PUT /api/:typeNum/workbook/kpi/config/
162     *
163     * @param string $typeNum Store type number
164     */
165    public function updateConfig($typeNum)
166    {
167        try {
168            // Check access - require store settings permission
169            if (!$this->app->user->checkStoreGroup($typeNum)) {
170                $this->app->halt(403, json_encode(['error' => 'Access denied']));
171            }
172
173            // Check for admin permission - fallback to store_settings if workbook_manage_kpi doesn't exist
174            $hasPermission = $this->app->user->checkAccess('workbook_manage_kpi')
175                          || $this->app->user->checkAccess('store_settings');
176
177            if (!$hasPermission) {
178                $this->app->halt(403, json_encode(['error' => 'Permission denied']));
179            }
180
181            // Get request body
182            $requestBody = $this->app->request->getBody();
183            $data = json_decode($requestBody, true);
184
185            if (!isset($data['config']) || !is_array($data['config'])) {
186                $this->app->halt(400, json_encode(['error' => 'Invalid request: config array required']));
187            }
188
189            $kpiConfig = new KPIConfig($this->store);
190            $success = $kpiConfig->update($data['config']);
191
192            if ($success) {
193                // Clear cache after updating config
194                $this->clearKPICache();
195
196                // Publish to Ably for real-time updates
197                try {
198                    $ably = new WorkbookAbly($typeNum);
199                    $ably->kpiRefresh();
200                } catch (Exception $e) {
201                    error_log("KPIApiController::updateConfig Ably error: " . $e->getMessage());
202                }
203
204                $result = [
205                    'success' => true,
206                    'message' => 'KPI configuration updated successfully'
207                ];
208            } else {
209                $result = [
210                    'success' => false,
211                    'error' => 'Failed to update KPI configuration'
212                ];
213            }
214
215            $this->app->response->headers->set('Content-Type', 'application/json');
216            echo json_encode($result);
217        } catch (Exception $e) {
218            error_log("KPIApiController: Error in updateConfig - " . $e->getMessage());
219            $this->app->halt(500, json_encode([
220                'error' => 'Failed to update KPI configuration',
221                'message' => $e->getMessage()
222            ]));
223        }
224    }
225
226    /**
227     * Reset KPI configuration to defaults
228     * POST /api/:typeNum/workbook/kpi/config/reset/
229     *
230     * @param string $typeNum Store type number
231     */
232    public function resetConfig($typeNum)
233    {
234        try {
235            // Check access - require store settings permission
236            if (!$this->app->user->checkStoreGroup($typeNum)) {
237                $this->app->halt(403, json_encode(['error' => 'Access denied']));
238            }
239
240            // Check for admin permission
241            $hasPermission = $this->app->user->checkAccess('workbook_manage_kpi')
242                          || $this->app->user->checkAccess('store_settings');
243
244            if (!$hasPermission) {
245                $this->app->halt(403, json_encode(['error' => 'Permission denied']));
246            }
247
248            $kpiConfig = new KPIConfig($this->store);
249            $success = $kpiConfig->resetDefaults();
250
251            if ($success) {
252                // Clear cache after reset
253                $this->clearKPICache();
254
255                // Publish to Ably for real-time updates
256                try {
257                    $ably = new WorkbookAbly($typeNum);
258                    $ably->kpiRefresh();
259                } catch (Exception $e) {
260                    error_log("KPIApiController::resetConfig Ably error: " . $e->getMessage());
261                }
262
263                $result = [
264                    'success' => true,
265                    'message' => 'KPI configuration reset to defaults'
266                ];
267            } else {
268                $result = [
269                    'success' => false,
270                    'error' => 'Failed to reset KPI configuration'
271                ];
272            }
273
274            $this->app->response->headers->set('Content-Type', 'application/json');
275            echo json_encode($result);
276        } catch (Exception $e) {
277            error_log("KPIApiController: Error in resetConfig - " . $e->getMessage());
278            $this->app->halt(500, json_encode([
279                'error' => 'Failed to reset KPI configuration',
280                'message' => $e->getMessage()
281            ]));
282        }
283    }
284
285    /**
286     * Clear KPI cache for current store
287     */
288    protected function clearKPICache()
289    {
290        try {
291            if (class_exists('Redis')) {
292                $redis = new \Redis();
293                $redisHost = getenv('REDIS_HOST') ?: 'localhost';
294                $redisPort = getenv('REDIS_PORT') ?: 6379;
295
296                if ($redis->connect($redisHost, $redisPort)) {
297                    $date = new \DateTime('now', new \DateTimeZone($this->store->getTimezone()));
298                    $cacheKey = $this->store->getTypeNum() . '_workbook_kpis_' . $date->format('Y-m-d');
299                    $redis->del($cacheKey);
300                }
301            }
302        } catch (Exception $e) {
303            error_log("KPIApiController: Error clearing cache - " . $e->getMessage());
304        }
305    }
306}