Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
2.78% covered (danger)
2.78%
4 / 144
0.00% covered (danger)
0.00%
0 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
GestionaService
2.78% covered (danger)
2.78%
4 / 144
0.00% covered (danger)
0.00%
0 / 15
2634.36
0.00% covered (danger)
0.00%
0 / 1
 __construct
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 getCredentials
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 encrypt
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 decrypt
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 auth
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
72
 request
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
210
 updateApiCredentials
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 getApiDetails
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getLastUpdate
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getSyncStatus
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 setSyncStatus
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getG3wActive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateG3wActive
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getBudgetsByDay
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkDeleted
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Services;
4
5use App\Models\TblCompanies;
6use App\Models\TblG3WCredentials;
7use App\Models\TblG3wLastUpdate;
8use Illuminate\Http\JsonResponse;
9use Illuminate\Support\Facades\Log;
10
11class GestionaService
12{
13    protected $apiUrl;
14
15    protected $user;
16
17    protected $password;
18
19    protected $accessToken;
20
21    protected $region;
22
23    protected $credentials;
24
25    protected $appUser;
26
27    protected $encryptKey;
28
29    protected string $cipher;
30
31    public function __construct()
32    {
33        $this->encryptKey = config('app.encrypt_key');
34        if (! $this->encryptKey) {
35            throw new \Exception('Encryption key not set in environment variables');
36        }
37
38        $this->cipher = 'AES-256-CBC';
39
40        $this->appUser = request()->header('Name') ?? 'System';
41    }
42
43    public function getCredentials($region): void
44    {
45
46        if ($region === 'Catalunya') {
47            $region = 'Cataluña';
48        }
49
50        $this->credentials = TblG3WCredentials::where('region', $region)->first();
51
52        if (! $this->credentials) {
53            TblG3WCredentials::create([
54                'region' => $region,
55            ]);
56            $this->credentials = TblG3WCredentials::where('region', $region)->first();
57        }
58
59        $this->user = $this->credentials->user ? $this->decrypt($this->credentials->user) : null;
60        $this->password = $this->credentials->password ? $this->decrypt($this->credentials->password) : null;
61        $this->apiUrl = $this->credentials->url ? $this->decrypt($this->credentials->url) : null;
62    }
63
64    private function encrypt($data): string|false
65    {
66        $iv = substr(hash('sha256', (string) $this->encryptKey), 0, 16);
67
68        return openssl_encrypt($data, $this->cipher, $this->encryptKey, 0, $iv);
69    }
70
71    private function decrypt($encryptedData): string|false
72    {
73        $iv = substr(hash('sha256', (string) $this->encryptKey), 0, 16);
74
75        return openssl_decrypt($encryptedData, $this->cipher, $this->encryptKey, 0, $iv);
76    }
77
78    /**
79     * @return void
80     *
81     * @throws \Exception
82     */
83    protected function auth($region)
84    {
85        try {
86            $this->getCredentials($region);
87            if (! $this->apiUrl || ! $this->user || ! $this->password) {
88                throw new \Exception('Incomplete credentials: URL, user, or password missing.');
89            }
90
91            $url = "{$this->apiUrl}login";
92
93            $curl = curl_init();
94            curl_setopt_array($curl, [
95                CURLOPT_URL => $url,
96                CURLOPT_RETURNTRANSFER => true,
97                CURLOPT_POST => true,
98                CURLOPT_POSTFIELDS => json_encode([
99                    'email' => $this->user,
100                    'password' => $this->password,
101                ]),
102                CURLOPT_HTTPHEADER => [
103                    'Accept: application/json',
104                    'Content-Type: application/json',
105                ],
106            ]);
107
108            $response = curl_exec($curl);
109
110            if (curl_errno($curl)) {
111                throw new \Exception('cURL error: '.curl_error($curl));
112            }
113
114            $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
115            curl_close($curl);
116
117            if ($httpCode !== 200) {
118                throw new \Exception('Authentication error: '.$response);
119            }
120
121            $responseData = json_decode($response, true);
122            $this->accessToken = $responseData['accessToken'] ?? null;
123
124            if (! $this->accessToken) {
125                throw new \Exception('No access token returned.');
126            }
127        } catch (\Exception $e) {
128            Log::channel('g3w')->error('Error in auth: '.$e->getMessage());
129            throw new \Exception('Authentication failed: '.$e->getMessage());
130        }
131    }
132
133    /**
134     * @return mixed
135     *
136     * @throws \Exception
137     */
138    public function request($method, $endpoint, $region, array $data = [])
139    {
140        try {
141            $this->getCredentials($region);
142
143            if (! $this->apiUrl) {
144                throw new \Exception('API URL is not defined.');
145            }
146
147            if (! $this->accessToken) {
148                $this->auth($region);
149            }
150
151            $url = "{$this->apiUrl}{$endpoint}";
152            $curl = curl_init();
153
154            curl_setopt_array($curl, [
155                CURLOPT_URL => $url,
156                CURLOPT_RETURNTRANSFER => true,
157                CURLOPT_CUSTOMREQUEST => strtoupper((string) $method),
158                CURLOPT_HTTPHEADER => [
159                    'Authorization: Bearer '.$this->accessToken,
160                    'Accept: application/json',
161                    'Content-Type: application/json',
162                ],
163            ]);
164
165            if (in_array(strtoupper((string) $method), ['POST', 'PUT', 'PATCH']) && ! empty($data)) {
166                if (! is_array($data)) {
167                    throw new \Exception('The $data parameter must be an array.');
168                }
169                curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
170            }
171
172            $response = curl_exec($curl);
173
174            if (curl_errno($curl)) {
175                throw new \Exception('cURL error: '.curl_error($curl));
176            }
177
178            $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
179            curl_close($curl);
180
181            if ($httpCode === 401) {
182                $this->auth($region);
183
184                return $this->request($method, $endpoint, $region, $data);
185            }
186
187            if ($httpCode < 200 || $httpCode >= 300) {
188                Log::channel('g3w')->error('API Response Error: '.$response);
189
190                $errorResponse = json_decode($response, true);
191                $errorMessage = is_array($errorResponse) && isset($errorResponse['message'])
192                    ? $errorResponse['message']
193                    : 'Request error: '.$response;
194
195                throw new \Exception($errorMessage);
196            }
197
198            $responseData = json_decode($response, true);
199
200            if (json_last_error() !== JSON_ERROR_NONE) {
201                throw new \Exception('Invalid JSON response: '.$response);
202            }
203
204            return $responseData;
205        } catch (\Exception $e) {
206            Log::channel('g3w')->error('Error in request: '.$e->getMessage());
207            throw $e;
208        }
209    }
210
211    /**
212     * @return true
213     *
214     * @throws \Exception
215     */
216    public function updateApiCredentials(array $params, $region = null): bool
217    {
218        try {
219            $this->getCredentials($region);
220
221            $updateData = [];
222            if (isset($params['url'])) {
223                if (! str_ends_with($params['url'], '/')) {
224                    $params['url'] .= '/';
225                }
226                $updateData['url'] = $this->encrypt($params['url']);
227            }
228
229            if (isset($params['user'])) {
230                $updateData['user'] = $this->encrypt($params['user']);
231            }
232
233            if (isset($params['password'])) {
234                $updateData['password'] = $this->encrypt($params['password']);
235            }
236
237            $updateData['user_id'] = $params['user_id'] ?? null;
238
239            TblG3WCredentials::where('region', $region)->update($updateData);
240
241            return true;
242        } catch (\Exception $e) {
243            Log::channel('g3w')->error('Error updating credentials for region '.$this->region.': '.$e->getMessage());
244            throw $e;
245        }
246    }
247
248    /**
249     * @return JsonResponse
250     */
251    public function getApiDetails($region = null)
252    {
253        $this->getCredentials($region);
254        if (! $this->credentials) {
255            return response()->json(['message' => 'No credentials found'], 404);
256        }
257
258        return response()->json([
259            'url' => $this->decrypt($this->credentials->url),
260            'user' => $this->decrypt($this->credentials->user),
261        ]);
262    }
263
264    /**
265     * @return JsonResponse
266     */
267    public function getLastUpdate($region)
268    {
269        $lastUpdate = TblG3wLastUpdate::where('region', $region)->first();
270
271        if (! $lastUpdate) {
272            return response()->json(['message' => 'No last update found'], 404);
273        }
274
275        return response()->json([
276            'lastUpdate' => $lastUpdate->updated_at->format('Y-m-d H:i:s'),
277            'updatingNow' => $lastUpdate->updatingNow,
278        ]);
279    }
280
281    public function getSyncStatus($region)
282    {
283        if ($region === "'Catalunya'") {
284            $region = 'Cataluña';
285        }
286
287        return TblG3wLastUpdate::where('region', $region)->first()->updatingNow ?? 0;
288
289    }
290
291    public function setSyncStatus($status, $region): void
292    {
293        if ($region === 'Catalunya' || $region === "'Catalunya'") {
294            $region = 'Cataluña';
295        }
296
297        $record = TblG3wLastUpdate::where('region', $region)->first();
298
299        $record->updatingNow = $status;
300        $record->save();
301    }
302
303    public function getG3wActive($region)
304    {
305        return TblCompanies::where('region', $region)->first()->g3W_active ?? 0;
306    }
307
308    public function updateG3wActive($active, $region): void
309    {
310        $record = TblCompanies::where('region', $region)->first();
311
312        $record->g3W_active = $active;
313        $record->save();
314    }
315
316    public function getBudgetsByDay(string $date, $region)
317    {
318        return $this->request('get', 'presupuesto?fecha='.$date, $region, []);
319    }
320
321    public function checkDeleted($id, $region): bool
322    {
323        try {
324            $this->request('get', "presupuesto/{$id}", $region, []);
325
326            return false;
327        } catch (\Exception $e) {
328            if (str_contains($e->getMessage(), 'No se ha encontrado el presupuesto')) {
329                return true;
330            }
331
332            return false;
333        }
334
335    }
336}