Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
11.93% covered (danger)
11.93%
47 / 394
7.14% covered (danger)
7.14%
1 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
GestionaController
11.93% covered (danger)
11.93%
47 / 394
7.14% covered (danger)
7.14%
1 / 14
3514.63
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
1
 updateApiCredentials
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getApiDetails
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 getLastUpdate
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 getAcceptanceWarnings
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
42
 getSyncStatus
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getG3wActive
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 updateG3wActive
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 getMappings
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 setMappings
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
156
 deleteMappings
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getAllBudgetMonitor
0.00% covered (danger)
0.00%
0 / 116
0.00% covered (danger)
0.00%
0 / 1
182
 syncAllBudgetMonitor
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
42
 getDuplicatedValues
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\TblCompanies;
6use App\Models\TblG3WOrdersUpdateLogs;
7use App\Models\TblQuotations;
8use App\Models\TblSegmentG3wMapping;
9use App\Models\TblSourceG3wMapping;
10use App\Models\TblSources;
11use App\Models\TblStatusG3wMapping;
12use App\Models\TblTypeG3wMapping;
13use App\Models\TblUserG3wMapping;
14use App\Models\TblUsers;
15use App\Services\GestionaService;
16use App\Services\PresupuestosService;
17use Illuminate\Http\JsonResponse;
18use Illuminate\Http\Request;
19use Illuminate\Support\Carbon;
20use Illuminate\Support\Facades\DB;
21use Illuminate\Support\Facades\Log;
22use Illuminate\Support\Facades\Schema;
23use App\Exceptions\AppException;
24use function Illuminate\Events\queueable;
25
26class GestionaController extends Controller
27{
28    protected $tables;
29
30    public function __construct(protected GestionaService $gestionaService, protected PresupuestosService $presupuestoService)
31    {
32        $this->tables = [
33            'status' => [
34                'model' => TblStatusG3wMapping::class,
35                'index' => 'budget_status_id',
36                'relation' => 'budgetStatus',
37                'tblQuotationsG3wNameColumn' => 'status_by_g3w',
38                'tblMappingNameColumn' => 'name_g3w',
39                'tblQuotationsNameColumn' => 'budget_status_id',
40                'requestNameKey' => 'budget_status_id',
41            ],
42            'segment' => [
43                'model' => TblSegmentG3wMapping::class,
44                'index' => 'segment_id',
45                'relation' => 'segment',
46                'tblQuotationsG3wNameColumn' => 'segment_by_g3w',
47                'tblMappingNameColumn' => 'name_g3w',
48                'tblQuotationsNameColumn' => 'segment_id',
49                'requestNameKey' => 'segment_id',
50            ],
51            'type' => [
52                'model' => TblTypeG3wMapping::class,
53                'index' => 'budget_type_name',
54                'relation' => 'budgetType',
55                'tblQuotationsG3wNameColumn' => 'type_by_g3w',
56                'tblMappingNameColumn' => 'id_g3w',
57                'tblQuotationsNameColumn' => 'budget_type_id',
58                'requestNameKey' => 'id_g3w',
59            ],
60            'source' => [
61                'model' => TblSourceG3wMapping::class,
62                'index' => 'source_name',
63                'relation' => 'source',
64                'tblQuotationsG3wNameColumn' => 'source_by_g3w',
65                'tblMappingNameColumn' => 'id_g3w',
66                'tblQuotationsNameColumn' => 'source_id',
67                'requestNameKey' => 'id_g3w',
68            ],
69            'user' => [
70                'model' => TblUserG3wMapping::class,
71                'index' => 'id_fst',
72                'relation' => 'user',
73                'tblQuotationsG3wNameColumn' => 'G3W_code',
74                'tblMappingNameColumn' => 'id_fst',
75                'tblQuotationsNameColumn' => 'G3W_code',
76                'requestNameKey' => 'id_fst',
77            ],
78        ];
79    }
80
81    /**
82     * Sincroniza un presupuesto por su ID.
83     *
84     * @param $id
85     * @return JsonResponse
86     */
87    public function updateApiCredentials(Request $request)
88    {
89        try {
90            $region = request()->header('Region');
91
92            if ($region == 'Catalunya') {
93                $region = 'Cataluña';
94            }
95
96            $params = $request->only(['url', 'user', 'password', 'user_id']);
97            $this->gestionaService->updateApiCredentials($params, $region);
98
99            return response()->json(['message' => 'Credentials updated successfully'], 200);
100        } catch (\Exception $e) {
101            report(AppException::fromException($e, 'UPDATE_API_CREDENTIALS_EXCEPTION'));
102            return response()->json(['error' => $e->getMessage()], 400);
103        }
104    }
105
106    /**
107     * @return JsonResponse
108     * @throws \Exception
109     */
110    public function getApiDetails()
111    {
112        try {
113            $region = request()->header('Region');
114
115            if ($region === 'Catalunya') {
116                $region = 'Cataluña';
117            }
118
119            $apiDetailsResponse = $this->gestionaService->getApiDetails($region);
120
121            if (! $apiDetailsResponse) {
122                throw new \Exception('Api details not found');
123            }
124
125            $apiDetails = json_decode((string) $apiDetailsResponse->getContent(), true);
126
127            return response()->json([
128                'url' => $apiDetails['url'] ?? null,
129                'user' => $apiDetails['user'] ?? null,
130            ], 200);
131
132        } catch (\Illuminate\Database\QueryException $e) {
133            report(AppException::fromException($e, 'GET_API_DETAILS_EXCEPTION'));
134            return response()->json(['error' => $e->getMessage()], 400);
135        }
136    }
137
138    public function getLastUpdate()
139    {
140        try {
141            $region = request()->header('Region');
142
143            if ($region === 'Catalunya') {
144                $region = 'Cataluña';
145            }
146
147            $apiLastUpdateResponse = $this->gestionaService->getLastUpdate($region);
148
149            if (! $apiLastUpdateResponse) {
150                throw new \Exception('Last update not found');
151            }
152
153            $apiLastUpdate = json_decode((string) $apiLastUpdateResponse->getContent(), true);
154
155            return response()->json([
156                'lastUpdate' => $apiLastUpdate['lastUpdate'] ?? null,
157                'updatingNow' => $apiLastUpdate['updatingNow'] ?? null,
158            ], 200);
159
160        } catch (\Illuminate\Database\QueryException $e) {
161            report(AppException::fromException($e, 'GET_LAST_UPDATE_EXCEPTION'));
162            Log::error('Error en getLastUpdate: ' . $e->getMessage());
163            return response()->json(['error' => $e->getMessage()], 400);
164        }
165    }
166
167    public function getAcceptanceWarnings(Request $request)
168    {
169        try {
170            $region = request()->header('Region') ?? null;
171
172            if (! $region) {
173                return response()->json(['error' => 'Region header is missing'], 400);
174            }
175
176            if ($region == 'Catalunya') {
177                $region = 'Cataluña';
178            }
179
180            $commercial = $request->input('commercial');
181
182            $company = TblCompanies::where('region', $region)->first();
183            if (! $company) {
184                return response()->json([
185                    'countWarnings' => 0,
186                ], 200);
187            }
188
189            $companyId = $company->company_id;
190
191            /*$countWarnings = TblQuotations::where('sync_import', 1)
192                ->where('company_id', $companyId)
193                ->when(!empty($commercial) && $commercial !== "All", function ($query) use ($commercial) {
194                    return $query->where('commercial', $commercial);
195                })
196                ->where(function ($query) {
197                    $query->WhereIn('budget_status_id', [13, 14])
198                        ->orWhere(function ($subQuery) {
199                            $subQuery->whereNull('commercial')
200                                ->orWhereNotExists(function ($subQuery) {
201                                    $subQuery->select(DB::raw(1))
202                                        ->from('tbl_users')
203                                        ->whereColumn('tbl_users.name', 'tbl_quotations.commercial');
204                                });
205                        })
206                        ->orWhereNull('phone_number')
207                        ->orWhereNull('source_id')
208                        ->orWhereNull('budget_type_id')
209                        ->orWhere(function ($subQuery) {
210                            $subQuery->whereNull('client')
211                                ->orWhere(DB::raw('TRIM(client)'), '');
212                        })
213                        ->orWhere(function ($subQuery) {
214                            $subQuery->whereNull('email')
215                                ->orWhere(DB::raw('TRIM(email)'), '');
216                        });
217                })
218                ->count();*/
219
220            $countWarnings = TblQuotations::where('sync_import', 1)
221                ->where('company_id', $companyId)
222                ->where('g3w_warning', 1)
223                ->when(!empty($commercial) && $commercial !== "All", fn($query) => $query->where('commercial', $commercial))
224                ->count();
225
226            return response()->json([
227                'countWarnings' => $countWarnings,
228            ], 200);
229
230        } catch (\Illuminate\Database\QueryException $e) {
231            report(AppException::fromException($e, 'ADD_WORK_STATUS_EXCEPTION'));
232            Log::error('Error en getAcceptanceWarnings: ' . $e->getMessage());
233            return response()->json(['error' => $e->getMessage()], 400);
234        }
235    }
236
237    public function getSyncStatus()
238    {
239        try {
240            $region = request()->header('Region');
241
242            if ($region === 'Catalunya') {
243                $region = 'Cataluña';
244            }
245
246            return response()->json([
247                'status' => $this->gestionaService->getSyncStatus($region),
248            ], 200);
249
250        } catch (\Illuminate\Database\QueryException $e) {
251            report(AppException::fromException($e, 'GET_SYNC_STATUS_EXCEPTION'));
252            return response()->json(['error' => $e->getMessage()], 400);
253        }
254    }
255
256    public function getG3wActive()
257    {
258        try {
259            $region = request()->header('Region');
260
261            $region = urldecode((string) $region);
262
263            if ($region === 'Catalunya') {
264                $region = 'Cataluña';
265            }
266
267            return response()->json([
268                'active' => $this->gestionaService->getG3wActive($region),
269            ], 200);
270
271        } catch (\Illuminate\Database\QueryException $e) {
272            report(AppException::fromException($e, 'GET_G3W_ACTIVE_EXCEPTION'));
273            Log::error('Error en getG3wActive: ' . $e->getMessage());
274            return response()->json(['error' => $e->getMessage()], 400);
275        }
276    }
277
278    public function updateG3wActive(Request $request)
279    {
280        try {
281            $region = urldecode($request->header('Region'));
282
283            if ($region === 'Catalunya') {
284                $region = 'Cataluña';
285            }
286
287            $active = $request->input('active');
288
289            $this->gestionaService->updateG3wActive($active, $region);
290
291            return response()->json([
292                'success' => 1,
293            ], 200);
294
295        } catch (\Illuminate\Database\QueryException $e) {
296            report(AppException::fromException($e, 'UPDATE_G3W_ACTIVE_EXCEPTION'));
297            return response()->json(['error' => $e->getMessage()], 400);
298        }
299    }
300
301    public function getMappings(Request $request)
302    {
303        try {
304
305            if (! $request->has('type') || ! array_key_exists($request->type, $this->tables)) {
306                throw new \Exception('Type parameter is missing or invalid');
307            }
308
309            $tableConfig = $this->tables[$request->type];
310
311            if (isset($tableConfig['model'])) {
312                $mappings = $tableConfig['model']::with([$tableConfig['relation'] => function ($query) use ($tableConfig): void {
313                    $relatedModel = (new $tableConfig['model'])->{$tableConfig['relation']}()->getRelated();
314                    $relatedTable = $relatedModel->getTable();
315
316                    if (Schema::hasColumn($relatedTable, 'priority')) {
317                        $query->orderBy('priority', 'desc');
318                    }
319                }])->get();
320            } else {
321                $mappings = DB::table($tableConfig['table'])->get();
322            }
323
324            return response()->json($mappings);
325
326        } catch (\Exception $e) {
327            report(AppException::fromException($e, 'GET_MAPPINGS_EXCEPTION'));
328            return response()->json(['error' => $e->getMessage()], 400);
329        }
330    }
331
332    public function setMappings(Request $request)
333    {
334        try {
335            if (! $request->has('type')) {
336                return response()->json(['message' => 'Not type especified']);
337            }
338
339            $tableConfig = $this->tables[$request->type];
340
341            if (! isset($tableConfig['tblQuotationsG3wNameColumn'], $tableConfig['tblMappingNameColumn'], $tableConfig['tblQuotationsNameColumn'], $tableConfig['requestNameKey'])) {
342                return response()->json(['message' => 'Missing configuration keys'], 500);
343            }
344
345            $data = $request->except('type');
346
347            if (! $request->has('id')) {
348                $tableConfig['model']::create($data);
349
350                return response()->json(['message' => $request->type.' created successfully']);
351            }
352
353            if ($tableConfig['relation'] === 'user') {
354                if (is_numeric($request['id'])) {
355                    $rowMapping = $tableConfig['model']::where('id_fst', $request['id'])->first();
356                    $data = $request->except('id');
357
358                    if (! $rowMapping) {
359                        $tableConfig['model']::create($data);
360
361                        return response()->json(['error' => 'User mapping not found'], 404);
362                    }
363
364                    $userName = TblUsers::where('id', $rowMapping->id_fst)->first()->name;
365                    $nameG3w = $rowMapping->name_g3w;
366
367                    TblQuotations::where('user_create_by_g3w', $nameG3w)
368                        ->update(['created_by' => $userName]);
369
370                    TblQuotations::where('user_commercial_by_g3w', $nameG3w)
371                        ->update(['commercial' => $userName]);
372
373                    TblQuotations::where('user_create_by_g3w', $nameG3w)
374                        ->whereNull('commercial')
375                        ->whereNull('user_commercial_by_g3w')
376                        ->update(['commercial' => $nameG3w]);
377
378                    $rowMapping->update($data);
379
380                } else {
381                    $rowMapping = $tableConfig['model']::where('name_g3w', $request['id'])->first();
382
383                    if (! $rowMapping) {
384                        return response()->json(['error' => 'User mapping not found'], 404);
385                    }
386
387                    $rowMapping->update(['id_fst' => $request["id_fst"]]);
388
389                    $userName = TblUsers::where('id', $request['id_fst'])->first();
390
391                    if (! $userName) {
392                        return response()->json(['error' => 'User mapping not found'], 404);
393                    }
394
395                    TblQuotations::where('user_create_by_g3w', $request['id'])
396                        ->update(['created_by' => $userName->name]);
397
398                    TblQuotations::where('user_commercial_by_g3w', $request['id'])
399                        ->update(['commercial' => $userName->name]);
400
401                    TblQuotations::where('user_create_by_g3w', $request['id'])
402                        ->whereNull('commercial')
403                        ->whereNull('user_commercial_by_g3w')
404                        ->update(['commercial' => $userName->name]);
405
406                }
407
408                return response()->json(['message' => $request->type.' edited successfully']);
409            }
410
411            $rowMapping = $tableConfig['model']::where('id', $request['id'])->first();
412
413            if ($tableConfig['relation'] === 'source') {
414                $data = [];
415                $sourceRow = TblSources::where('source_id', $request['id_fst'])->first();
416                if (! $sourceRow) {
417                    return response()->json(['error' => 'Source mapping not found'], 404);
418                }
419
420                $data['source_name'] = $sourceRow->name;
421            }
422
423            $rowMapping->update($data);
424
425            TblQuotations::where($tableConfig['tblQuotationsG3wNameColumn'], $rowMapping[$tableConfig['tblMappingNameColumn']])
426                ->update([$tableConfig['tblQuotationsNameColumn'] => $rowMapping[$tableConfig['requestNameKey']]]);
427
428            return response()->json(['message' => $request->type.' edited successfully']);
429
430        }catch (\Illuminate\Database\QueryException $e) {
431            report(AppException::fromException($e, 'SET_MAPPING_EXCEPTION'));
432            return response()->json(['error' => $e->getMessage()], 400);
433        }
434    }
435
436    public function deleteMappings(Request $request)
437    {
438        try {
439            if (! $request->has('id')) {
440                return response()->json(['message' => 'Can`t be deleted without ID']);
441            }
442
443            if (! $request->has('type')) {
444                return response()->json(['message' => 'Not type especified']);
445            }
446
447            $tableConfig = $this->tables[$request->type];
448
449            if ($tableConfig['relation'] === 'user') {
450                $tableConfig['model']::where('id_fst', $request['id'])->delete();
451
452                return response()->json(['message' => $request->type.' deleted successfully']);
453            }
454
455            $tableConfig['model']::where('id', $request['id'])->delete();
456
457            return response()->json(['message' => $request->type.' deleted successfully']);
458
459        }catch (\Illuminate\Database\QueryException $e) {
460            report(AppException::fromException($e, 'DELETE_MAPPING_EXCEPTION'));
461            return response()->json(['error' => $e->getMessage()], 400);
462        }
463    }
464
465    public function getAllBudgetMonitor(Request $request)
466    {
467        try {
468            $data = $request->all();
469            $region = urldecode((string) request()->header('Region'));
470            $page = $data["page"];
471
472            $daysOffset = $page * 10;
473            $endDate = Carbon::today()->subDays($daysOffset);
474            $startDate = $endDate->copy()->addDays(10);
475
476            $dataResponse = [];
477            $currentDate = $startDate->copy();
478            $companyId = TblCompanies::where('region', $region)->first()->company_id;
479            $alreadyG3wBudgets = [];
480
481            while ($currentDate > $endDate) {
482                $dateStr = $currentDate->format('Y-m-d');
483
484                $g3wBudgets = $this->gestionaService->getBudgetsByDay($dateStr, $region);
485                if (is_string($g3wBudgets)) {
486                    $g3wBudgets = json_decode($g3wBudgets, true);
487                }
488
489                $g3wBudgetIds = array_map(fn(array $item) => $item["ID"], $g3wBudgets);
490
491                $newG3wBudgetIds = array_diff($g3wBudgetIds, $alreadyG3wBudgets);
492                $alreadyG3wBudgets = array_merge($alreadyG3wBudgets, $newG3wBudgetIds);
493                $countG3wBudgets = count($newG3wBudgetIds);
494
495                $fstBudgets = TblQuotations::where("company_id", $companyId)
496                    ->where(function($query): void {
497                        $query->where("sync_import", 1)
498                            ->orWhere("sync_import_edited", 1);
499                    })
500                    ->whereDate('created_at', $dateStr)
501                    ->pluck('internal_quote_id')
502                    ->toArray();
503
504                if ($companyId == 18 || $companyId == 22){
505                    $fstBudgets = TblQuotations::whereIn("company_id", [18, 22])
506                        ->where(function($query): void {
507                            $query->where("sync_import", 1)
508                                ->orWhere("sync_import_edited", 1);
509                        })
510                        ->whereDate('created_at', $dateStr)
511                        ->pluck('internal_quote_id')
512                        ->toArray();
513                }
514
515                $countFstBudgets = count($fstBudgets);
516
517                $missingIds = array_diff($newG3wBudgetIds, $fstBudgets);
518
519                $existingIds = TblQuotations::where("company_id", $companyId)
520                    ->where(function($query): void {
521                        $query->where("sync_import", 1)
522                            ->orWhere("sync_import_edited", 1);
523                    })
524                    ->whereIn('internal_quote_id', $missingIds)
525                    ->pluck('internal_quote_id')
526                    ->toArray();
527
528                if($companyId == 18 || $companyId == 22){
529                    $existingIds = TblQuotations::where("company_id", $companyId)
530                        ->where(function($query): void {
531                            $query->where("sync_import", 1)
532                                ->orWhere("sync_import_edited", 1);
533                        })
534                        ->whereIn('internal_quote_id', $missingIds)
535                        ->pluck('internal_quote_id')
536                        ->toArray();
537                }
538
539                $finalMissingIds = array_diff($missingIds, $existingIds);
540
541                $duplicatedFst = $this->getDuplicatedValues($fstBudgets);
542
543                $deletedIds = array_diff($fstBudgets, $newG3wBudgetIds);
544                foreach ($deletedIds as $key => $id) {
545                    $deleted = $this->gestionaService->checkDeleted($id, $region);
546                    if (! $deleted) {
547                        unset($deletedIds[$key]);
548                    }
549                }
550
551                $g3wOrdersUpdateLogs = TblG3WOrdersUpdateLogs::where('company_id', $companyId)
552                    ->whereDate('ended_at', $dateStr)
553                    ->where('processed_by', 'System')
554                    ->get();
555
556                $syncs = $g3wOrdersUpdateLogs->count();
557
558                $allUniqueErrorIds = [];
559
560                $syncErrorIds = $g3wOrdersUpdateLogs->flatMap(function ($item): array {
561                        $ids = $item->sync_error_ids;
562                        if (is_string($ids)) {
563                            $decoded = json_decode($ids, true);
564                            $ids = is_array($decoded) ? $decoded : explode(',', $ids);
565                        }
566                        return (array) $ids;
567                    })
568                    ->filter()
569                    ->unique()
570                    ->toArray();
571
572                $allUniqueErrorIds = array_merge($allUniqueErrorIds, $syncErrorIds);
573
574                $finalUniqueIds = array_values(array_unique($allUniqueErrorIds));
575                $countSyncErrorIds = count($finalUniqueIds);
576
577                $dataResponse[] = [
578                    'date' => $dateStr,
579                    'g3wBudgets' => array_values($newG3wBudgetIds),
580                    'countG3wBudgets' => $countG3wBudgets,
581                    'fstBudgets' => $fstBudgets,
582                    'countfstBudgets' => $countFstBudgets,
583                    'missingIds' => array_values($finalMissingIds),
584                    'duplicatedFst' => $duplicatedFst,
585                    'deletedIds' => array_values($deletedIds),
586                    'syncs' => $syncs,
587                    'syncErrorIds' => $syncErrorIds,
588                    'countSyncErrorIds' => $countSyncErrorIds,
589                ];
590
591                $currentDate->subDay();
592
593            }
594
595            usort($dataResponse, fn(array $a, array $b) => strtotime($b['date']) - strtotime($a['date']));
596
597            return response()->json([
598                'data' => $dataResponse,
599                'meta' => [
600                    'current_page' => (int) $page,
601                    'date_range' => [
602                        'start' => $startDate->format('Y-m-d'),
603                        'end' => $endDate->format('Y-m-d'),
604                    ],
605                ]]);
606        } catch (\Exception $e){
607            report(AppException::fromException($e, 'GET_ALL_BUDGET_MONITOR_EXCEPTION'));
608            Log::error('Error en getAllBudgetMonitor: ' . $e->getMessage());
609
610            if ($e->getMessage() == 'API URL is not defined.') {
611                return response()->json([
612                    'error' => 'KO',
613                    'message' => 'La conexión con la API de G3W no está configurada. Por favor, configúrala en los ajustes de la empresa.',
614                ], 200);
615            }
616
617            return response()->json([
618                'message' => 'Error interno del servidor',
619            ], 500);
620        }
621    }
622
623    public function syncAllBudgetMonitor(Request $request)
624    {
625        try {
626            $data = $request->all();
627            if (app()->runningInConsole()) {
628                $region = $request->header('Region');
629            } else {
630                $region = urldecode(request()->header('Region') ?? '');
631            }
632            $day = $data['day'];
633            $dataResponse = [];
634            $companyId = TblCompanies::where('region', $region)->first()->company_id;
635            $alreadyG3wBudgets = [];
636
637            $g3wBudgets = $this->gestionaService->getBudgetsByDay($day, $region);
638
639            if (is_string($g3wBudgets)) {
640                $g3wBudgets = json_decode($g3wBudgets, true);
641            }
642
643            $g3wBudgetIds = array_map(fn(array $item) => $item["ID"], $g3wBudgets);
644
645            $newG3wBudgetIds = array_diff($g3wBudgetIds, $alreadyG3wBudgets);
646            $alreadyG3wBudgets = array_merge($alreadyG3wBudgets, $newG3wBudgetIds);
647
648            $fstBudgets = TblQuotations::where('company_id', $companyId)
649                ->where('sync_import', 1)
650                ->whereDate('created_at', $day)
651                ->pluck('internal_quote_id')
652                ->toArray();
653
654            $missingIds = array_diff($newG3wBudgetIds, $fstBudgets);
655
656            $existingIds = TblQuotations::where('company_id', $companyId)
657                ->where('sync_import', 1)
658                ->whereIn('internal_quote_id', $missingIds)
659                ->pluck('internal_quote_id')
660                ->toArray();
661
662            $finalMissingIds = array_diff($missingIds, $existingIds);
663
664            $duplicatedFst = $this->getDuplicatedValues($fstBudgets);
665
666            $deletedIds = array_diff($fstBudgets, $newG3wBudgetIds);
667
668            foreach ($deletedIds as $key => $id) {
669                $deleted = $this->gestionaService->checkDeleted($id, $region);
670                if (! $deleted) {
671                    unset($deletedIds[$key]);
672                }
673            }
674
675            $this->presupuestoService->syncByIds(implode(',', $finalMissingIds), $region, $day);
676
677            $dataResponse[] = [
678                'missingIds' => array_values($finalMissingIds),
679                'deletedIds' => array_values($deletedIds),
680            ];
681
682            return response()->json([
683                'success' => true,
684                'message' => 'Presupuestos que faltan sincronizados correctamente',
685            ]);
686        } catch (\Exception $e){
687            report(AppException::fromException($e, 'SYNC_ALL_BUDGET_MONITOR_EXCEPTION'));
688            Log::error('Error en getAllBudgetMonitor: ' . $e->getMessage());
689
690            return response()->json([
691                'message' => 'Error interno del servidor',
692            ], 500);
693        }
694    }
695
696    /**
697     * @return int[]|string[]
698     */
699    private function getDuplicatedValues($fstBudgets): array {
700        $filteredValues = array_filter($fstBudgets, fn($value) => $value !== null);
701
702        if (empty($filteredValues)) {
703            return [];
704        }
705
706        $counts = array_count_values($filteredValues);
707        $duplicates = array_filter($counts, fn($count) => $count > 1);
708
709        return array_keys($duplicates);
710    }
711}