Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
104 / 104
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
GestionaClientSyncService
100.00% covered (success)
100.00%
104 / 104
100.00% covered (success)
100.00%
9 / 9
30
100.00% covered (success)
100.00%
1 / 1
 runSync
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 syncRegion
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 syncClientIds
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
 fetchClientIds
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 fetchClient
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 upsertClient
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 resolveClientTypeId
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 createNewClient
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
1
 updateExistingClient
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Services;
4
5use App\Jobs\SyncG3WClientsForRegion;
6use App\Models\Client;
7use App\Models\ClientGestionaResponse;
8use Illuminate\Console\Command;
9use Illuminate\Support\Facades\Log;
10
11class GestionaClientSyncService extends GestionaService
12{
13    public function runSync(string $date, array $regions, Command $command): int
14    {
15        Log::channel('g3w')->info("clients:sync — Dispatching for date {$date}, regions: ".implode(', ', $regions));
16
17        // FIRE-1151: dispatch one job per region. Per-region totals + wall-time
18        // are logged from inside SyncG3WClientsForRegion::handle().
19        $cronStart = microtime(true);
20        foreach ($regions as $region) {
21            SyncG3WClientsForRegion::dispatch($date, $region)->onQueue('g3w');
22            $command->info("Dispatched: {$region}");
23        }
24
25        Log::channel('g3w')->info('clients:sync dispatched', [
26            'regions' => count($regions),
27            'date' => $date,
28            'dispatch_ms' => (int) round((microtime(true) - $cronStart) * 1000),
29        ]);
30
31        return Command::SUCCESS;
32    }
33
34    public function syncRegion(string $date, string $region): array
35    {
36        $clientIds = $this->fetchClientIds($date, $region);
37
38        if (empty($clientIds)) {
39            Log::channel('g3w')->info("clients:sync — No client IDs returned for region {$region}, date {$date}.");
40
41            return ['created' => 0, 'updated' => 0, 'failed' => 0, 'skipped' => 0];
42        }
43
44        $originalCount = count($clientIds);
45        $clientIds = array_unique(array_filter($clientIds));
46
47        Log::channel('g3w')->info("clients:sync — Found {$originalCount} client ID(s) for region {$region}, date {$date} (".count($clientIds).' unique).');
48
49        return $this->syncClientIds($clientIds, $region);
50    }
51
52    public function syncClientIds(array $clientIds, string $region): array
53    {
54        $stats = ['created' => 0, 'updated' => 0, 'failed' => 0, 'skipped' => 0];
55
56        foreach ($clientIds as $clientId) {
57            try {
58                $gestionaData = $this->fetchClient($clientId, $region);
59
60                if (! $gestionaData) {
61                    $stats['skipped']++;
62
63                    continue;
64                }
65
66                $result = $this->upsertClient($gestionaData, $region);
67
68                $result === 'created' ? $stats['created']++ : $stats['updated']++;
69            } catch (\Exception $e) {
70                Log::channel('g3w')->error("clients:sync — Error syncing client ID {$clientId} in region {$region}".$e->getMessage());
71                $stats['failed']++;
72            }
73        }
74
75        return $stats;
76    }
77
78    public function fetchClientIds(string $date, string $region): array
79    {
80        $response = $this->request('get', "cliente/fecha/{$date}", $region);
81
82        if (! is_array($response)) {
83            return [];
84        }
85
86        return array_map(fn ($item) => is_array($item) ? ($item['ID'] ?? null) : $item, $response);
87    }
88
89    public function fetchClient(int $id, string $region): ?array
90    {
91        try {
92            $response = $this->request('get', "cliente/{$id}", $region);
93
94            if (is_array($response) && isset($response['cliente'])) {
95                return $response['cliente'];
96            }
97
98            return is_array($response) ? $response : null;
99        } catch (\Exception $e) {
100            Log::channel('g3w')->warning("clients:sync — Could not fetch client {$id} in region {$region}".$e->getMessage());
101
102            return null;
103        }
104    }
105
106    public function upsertClient(array $data, string $region): string
107    {
108        $gestionaId = $data['cod_cliente'] ?? $data['ID'] ?? null;
109
110        if (! $gestionaId) {
111            throw new \Exception('Gestiona client data missing cod_cliente/ID field.');
112        }
113
114        if (isset($data['tipo_cliente'])) {
115            Log::channel('g3w')->info("clients:sync — Client {$gestionaId} tipo_cliente: {$data['tipo_cliente']}");
116        }
117
118        $existing = Client::where('gestiona_client_code', $gestionaId)->first();
119        $clientTypeId = $this->resolveClientTypeId($data);
120
121        if ($existing) {
122            $this->updateExistingClient($existing, $data, $clientTypeId);
123
124            return 'updated';
125        }
126
127        $this->createNewClient($data, $region, $clientTypeId, $gestionaId);
128
129        return 'created';
130    }
131
132    private function resolveClientTypeId(array $data): int
133    {
134        $tipoCliente = $data['tipo_cliente'] ?? null;
135
136        return match ($tipoCliente) {
137            'Administrador' => Client::TYPE_ADMINISTRADOR,
138            'Grandes Cuentas', 'Grandes Clientes' => Client::TYPE_GRAN_CLIENTE,
139            'Facilities' => Client::TYPE_FACILITIES,
140            default => Client::TYPE_GENERAL,
141        };
142    }
143
144    private function createNewClient(array $data, string $region, int $clientTypeId, int $gestionaId): Client
145    {
146        $client = Client::create([
147            'gestiona_client_code' => $gestionaId,
148            'client_type_id' => $clientTypeId,
149            'company_name' => $data['empresa'] ?? null,
150            'associated_billing_client' => $data['empresa'] ?? null,
151            'fiscal_id' => $data['cliente_cif'] ?? null,
152            'address' => $data['direccion'] ?? null,
153            'postal_code' => $data['codpostal'] ?? null,
154            'city' => $data['poblacion'] ?? null,
155            'province' => $data['provincia'] ?? null,
156            'contact_phone' => $data['telefono'] ?? null,
157            'contact_email' => $data['email_facturacion'] ?? null,
158            'contact_name' => $data['contacto'] ?? null,
159            'payment_method' => $data['forma_pago'] ?? null,
160            'region' => $region,
161        ]);
162
163        ClientGestionaResponse::create([
164            'client_id' => $client->id,
165            'gestiona_client_code' => $gestionaId,
166            'raw_response' => $data,
167        ]);
168
169        return $client;
170    }
171
172    private function updateExistingClient(Client $client, array $data, int $clientTypeId): void
173    {
174        $updateData = [
175            'client_type_id' => $clientTypeId,
176            'associated_billing_client' => $data['empresa'] ?? $client->associated_billing_client,
177            'fiscal_id' => $data['cliente_cif'] ?? $client->fiscal_id,
178            'address' => $data['direccion'] ?? $client->address,
179            'postal_code' => $data['codpostal'] ?? $client->postal_code,
180            'city' => $data['poblacion'] ?? $client->city,
181            'province' => $data['provincia'] ?? $client->province,
182            'contact_phone' => $data['telefono'] ?? $client->contact_phone,
183            'contact_email' => $data['email_facturacion'] ?? $client->contact_email,
184            'contact_name' => $data['contacto'] ?? $client->contact_name,
185            'payment_method' => $data['forma_pago'] ?? $client->payment_method,
186        ];
187
188        // Only update company_name if it's currently empty
189        if (empty($client->company_name) && ! empty($data['empresa'])) {
190            $updateData['company_name'] = $data['empresa'];
191        }
192
193        $client->update($updateData);
194
195        ClientGestionaResponse::create([
196            'client_id' => $client->id,
197            'gestiona_client_code' => $client->gestiona_client_code,
198            'raw_response' => $data,
199        ]);
200    }
201}