Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 704
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
FacturasService
0.00% covered (danger)
0.00%
0 / 704
0.00% covered (danger)
0.00%
0 / 16
14042
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInvoices
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 loopInvoices
0.00% covered (danger)
0.00%
0 / 144
0.00% covered (danger)
0.00%
0 / 1
342
 loopNextRemindersInvoices
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
42
 loopNextRemindersClients
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 1
240
 sendInvoice
0.00% covered (danger)
0.00%
0 / 123
0.00% covered (danger)
0.00%
0 / 1
600
 getAllInvoices
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 getAllInvoicesExceptions
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 sendCyCInvoices
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 1
110
 setAllMonthAdministratorsInvoices
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
110
 sendAdministratorsInvoices
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 1
20
 addToSheets
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
56
 getGoogleSheetsService
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
56
 writeToGoogleSheet
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 handleGoogleAuthCallback
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
42
 getVencimientosFormateados
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Services;
4
5use App\Models\TblCompanies;
6use App\Models\TblInvoiceAdministrators;
7use App\Models\TblInvoiceReminders;
8use App\Models\TblInvoiceRemindersEmailTemplate;
9use App\Models\TblInvoicesExceptions;
10use App\Models\TblInvoicesNextReminders;
11use App\Services\GestionaService;
12use Carbon\Carbon;
13use Google\Service\Sheets;
14use Google\Service\Sheets\ValueRange;
15use Illuminate\Http\Request;
16use Illuminate\Support\Facades\Log;
17use Mockery\Exception;
18use PhpOffice\PhpSpreadsheet\Spreadsheet;
19use PhpOffice\PhpSpreadsheet\Style\Fill;
20use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
21
22class FacturasService extends GestionaService
23{
24    public function __construct()
25    {
26        parent::__construct();
27    }
28
29    public function getInvoices($region = "Cataluña"){
30        try{
31            if(!TblCompanies::where('region', $region)->where('invoice_reminder_active', 1)->exists()){
32                throw new Exception("Sincronizacion no activa para $region");
33            }
34
35            $today = date("Y-m-d");
36            $next10days = date("Y-m-d", strtotime("+10 days"));
37            $lastWeek = date("Y-m-d", strtotime("-1 week"));
38
39            $counter = 1;
40
41            $nextWeekInvoices = $this->request('get', 'factura/vence/' . $next10days, $region, []);
42            //$todayInvoices = $this->request('get', 'factura/vence/' . $today, $region, []);
43            $lastWeekInvoices = $this->request('get', 'factura/vence/' . $lastWeek, $region, []);
44
45            $resultNextWeekInvoices = $this->loopInvoices($nextWeekInvoices, 1, $region, $counter, $next10days);
46            /*$counter = $resultNextWeekInvoices["counter"] - 1;
47            if($counter >= 30){
48                return ['success' => true];
49            }*/
50
51            //$resultTodayInvoices = $this->loopInvoices($todayInvoices, 2, $region, $counter);
52            /*$counter = $resultTodayInvoices["counter"] - 1;
53            if($counter >= 30){
54                return ['success' => true];
55            }*/
56
57            $this->loopInvoices($lastWeekInvoices, 3, $region, $counter, $lastWeek);
58
59            $this->loopNextRemindersInvoices($region);
60
61            $this->loopNextRemindersClients($region, $next10days, $lastWeek);
62
63            return ['success' => true];
64        } catch (\Exception $e) {
65            Log::channel('g3w_invoices')->error($e->getMessage());
66            Log::error("Trace: " . $e->getTraceAsString());
67            return ['success' => false, 'error' => $e->getMessage()];
68        }
69    }
70
71    public function loopInvoices($invoices, $reminder_type, $region, $counter, $date=null){
72        $senders = [];
73        foreach ($invoices["facturas"] as $invoice) {
74            // Continue if region is Comunidad Valenciana and the invoice starts with M
75            if(
76                $region == "Comunidad Valenciana"
77                && strpos($invoice["ID"], 'M') === 0
78            ){
79                continue;
80            }
81
82            //Check if exist a next reminder and jump the loop then
83            $existNextReminderInvoice = TblInvoicesNextReminders::where("invoice_number", $invoice["ID"])
84                ->where('region', $region)
85                ->exists();
86
87            if ($existNextReminderInvoice) {
88                continue;
89            }
90
91            $dataInvoice = $this->request('get', "factura/" . $invoice["ID"], $region, []);
92
93            if(!isset($dataInvoice["factura"])){
94                continue;
95            }
96
97            $cobrada = $dataInvoice["factura"]["cobrada"];
98            $formaPago = $dataInvoice["factura"]["forma_pago_factura"];
99
100            //Invoice already paied
101            //Invoice that the payment method is not "transferencia"
102            if (
103                $cobrada !== "NO"
104                || stripos($formaPago, "tr") === false
105            ) {
106                continue;
107            }
108
109            $existNextReminderClient = TblInvoicesNextReminders::where("id_client", $dataInvoice['factura']['cod_cliente'])
110                ->where('region', $region)
111                ->exists();
112
113            if($existNextReminderClient){
114                $this->addToSheets($dataInvoice['factura']['cod_cliente'], $dataInvoice['factura']['n_factura'], $region);
115
116                TblInvoicesNextReminders::create([
117                    "id_client" => $dataInvoice['factura']['cod_cliente'],
118                    "region" => $region,
119                    "invoice_number" => $dataInvoice['factura']['n_factura']
120                ]);
121
122                continue;
123            }
124
125            $dataClient = null;
126
127            if ($dataInvoice["factura"]["cod_cliente"]) {
128                $dataClient = $this->request('get', "cliente/" . $dataInvoice["factura"]["cod_cliente"], $region, []);
129            }
130
131            if($dataClient["cliente"]["tipo_cliente"] == "Administrador"){
132                $codService = $dataInvoice['factura']["lineas"][0]["cod_servicio"];
133                $dataService = $this->request('get', "servicio/" . $codService, $region, []);
134
135                TblInvoiceAdministrators::create(array(
136                    'invoice_number' => $dataInvoice['factura']['n_factura'],
137                    'region' => $region,
138                    'name' => $dataClient['cliente']['empresa'],
139                    'CIF' => $dataClient['cliente']["cliente_cif"],
140                    'email' => $dataClient['cliente']["email"],
141                    'service_name' => $dataService["servicio"]["nombre_servicio"],
142                    'service_addres' => $dataService["servicio"]["direccion"],
143                    'send_date' => $date,
144                    'expiration_date' => $date,
145                    'amount' => $dataInvoice["factura"]["importe_total_factura"],
146                ));
147                continue;
148            }
149
150            $exists = TblInvoicesExceptions::where('cif', $dataClient['cliente']['cliente_cif'])
151                ->orWhere('name', $dataClient['cliente']['empresa'])
152                ->orWhere('administrator', $dataClient['cliente']['empresa'])
153                ->orWhere('id_admin_g3w', $invoice['ID'])
154                ->orWhere('invoice_number', $dataInvoice['factura']['n_factura'])
155                ->exists();
156
157            if ($exists) {
158                continue;
159            }
160
161            $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null;
162            $client_name = $dataClient["cliente"]["empresa"] ?? null;
163            $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null;
164            $cif = $dataClient["cliente"]["cliente_cif"] ?? null;
165            $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null;
166            $expiration_date = $date?? $dataInvoice["factura"]["vencimientos"][0]["fecha_vencimiento"];
167            $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null;
168            $collection_date = null;
169            $document = $dataInvoice["factura"]["documento"] ?? null;
170            $senders[$cif][] = [
171                "invoice_number" => $invoice_number,
172                "client_name" => $client_name,
173                "client_email" => $client_email,
174                "issued_date" => $issued_date,
175                "expiration_date" => $expiration_date,
176                "document" => $document,
177                "amount" => $amount,
178                "reminder_type" => $reminder_type,
179                "cif" => $cif,
180                "collection_date" => $collection_date,
181                "region" => $region
182            ];
183            
184        }
185
186        if (env("APP_ENV") === "production") {
187            foreach ($senders as $cif => $invoicesGroup) {
188
189                $totalFacturas = count($invoicesGroup);
190
191                if($totalFacturas === 1){
192                    $resultSend = $this->sendInvoice(
193                        $invoicesGroup[0]["invoice_number"], 
194                        $invoicesGroup[0]["client_name"], 
195                        $invoicesGroup[0]["client_email"], 
196                        $invoicesGroup[0]["issued_date"], 
197                        $invoicesGroup[0]["expiration_date"], 
198                        $invoicesGroup[0]["document"], 
199                        $invoicesGroup[0]["amount"], 
200                        $invoicesGroup[0]["reminder_type"]);
201
202                    if (!$resultSend["success"]) {
203                        continue;
204                    }
205
206                    TblInvoiceReminders::create([
207                        "invoice_number" => $invoicesGroup[0]["invoice_number"],
208                        "client_name" => $invoicesGroup[0]["client_name"],
209                        "client_email" => $invoicesGroup[0]["client_email"],
210                        "cif" => $invoicesGroup[0]["cif"],
211                        "issued_date" => $invoicesGroup[0]["issued_date"],
212                        "expiration_date" => $invoicesGroup[0]["expiration_date"],
213                        "amount" => $invoicesGroup[0]["amount"],
214                        "collection_date" => $invoicesGroup[0]["collection_date"],
215                        "region" => $invoicesGroup[0]["region"],
216                        "reminder_type" => $invoicesGroup[0]["reminder_type"]
217                    ]);
218                } else {
219                    $table = "<table style='border-collapse: collapse; width: 100%; font-family: Arial, sans-serif;'>
220                        <tr>
221                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Número Factura</th>
222                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Nombre cliente de servicio</th>
223                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Dirección del servicio</th>
224                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de emisión</th>
225                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de vencimiento</th>
226                            <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Importe</th>
227                        </tr>";
228                    $documentsBase64 = [];
229
230                    $invoice = $this->request('get', 'factura/' . $invoiceNumber, $region, []);
231                    $invoiceData = $invoice["factura"];
232
233                    $codService = $invoiceData["lineas"][0]["cod_servicio"];
234                    $dataService = $this->request('get', "servicio/" . $codService, $region, []);
235                    $dataService = $dataService["servicio"];
236
237                    foreach ($invoicesGroup as $invoice) {
238                        $invoiceNumber = $invoice["invoice_number"];
239                        $table .= "<tr>
240                            <td class='invoice_number' style='border: 1px solid #999; padding: 8px;'>" . $invoiceNumber . "</td>
241                            <td class='invoice_service_name' style='border: 1px solid #999; padding: 8px;'>" . $dataService["nombre_servicio"] . "</td>
242                            <td class='invoice_service_addres' style='border: 1px solid #999; padding: 8px;'>" . $dataService["direccion"] . "</td>
243                            <td class='invoice_send_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice["send_date"] . "</td>
244                            <td class='invoice_expiration_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice["expiration_date"] . "</td>
245                            <td class='invoice_amount' style='border: 1px solid #999; padding: 8px;'>" . $invoice["amount"] . "€</td>
246                        </tr>";
247
248                        $documentsBase64[] = [
249                            'content' => $invoiceData["documento"], 
250                            'filename' => "Factura_{$invoiceNumber}.pdf"
251                        ];
252                    }
253
254                    $table .= "</table>";
255
256                    
257                    $this->sendInvoice(
258                        $invoicesGroup[0]["invoice_number"], 
259                        $invoicesGroup[0]["name"], 
260                        $invoicesGroup[0]["email"], 
261                        $invoicesGroup[0]["send_date"], 
262                        $invoicesGroup[0]["expiration_date"], 
263                        $documentsBase64, 
264                        $invoicesGroup[0]["amount"], 
265                        $invoicesGroup[0]["reminder_type"] === "1" ? 5 : 6, 
266                        $table
267                    );
268                    
269                }
270
271                $counter++;
272                
273            }
274        }
275
276        return [
277            'success' => true,
278            'counter' => $counter,
279        ];
280    }
281
282    private function loopNextRemindersInvoices($region){
283        $nextReminders = TblInvoicesNextReminders::where('region', $region)
284            ->where('next_reminders', date("Y-m-d"))
285            ->pluck('invoice_number');
286
287        foreach ($nextReminders as $nextReminder) {
288            $dataInvoice = $this->request('get', 'factura/' . $nextReminder, $region, []);
289
290            $cobrada = $dataInvoice["factura"]["cobrada"];
291            $formaPago = $dataInvoice["factura"]["forma_pago_factura"];
292
293            //Invoice already paied
294            //Invoice that the payment method is not "transferencia"
295            if (
296                $cobrada !== "NO"
297                || stripos($formaPago, "tr") === false
298            ) {
299                continue;
300            }
301
302            $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null;
303            $client_name = $dataClient["cliente"]["empresa"] ?? null;
304            $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null;
305            $cif = $dataClient["cliente"]["cliente_cif"] ?? null;
306            $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null;
307            $expiration_date = $this->getVencimientosFormateados($dataInvoice);
308            $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null;
309            $collection_date = null;
310            $document = $dataInvoice["factura"]["documento"] ?? null;
311
312            if (env("APP_ENV") === "production") {
313                $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, 2);
314
315                if (!$resultSend["success"]) {
316                    continue;
317                }
318            }
319
320            TblInvoiceReminders::create(array(
321                "invoice_number" => $invoice_number,
322                "client_name" => $client_name,
323                "client_email" => $client_email,
324                "cif" => $cif,
325                "issued_date" => $issued_date,
326                "expiration_date" => $expiration_date,
327                "amount" => $amount,
328                "collection_date" => $collection_date,
329                "region" => $region,
330                "reminder_type" => 2
331            ));
332            }
333    }
334
335    private function loopNextRemindersClients($region, $next10days, $lastWeek){
336        $diaNext10 = date("j", strtotime($next10days));
337        $diaLastWeek = date("j", strtotime($lastWeek));
338
339        $nextWeekInvoices = TblInvoicesNextReminders::where("payment_day", $diaNext10)->get()->pluck('invoice_number');
340        $lastWeekInvoices = TblInvoicesNextReminders::where("payment_day", $diaLastWeek)->get()->pluck('invoice_number');
341
342        //type 1
343        foreach ($nextWeekInvoices as $reminder) {
344            if(!$reminder || is_null($reminder)){
345                continue;
346            }
347            $dataInvoice = $this->request('get', 'factura/' . $reminder, $region, []);
348            $cobrada = $dataInvoice["factura"]["cobrada"];
349            $formaPago = $dataInvoice["factura"]["forma_pago_factura"];
350
351            //Invoice already paied
352            //Invoice that the payment method is not "transferencia"
353            if (
354                $cobrada !== "NO"
355                || stripos($formaPago, "tr") === false
356            ) {
357                continue;
358            }
359
360            $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null;
361            $client_name = $dataClient["cliente"]["empresa"] ?? null;
362            $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null;
363            $cif = $dataClient["cliente"]["cliente_cif"] ?? null;
364            $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null;
365            $expiration_date = $this->getVencimientosFormateados($dataInvoice);
366            $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null;
367            $collection_date = null;
368            $document = $dataInvoice["factura"]["documento"] ?? null;
369
370            if (env("APP_ENV") === "production") {
371                $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, 1);
372
373                if (!$resultSend["success"]) {
374                    continue;
375                }
376            }
377
378            TblInvoiceReminders::create(array(
379                "invoice_number" => $invoice_number,
380                "client_name" => $client_name,
381                "client_email" => $client_email,
382                "cif" => $cif,
383                "issued_date" => $issued_date,
384                "expiration_date" => $expiration_date,
385                "amount" => $amount,
386                "collection_date" => $collection_date,
387                "region" => $region,
388                "reminder_type" => 1
389            ));
390        }
391
392        //type 3
393        foreach ($lastWeekInvoices as $reminder) {
394            if(!$reminder || is_null($reminder)){
395                continue;
396            }
397            $dataInvoice = $this->request('get', 'factura/' . $reminder, $region, []);
398
399            $cobrada = $dataInvoice["factura"]["cobrada"];
400            $formaPago = $dataInvoice["factura"]["forma_pago_factura"];
401
402            //Invoice already paied
403            //Invoice that the payment method is not "transferencia"
404            if (
405                $cobrada !== "NO"
406                || stripos($formaPago, "tr") === false
407            ) {
408                continue;
409            }
410
411            $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null;
412            $client_name = $dataClient["cliente"]["empresa"] ?? null;
413            $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null;
414            $cif = $dataClient["cliente"]["cliente_cif"] ?? null;
415            $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null;
416            $expiration_date = $this->getVencimientosFormateados($dataInvoice);
417            $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null;
418            $collection_date = null;
419            $document = $dataInvoice["factura"]["documento"] ?? null;
420
421            if (env("APP_ENV") === "production") {
422                $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount,  3);
423
424                if (!$resultSend["success"]) {
425                    continue;
426                }
427            }
428
429            TblInvoiceReminders::create(array(
430                "invoice_number" => $invoice_number,
431                "client_name" => $client_name,
432                "client_email" => $client_email,
433                "cif" => $cif,
434                "issued_date" => $issued_date,
435                "expiration_date" => $expiration_date,
436                "amount" => $amount,
437                "collection_date" => $collection_date,
438                "region" => $region,
439                "reminder_type" => 3
440            ));
441        }
442
443    }
444
445    public function sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, $reminder_type, $table = null, $month = null) {
446        if (
447            empty($invoice_number) ||
448            empty($client_name) ||
449            empty($client_email) ||
450            empty($issued_date) ||
451            empty($expiration_date) ||
452            empty($document) ||
453            empty($reminder_type)
454        ) {
455            Log::channel('g3w_invoices_not_send')->error("$invoice_number => {
456            'client_name': $client_name,
457            'client_email': $client_email,
458            'issued_date': $issued_date,
459            'expiration_date': $expiration_date,
460            'reminder_type': $reminder_type
461        }");
462
463            return ['success' => false, 'message' => 'Campos obligatorios faltantes'];
464        }
465
466        try {
467            $emailTemplate = TblInvoiceRemindersEmailTemplate::where('type', $reminder_type)->first();
468
469            if (!$emailTemplate) {
470                throw new \Exception('No se encontró la plantilla de correo para este tipo de recordatorio');
471            }
472
473            $clientEmails = preg_split('/\s*[,;\-\s]\s*/', $client_email, -1, PREG_SPLIT_NO_EMPTY);
474            $clientEmails = array_map('trim', $clientEmails);
475            $validEmails = [];
476
477            foreach ($clientEmails as $email) {
478                if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
479                    $validEmails[] = $email;
480                } else {
481                    Log::channel('g3w_invoices_not_send')->warning("Email inválido omitido: $email para factura $invoice_number");
482                }
483            }
484
485            if (empty($validEmails)) {
486                Log::channel('g3w_invoices_not_send')->error("No hay emails válidos para enviar factura $invoice_number");
487                return ['success' => false, 'message' => 'No hay direcciones de correo válidas'];
488            }
489
490            $sendgrid = new \SendGrid(env('SENDGRID_API_KEY', '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'));
491            $successCount = 0;
492            $failCount = 0;
493
494            foreach ($validEmails as $toEmail) {
495                try {
496                    $email = new \SendGrid\Mail\Mail();
497
498                    $fromEmail = 'recordatorio.factura@fire.es';
499                    $fromName = 'Recordatorio Facturas Grupo Fire';
500                    $email->setFrom($fromEmail, $fromName);
501                    $email->setReplyTo('recordatorio.facturas@fire.es', 'Recordatorio Facturas Grupo Fire');
502                    
503                    if($reminder_type != 4){
504                        $subject = str_replace(
505                            //['{{invoice_number}}', '{{expiration_date}}', '{{amount}}'],
506                            ['{{invoice_number}}', '{{expiration_date}}'],
507                            [$invoice_number, $expiration_date, $amount],
508                            $emailTemplate->subject
509                        );
510                    } else {
511                        $subject = str_replace(
512                            //['{{invoice_number}}', '{{expiration_date}}', '{{amount}}'],
513                            ['{{expiration_date}}'],
514                            [$month],
515                            $emailTemplate->subject
516                        );
517                    }
518                    
519
520                    Carbon::setLocale('es');
521
522                    if($reminder_type < 4){
523                        $body = str_replace(
524                            ['{{invoice_number}}', '{{client_name}}', '{{issued_date}}', '{{expiration_date}}', '{{amount}}'],
525                            [
526                                $invoice_number,
527                                $client_name,
528                                Carbon::createFromFormat('Y-m-d', $issued_date)->isoFormat('D [de] MMMM [de] YYYY'),
529                                $expiration_date,
530                                $amount
531                            ],
532                            $emailTemplate->html_content
533                        );
534                    }else{
535                        $body = str_replace(
536                        ['{{invoice_number}}', '{{client_name}}', '{{issued_date}}', '{{expiration_date}}', '{{amount}}','{{table}}'],
537                        [
538                            $invoice_number,
539                            $client_name,
540                            Carbon::createFromFormat('Y-m-d', $issued_date)->isoFormat('D [de] MMMM [de] YYYY'),
541                            $expiration_date,
542                            $amount,
543                            $table
544                        ],
545                        $emailTemplate->html_content
546                    );
547                    }
548
549                    
550
551                    $email->setSubject($subject);
552                    $email->addContent("text/html", $body);
553                    $email->addTo($toEmail, $client_name);                    
554
555                    if(is_array($document)){
556                        foreach ($document as $doc) {
557                            if (!base64_decode($doc["content"], true)) {
558                                throw new \Exception('El documento no es un base64 válido');
559                            }
560
561                            $attachment = new \SendGrid\Mail\Attachment();
562                            $attachment->setContent($doc["content"]);
563                            $attachment->setType("application/pdf");
564                            $attachment->setFilename($doc["filename"]);
565                            $attachment->setDisposition("attachment");
566                            $attachment->setContentId("factura_" . uniqid());
567                            $email->addAttachment($attachment);
568                            
569                        }
570                    }
571
572                    if (!is_array($document)) {
573                        if (!base64_decode($document, true)) {
574                            throw new \Exception('El documento no es un base64 válido');
575                        }
576
577                        $attachment = new \SendGrid\Mail\Attachment();
578                        $attachment->setContent($document);
579                        $attachment->setType("application/pdf");
580                        $attachment->setFilename("Factura_{$invoice_number}.pdf");
581                        $attachment->setDisposition("attachment");
582                        $attachment->setContentId("factura_" . uniqid());
583                        $email->addAttachment($attachment);
584                    }
585
586                    $response = $sendgrid->send($email);
587
588                    if ($response->statusCode() == 202) {
589                        $successCount++;
590                        Log::channel('g3w_invoices_sent')->info("Factura $invoice_number enviada exitosamente a: $toEmail");
591                    } else {
592                        $failCount++;
593                        Log::channel('g3w_invoices_not_send')->error("Error enviando a $toEmail" . $response->body());
594                    }
595
596                } catch (\Exception $e) {
597                    $failCount++;
598                    Log::channel('g3w_invoices_not_send')->error("Error enviando a $toEmail" . $e->getMessage());
599                    continue;
600                }
601            }
602
603            if ($successCount > 0) {
604                return [
605                    'success' => true,
606                    'message' => "Enviados: $successCount, Fallidos: $failCount",
607                    'sent_count' => $successCount,
608                    'failed_count' => $failCount
609                ];
610            } else {
611                return [
612                    'success' => false,
613                    'message' => "Todos los envíos fallaron",
614                    'sent_count' => $successCount,
615                    'failed_count' => $failCount
616                ];
617            }
618
619        } catch (\Exception $e) {
620            Log::channel('g3w_invoices_not_send')->error("Error general enviando factura $invoice_number" . $e->getMessage());
621            return ['success' => false, 'message' => $e->getMessage()];
622        }
623    }
624
625    public function getAllInvoices($region = "Cataluña"){
626        if($region === "All"){
627            $invoices = TblInvoiceReminders::orderBy('id', 'desc')
628                ->paginate(50);
629        } else {
630            $invoices = TblInvoiceReminders::where('region', $region)
631                ->orderBy('id', 'desc')
632                ->paginate(50);
633        }
634
635        return response()->json([
636            'invoices' => $invoices->items(),
637            'pagination' => [
638                'total' => $invoices->total(),
639                'current_page' => $invoices->currentPage(),
640                'per_page' => $invoices->perPage(),
641                'last_page' => $invoices->lastPage()
642            ]
643        ]);
644    }
645
646    public function getAllInvoicesExceptions(){
647        $invoices = TblInvoicesExceptions::orderBy('id', 'desc')->paginate(50);
648
649        return response()->json([
650            'invoices' => $invoices->items(),
651            'pagination' => [
652                'total' => $invoices->total(),
653                'current_page' => $invoices->currentPage(),
654                'per_page' => $invoices->perPage(),
655                'last_page' => $invoices->lastPage()
656            ]
657        ]);
658    }
659
660    public function sendCyCInvoices($region){
661        try{
662            if(!$region){
663                throw new Exception("No region provided");
664            }
665
666            $fromDay = 45;
667            $toDay = 35;
668            $today = Carbon::createFromDate(date('Y'), date('m'), date('d'));
669            $todayFormatted = Carbon::now()->format('Y-m-d');
670
671
672            $documentName =  $todayFormatted . '.csv';
673
674            $filePath = storage_path('app/public/uploads/CyC/' . $todayFormatted . '/' . $region .'/' . $documentName);
675
676            if (!file_exists(dirname($filePath))) {
677                mkdir(dirname($filePath), 0777, true);
678            }
679
680            $file = fopen($filePath, 'w');
681
682            fputcsv($file, [
683                'Service',
684                'Language',
685                'Cyc Poliza',
686                'SP Tax Idenfication Number',
687                'SP Country',
688                'BP TaxIdentificationNumber',
689                'SP Corporate Name',
690                'BP Country',
691                'Invoice Number',
692                'Invoice Issue Date',
693                'PD Installment Due Date',
694                'PD Payment Means',
695                'Total Amount',
696                'Taxable Base',
697                'Tax Rate',
698                'Tax Amount'
699            ], ";");
700
701            while ($fromDay > $toDay) {
702                $date = $today->copy()->subDays($fromDay)->format('Y-m-d');
703                $invoices = $this->request('get', 'factura/vence/' . $date, $region, []);
704                foreach ($invoices["facturas"] as $invoice) {
705                    $invoiceData = $this->request('get', 'factura/' . $invoice["ID"], $region, []);
706                    $invoiceData = $invoiceData["factura"];
707
708                    $dataClient = $this->request('get', "cliente/" . $invoiceData["cod_cliente"], $region, []);
709                    $invoiceCobrada = $invoiceData["cobrada"];
710                    $invoiceDocument = $invoiceData["documento"];
711                    $invoiceNumber = $invoiceData["n_factura"];
712
713                    if(
714                        !$invoiceDocument ||
715                        $invoiceCobrada !== "NO"){
716                        continue;
717                    }
718
719                    if($region === "Cataluña" && stripos($invoiceNumber, "R") === false){
720                        continue;
721                    }
722
723                    fputcsv($file, [
724                        'grabacionFacturas',
725                        'ESP',
726                        '156547',
727                        'B67795088',
728                        'ESP',
729                        $dataClient['cliente']['cliente_cif'],
730                        $dataClient['cliente']['empresa'],
731                        'ESP',
732                        $invoiceNumber,
733                        $invoiceData["fecha_creacion"],
734                        $invoiceData["vencimientos"][0]["fecha_vencimiento"],
735                        $invoiceData["forma_pago_factura"],
736                        $invoiceData["importe_total_factura"],
737                        $invoiceData["base_imponible_factura"],
738                        $invoiceData["iva_factura"]*100,
739                        $invoiceData["importe_iva_factura"]
740                    ],";");
741
742                }
743                --$fromDay;
744            }
745
746            fclose($file);
747
748            return ['success' => true];
749        } catch (\Exception $e) {
750            Log::channel('g3w_invoices')->error($e->getMessage());
751            return ['success' => false, 'error' => $e->getMessage()];
752        }
753    }
754    public function setAllMonthAdministratorsInvoices($region){
755        if(!$region){
756            return ['success'=> false,'error'=> 'No region provided'];
757        }
758        
759        try {
760            $now = Carbon::now();
761            $month = $now->format('m');
762        } catch (\Exception $e) {
763            Log::channel('g3w_invoices')->error("Formato de fecha de mes no válido (esperado YYYY-mm)");
764            return ['success'=> false, 'error'=> 'Formato de fecha de mes no válido (esperado YYYY-mm)'];
765        }
766
767        $invoices = $this->request('get', 'factura/vencemesadministrador/' . $month, $region, []);
768        
769        foreach ($invoices["facturas"] as $invoice) {
770            $invoiceData = $this->request('get', 'factura/' . $invoice["ID"], $region, []);
771            $invoiceData = $invoiceData["factura"];
772
773            // Continue if region is Comunidad Valenciana and the invoice starts with M
774            if(
775                $region == "Comunidad Valenciana"
776                && strpos($invoice["ID"], 'M') === 0
777            ){
778                continue;
779            }
780
781            $cobrada = $invoiceData["cobrada"];
782            $formaPago = $invoiceData["forma_pago_factura"];
783            //Invoice already paied
784            //Invoice that the payment method is not "transferencia"
785            if (
786                $cobrada !== "NO"
787                || stripos($formaPago, "tr") === false
788            ) {
789                continue;
790            }
791
792            if ($invoiceData["cod_cliente"]) {
793                $dataClient = $this->request('get', "cliente/" . $invoiceData["cod_cliente"], $region, []);
794                $dataClient = $dataClient["cliente"];
795            }
796
797            if (
798                $dataClient["tipo_cliente"] !== "Administrador"
799                ){
800                continue;
801            }
802
803            $codService = $invoiceData["lineas"][0]["cod_servicio"];
804            $dataService = $this->request('get', "servicio/" . $codService, $region, []);
805            $dataService = $dataService["servicio"];
806
807            TblInvoiceAdministrators::create(array(
808                'invoice_number' => $invoiceData['n_factura'],
809                'region' => $region,
810                'name' => $dataClient['empresa'],
811                'CIF' => $dataClient["cliente_cif"],
812                'email' => $dataClient["email"],
813                'service_name' => $dataService["nombre_servicio"],
814                'service_addres' => $dataService["direccion"],
815                'send_date' => Carbon::now('Europe/Madrid')->toDateString(),
816                'expiration_date' => Carbon::parse($invoice["fecha_vencimiento"])->toDateString(),
817                'amount' => $invoiceData["importe_total_factura"],
818            ));
819        }
820
821
822        return [
823            'success' => true,
824            'message' => "Proceso completado para el mes $month en la región $region.",
825        ];
826    }
827
828    public function sendAdministratorsInvoices($region){
829        if (!$region) {
830            return ['success' => false, 'error' => 'No region provided'];
831        }
832
833        $month = [
834            1 => "Enero",
835            2 => "Febrero",
836            3 => "Marzo",
837            4 => "Abril",
838            5 => "Mayo",
839            6 => "Junio",
840            7 => "Julio",
841            8 => "Agosto",
842            9 => "Septiembre",
843            10 => "Octubre",
844            11 => "Noviembre",
845            12 => "Diciembre",
846        ];
847
848        $startOfMonth = Carbon::now()->startOfMonth()->toDateString();
849        $endOfMonth = Carbon::now()->endOfMonth()->toDateString();
850
851        $currentMonthInvoices = TblInvoiceAdministrators::where('region', $region)
852            ->where('paid', 0)
853            ->whereBetween('expiration_date', [$startOfMonth, $endOfMonth])
854            ->orderBy('expiration_date', 'asc')
855            ->get();
856
857        $invoicesGroupedByAdministrator = $currentMonthInvoices->groupBy('CIF');
858
859        foreach ($invoicesGroupedByAdministrator as $administratorCIF => $administratorInvoices) {
860            $table = "<table style='border-collapse: collapse; width: 100%; font-family: Arial, sans-serif;'>
861            <tr>
862                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Número Factura</th>
863                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Nombre cliente de servicio</th>
864                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Dirección del servicio</th>
865                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de emisión</th>
866                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de vencimiento</th>
867                <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Importe</th>
868            </tr>";
869
870            $documentsBase64 = [];
871
872            foreach ($administratorInvoices as $invoice) {
873                $invoiceNumber = $invoice->invoice_number;
874                $table .= "<tr>
875                    <td class='invoice_number' style='border: 1px solid #999; padding: 8px;'>" . $invoiceNumber . "</td>
876                    <td class='invoice_service_name' style='border: 1px solid #999; padding: 8px;'>" . $invoice->service_name . "</td>
877                    <td class='invoice_service_addres' style='border: 1px solid #999; padding: 8px;'>" . $invoice->service_addres . "</td>
878                    <td class='invoice_send_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice->send_date . "</td>
879                    <td class='invoice_expiration_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice->expiration_date . "</td>
880                    <td class='invoice_amount' style='border: 1px solid #999; padding: 8px;'>" . $invoice->amount . "€</td>
881                </tr>";
882
883
884                $invoice = $this->request('get', 'factura/' . $invoiceNumber, $region, []);
885                $invoiceData = $invoice["factura"];
886
887                $documentsBase64[] = [
888                    'content' => $invoiceData["documento"], 
889                    'filename' => "Factura_{$invoiceNumber}.pdf"
890                ];
891            }
892
893            $table .= "</table>";
894
895            $monthText = $month[Carbon::now()->format('n')];
896            
897            $invoiceSendData = $this->sendInvoice(
898                $administratorInvoices->first()->invoice_number, 
899                $administratorInvoices->first()->name, 
900                $administratorInvoices->first()->email, 
901                $administratorInvoices->first()->send_date, 
902                $administratorInvoices->first()->expiration_date, 
903                $documentsBase64, 
904                $administratorInvoices->first()->amount, 
905                4, 
906                $table,
907                $monthText . " de " . Carbon::now()->format('Y')
908            );
909            
910
911        }
912        
913        return ['success' => true, 'message' => 'Processing complete'];
914    }
915        
916
917    //Google sheets
918    public function addToSheets($codCliente, $invoice, $region){
919        $spreadsheetId = '15Lc9tJnHDOGp33V9RH86mXtIybJBAWWlW4fe7knhDZY';
920        $sheetName = 'Hoja 1';
921        $googleSheetsService = null;
922        $nextRow = 1;
923
924        try {
925            $googleSheetsService = $this->getGoogleSheetsService();
926            $range = $sheetName . '!A:A';
927            $response = $googleSheetsService->spreadsheets_values->get($spreadsheetId, $range);
928            $values = $response->getValues();
929            $nextRow = $values ? count($values) + 1 : 1;
930
931        } catch (\Exception $e) {
932            if (strpos($e->getMessage(), 'Primera configuración requerida') !== false) {
933                return [
934                    'success' => false,
935                    'message' => $e->getMessage(),
936                    'requires_auth' => true
937                ];
938            }
939
940            return [
941                'success' => false,
942                'message' => 'Error de conexión con Google Sheets: ' . $e->getMessage(),
943            ];
944        }
945
946        if ($googleSheetsService && $nextRow === 1) {
947            $this->writeToGoogleSheet($googleSheetsService, $spreadsheetId, $sheetName, [
948                ['ID Cliente', 'Número de Factura', 'Fecha de Compromiso de Pago', 'Fecha de Compromiso de Pago + 10 días', 'Region']
949            ], 1);
950            $nextRow = 2;
951        }
952
953        $fechaCompromiso = date('Y-m-d');
954        $fechaCompromisoMas10 = date('Y-m-d', strtotime('+10 days'));
955
956        $data = [
957            $codCliente,
958            $invoice,
959            $fechaCompromiso,
960            $fechaCompromisoMas10,
961            $region
962        ];
963
964        if ($googleSheetsService) {
965            $this->writeToGoogleSheet($googleSheetsService, $spreadsheetId, $sheetName, [$data], $nextRow);
966            $nextRow++;
967        }
968
969    }
970
971    private function getGoogleSheetsService()
972    {
973
974        $redirectUrl = "";
975        if(env("APP_ENV") === "production"){
976            $redirectUrl = "https://fireservicetitan.com/api/google-sheets-callback";
977        }
978
979        if(env("APP_ENV") === "staging"){
980            $redirectUrl = "https://stg.fireservicetitan.com/api/google-sheets-callback";
981        }
982
983        if(env("APP_ENV") === "local"){
984            $redirectUrl = "http://localhost:8000/api/google-sheets-callback";
985        }
986
987        $client = new \Google\Client();
988        $client->setAuthConfig(storage_path('app/credentials.json'));
989        $client->addScope(\Google\Service\Sheets::SPREADSHEETS);
990        $client->setAccessType('offline');
991        $client->setPrompt('select_account consent');
992        $client->setRedirectUri($redirectUrl);
993
994        $tokenPath = storage_path('app/token.json');
995
996        if (file_exists($tokenPath)) {
997            $accessToken = json_decode(file_get_contents($tokenPath), true);
998            $client->setAccessToken($accessToken);
999        }
1000
1001        if ($client->isAccessTokenExpired()) {
1002            if ($client->getRefreshToken()) {
1003                $newToken = $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
1004                file_put_contents($tokenPath, json_encode($newToken));
1005            } else {
1006                $authUrl = $client->createAuthUrl();
1007                throw new \Exception("Initial setup required. Visit this URL to authorize: " . $authUrl);
1008            }
1009        }
1010
1011        return new \Google\Service\Sheets($client);
1012    }
1013
1014    private function writeToGoogleSheet($service, $spreadsheetId, $sheetName, $data, $rowNumber)
1015    {
1016        try {
1017            $range = $sheetName . '!A' . $rowNumber . ':E' . $rowNumber;
1018
1019            $body = new \Google\Service\Sheets\ValueRange([
1020                'values' => $data
1021            ]);
1022
1023            $params = [
1024                'valueInputOption' => 'USER_ENTERED'
1025            ];
1026
1027            $result = $service->spreadsheets_values->update(
1028                $spreadsheetId,
1029                $range,
1030                $body,
1031                $params
1032            );
1033
1034            Log::info('✅ Datos escritos en Google Sheets - Fila: ' . $rowNumber);
1035            return $result;
1036
1037        } catch (\Exception $e) {
1038            Log::error('❌ Error escribiendo en Google Sheets: ' . $e->getMessage());
1039            throw $e;
1040        }
1041    }
1042
1043    public function handleGoogleAuthCallback(Request $request)
1044    {
1045        try {
1046            // Verificar que el código existe
1047            if (!$request->get('code')) {
1048                Log::error('❌ No code parameter found');
1049                return response()->json([
1050                    'success' => false,
1051                    'message' => 'No authorization code provided'
1052                ], 400);
1053            }
1054
1055            $redirectUrl = "";
1056
1057            if(env("APP_ENV") === "production"){
1058                $redirectUrl = "https://fireservicetitan.com/api/google-sheets-callback";
1059            }
1060
1061            if(env("APP_ENV") === "staging"){
1062                $redirectUrl = "https://stg.fireservicetitan.com/api/google-sheets-callback";
1063            }
1064
1065            if(env("APP_ENV") === "local"){
1066                $redirectUrl = "http://localhost:8000/api/google-sheets-callback";
1067            }
1068
1069            $client = new \Google\Client();
1070            $client->setAuthConfig(storage_path('app/credentials.json'));
1071            $client->addScope(\Google\Service\Sheets::SPREADSHEETS);
1072            $client->setRedirectUri($redirectUrl);
1073
1074            $token = $client->fetchAccessTokenWithAuthCode($request->get('code'));
1075
1076            file_put_contents(storage_path('app/token.json'), json_encode($token));
1077
1078            return response()->json([
1079                'success' => true,
1080                'message' => 'Google Sheets authentication complete! You can now run the invoicing function.'
1081            ]);
1082
1083        } catch (\Exception $e) {
1084            Log::error('Error on Google sheets callback: ' . $e->getMessage());
1085            Log::error('Error details: ' . $e->getTraceAsString());
1086            return response()->json([
1087                'success' => false,
1088                'message' => 'Error: ' . $e->getMessage()
1089            ], 400);
1090        }
1091    }
1092
1093    private function getVencimientosFormateados($dataInvoice)
1094    {
1095        $vencimientos = $dataInvoice["factura"]["vencimientos"] ?? [];
1096
1097        if (count($vencimientos) === 0) {
1098            return null;
1099        }
1100
1101        $fechasFormateadas = array_map(function($vencimiento) {
1102            return Carbon::createFromFormat('Y-m-d', $vencimiento['fecha_vencimiento'])
1103                ->isoFormat('D [de] MMMM [de] YYYY');
1104        }, $vencimientos);
1105
1106        return implode(' ó ', $fechasFormateadas);
1107    }
1108
1109}