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