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