Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 790 |
|
0.00% |
0 / 17 |
CRAP | |
0.00% |
0 / 1 |
| FacturasService | |
0.00% |
0 / 790 |
|
0.00% |
0 / 17 |
17556 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getInvoices | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
| loopInvoices | |
0.00% |
0 / 146 |
|
0.00% |
0 / 1 |
342 | |||
| loopNextRemindersInvoices | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
42 | |||
| loopNextRemindersClients | |
0.00% |
0 / 72 |
|
0.00% |
0 / 1 |
240 | |||
| sendInvoice | |
0.00% |
0 / 132 |
|
0.00% |
0 / 1 |
600 | |||
| getAllInvoices | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
6 | |||
| getAllInvoicesExceptions | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
| sendCyCInvoices | |
0.00% |
0 / 68 |
|
0.00% |
0 / 1 |
110 | |||
| setAllMonthAdministratorsInvoices | |
0.00% |
0 / 43 |
|
0.00% |
0 / 1 |
110 | |||
| sendAdministratorsInvoices | |
0.00% |
0 / 60 |
|
0.00% |
0 / 1 |
20 | |||
| sendCallCenterInvoices | |
0.00% |
0 / 75 |
|
0.00% |
0 / 1 |
210 | |||
| addToSheets | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
56 | |||
| getGoogleSheetsService | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
56 | |||
| writeToGoogleSheet | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
6 | |||
| handleGoogleAuthCallback | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
42 | |||
| getVencimientosFormateados | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services; |
| 4 | |
| 5 | use App\Models\TblCompanies; |
| 6 | use App\Models\TblInvoiceAdministrators; |
| 7 | use App\Models\TblInvoiceReminders; |
| 8 | use App\Models\TblInvoiceRemindersEmailTemplate; |
| 9 | use App\Models\TblInvoicesExceptions; |
| 10 | use App\Models\TblInvoicesNextReminders; |
| 11 | use App\Services\GestionaService; |
| 12 | use Carbon\Carbon; |
| 13 | use Google\Service\Sheets; |
| 14 | use Google\Service\Sheets\ValueRange; |
| 15 | use Illuminate\Http\Request; |
| 16 | use Illuminate\Support\Facades\Log; |
| 17 | use Mockery\Exception; |
| 18 | use PhpOffice\PhpSpreadsheet\Spreadsheet; |
| 19 | use PhpOffice\PhpSpreadsheet\Style\Fill; |
| 20 | use PhpOffice\PhpSpreadsheet\Writer\Xlsx; |
| 21 | |
| 22 | class 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 | $region); |
| 202 | |
| 203 | if (!$resultSend["success"]) { |
| 204 | continue; |
| 205 | } |
| 206 | |
| 207 | TblInvoiceReminders::create([ |
| 208 | "invoice_number" => $invoicesGroup[0]["invoice_number"], |
| 209 | "client_name" => $invoicesGroup[0]["client_name"], |
| 210 | "client_email" => $invoicesGroup[0]["client_email"], |
| 211 | "cif" => $invoicesGroup[0]["cif"], |
| 212 | "issued_date" => $invoicesGroup[0]["issued_date"], |
| 213 | "expiration_date" => $invoicesGroup[0]["expiration_date"], |
| 214 | "amount" => $invoicesGroup[0]["amount"], |
| 215 | "collection_date" => $invoicesGroup[0]["collection_date"], |
| 216 | "region" => $invoicesGroup[0]["region"], |
| 217 | "reminder_type" => $invoicesGroup[0]["reminder_type"] |
| 218 | ]); |
| 219 | } else { |
| 220 | $table = "<table style='border-collapse: collapse; width: 100%; font-family: Arial, sans-serif;'> |
| 221 | <tr> |
| 222 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Número Factura</th> |
| 223 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Nombre cliente de servicio</th> |
| 224 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Dirección del servicio</th> |
| 225 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de emisión</th> |
| 226 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de vencimiento</th> |
| 227 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Importe</th> |
| 228 | </tr>"; |
| 229 | $documentsBase64 = []; |
| 230 | |
| 231 | foreach ($invoicesGroup as $invoice) { |
| 232 | $invoiceG3W = $this->request('get', 'factura/' . $invoice["invoice_number"], $region, []); |
| 233 | $invoiceData = $invoiceG3W["factura"]; |
| 234 | |
| 235 | $codService = $invoiceData["lineas"][0]["cod_servicio"]; |
| 236 | $dataService = $this->request('get', "servicio/" . $codService, $region, []); |
| 237 | $dataService = $dataService["servicio"]; |
| 238 | |
| 239 | $invoiceNumber = $invoice["invoice_number"]; |
| 240 | $table .= "<tr> |
| 241 | <td class='invoice_number' style='border: 1px solid #999; padding: 8px;'>" . $invoiceNumber . "</td> |
| 242 | <td class='invoice_service_name' style='border: 1px solid #999; padding: 8px;'>" . $dataService["nombre_servicio"] . "</td> |
| 243 | <td class='invoice_service_addres' style='border: 1px solid #999; padding: 8px;'>" . $dataService["direccion"] . "</td> |
| 244 | <td class='invoice_send_date' style='border: 1px solid #999; padding: 8px;'>" . Carbon::now()->format('d-m-Y') . "</td> |
| 245 | <td class='invoice_expiration_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice["expiration_date"] . "</td> |
| 246 | <td class='invoice_amount' style='border: 1px solid #999; padding: 8px;'>" . $invoice["amount"] . "€</td> |
| 247 | </tr>"; |
| 248 | |
| 249 | $documentsBase64[] = [ |
| 250 | 'content' => $invoiceData["documento"], |
| 251 | 'filename' => "Factura_{$invoiceNumber}.pdf" |
| 252 | ]; |
| 253 | } |
| 254 | |
| 255 | $table .= "</table>"; |
| 256 | |
| 257 | |
| 258 | $this->sendInvoice( |
| 259 | $invoicesGroup[0]["invoice_number"], |
| 260 | $invoicesGroup[0]["client_name"], |
| 261 | $invoicesGroup[0]["client_email"], |
| 262 | Carbon::now()->format('d-m-Y'), |
| 263 | $invoicesGroup[0]["expiration_date"], |
| 264 | $documentsBase64, |
| 265 | $invoicesGroup[0]["amount"], |
| 266 | $invoicesGroup[0]["reminder_type"] === "1" ? 5 : 6, |
| 267 | $region, |
| 268 | $table |
| 269 | ); |
| 270 | |
| 271 | } |
| 272 | |
| 273 | $counter++; |
| 274 | |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | return [ |
| 279 | 'success' => true, |
| 280 | 'counter' => $counter, |
| 281 | ]; |
| 282 | } |
| 283 | |
| 284 | private function loopNextRemindersInvoices($region){ |
| 285 | $nextReminders = TblInvoicesNextReminders::where('region', $region) |
| 286 | ->where('next_reminders', date("Y-m-d")) |
| 287 | ->pluck('invoice_number'); |
| 288 | |
| 289 | foreach ($nextReminders as $nextReminder) { |
| 290 | $dataInvoice = $this->request('get', 'factura/' . $nextReminder, $region, []); |
| 291 | |
| 292 | $cobrada = $dataInvoice["factura"]["cobrada"]; |
| 293 | $formaPago = $dataInvoice["factura"]["forma_pago_factura"]; |
| 294 | |
| 295 | //Invoice already paied |
| 296 | //Invoice that the payment method is not "transferencia" |
| 297 | if ( |
| 298 | $cobrada !== "NO" |
| 299 | || stripos($formaPago, "tr") === false |
| 300 | ) { |
| 301 | continue; |
| 302 | } |
| 303 | |
| 304 | $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null; |
| 305 | $client_name = $dataClient["cliente"]["empresa"] ?? null; |
| 306 | $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null; |
| 307 | $cif = $dataClient["cliente"]["cliente_cif"] ?? null; |
| 308 | $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null; |
| 309 | $expiration_date = $this->getVencimientosFormateados($dataInvoice); |
| 310 | $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null; |
| 311 | $collection_date = null; |
| 312 | $document = $dataInvoice["factura"]["documento"] ?? null; |
| 313 | |
| 314 | if (env("APP_ENV") === "production") { |
| 315 | $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, 2, $region); |
| 316 | |
| 317 | if (!$resultSend["success"]) { |
| 318 | continue; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | TblInvoiceReminders::create(array( |
| 323 | "invoice_number" => $invoice_number, |
| 324 | "client_name" => $client_name, |
| 325 | "client_email" => $client_email, |
| 326 | "cif" => $cif, |
| 327 | "issued_date" => $issued_date, |
| 328 | "expiration_date" => $expiration_date, |
| 329 | "amount" => $amount, |
| 330 | "collection_date" => $collection_date, |
| 331 | "region" => $region, |
| 332 | "reminder_type" => 2 |
| 333 | )); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | private function loopNextRemindersClients($region, $next10days, $lastWeek){ |
| 338 | $diaNext10 = date("j", strtotime($next10days)); |
| 339 | $diaLastWeek = date("j", strtotime($lastWeek)); |
| 340 | |
| 341 | $nextWeekInvoices = TblInvoicesNextReminders::where("payment_day", $diaNext10)->get()->pluck('invoice_number'); |
| 342 | $lastWeekInvoices = TblInvoicesNextReminders::where("payment_day", $diaLastWeek)->get()->pluck('invoice_number'); |
| 343 | |
| 344 | //type 1 |
| 345 | foreach ($nextWeekInvoices as $reminder) { |
| 346 | if(!$reminder || is_null($reminder)){ |
| 347 | continue; |
| 348 | } |
| 349 | $dataInvoice = $this->request('get', 'factura/' . $reminder, $region, []); |
| 350 | $cobrada = $dataInvoice["factura"]["cobrada"]; |
| 351 | $formaPago = $dataInvoice["factura"]["forma_pago_factura"]; |
| 352 | |
| 353 | //Invoice already paied |
| 354 | //Invoice that the payment method is not "transferencia" |
| 355 | if ( |
| 356 | $cobrada !== "NO" |
| 357 | || stripos($formaPago, "tr") === false |
| 358 | ) { |
| 359 | continue; |
| 360 | } |
| 361 | |
| 362 | $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null; |
| 363 | $client_name = $dataClient["cliente"]["empresa"] ?? null; |
| 364 | $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null; |
| 365 | $cif = $dataClient["cliente"]["cliente_cif"] ?? null; |
| 366 | $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null; |
| 367 | $expiration_date = $this->getVencimientosFormateados($dataInvoice); |
| 368 | $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null; |
| 369 | $collection_date = null; |
| 370 | $document = $dataInvoice["factura"]["documento"] ?? null; |
| 371 | |
| 372 | if (env("APP_ENV") === "production") { |
| 373 | $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, 1, $region); |
| 374 | |
| 375 | if (!$resultSend["success"]) { |
| 376 | continue; |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | TblInvoiceReminders::create(array( |
| 381 | "invoice_number" => $invoice_number, |
| 382 | "client_name" => $client_name, |
| 383 | "client_email" => $client_email, |
| 384 | "cif" => $cif, |
| 385 | "issued_date" => $issued_date, |
| 386 | "expiration_date" => $expiration_date, |
| 387 | "amount" => $amount, |
| 388 | "collection_date" => $collection_date, |
| 389 | "region" => $region, |
| 390 | "reminder_type" => 1 |
| 391 | )); |
| 392 | } |
| 393 | |
| 394 | //type 3 |
| 395 | foreach ($lastWeekInvoices as $reminder) { |
| 396 | if(!$reminder || is_null($reminder)){ |
| 397 | continue; |
| 398 | } |
| 399 | $dataInvoice = $this->request('get', 'factura/' . $reminder, $region, []); |
| 400 | |
| 401 | $cobrada = $dataInvoice["factura"]["cobrada"]; |
| 402 | $formaPago = $dataInvoice["factura"]["forma_pago_factura"]; |
| 403 | |
| 404 | //Invoice already paied |
| 405 | //Invoice that the payment method is not "transferencia" |
| 406 | if ( |
| 407 | $cobrada !== "NO" |
| 408 | || stripos($formaPago, "tr") === false |
| 409 | ) { |
| 410 | continue; |
| 411 | } |
| 412 | |
| 413 | $invoice_number = $dataInvoice["factura"]["n_factura"] ?? null; |
| 414 | $client_name = $dataClient["cliente"]["empresa"] ?? null; |
| 415 | $client_email = $dataClient["cliente"]["email"] ?? $dataClient["cliente"]["email_facturacion"] ?? null; |
| 416 | $cif = $dataClient["cliente"]["cliente_cif"] ?? null; |
| 417 | $issued_date = $dataInvoice["factura"]["fecha_emision"] ?? null; |
| 418 | $expiration_date = $this->getVencimientosFormateados($dataInvoice); |
| 419 | $amount = $dataInvoice["factura"]["importe_total_factura"] ?? null; |
| 420 | $collection_date = null; |
| 421 | $document = $dataInvoice["factura"]["documento"] ?? null; |
| 422 | |
| 423 | if (env("APP_ENV") === "production") { |
| 424 | $resultSend = $this->sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, 3, $region); |
| 425 | |
| 426 | if (!$resultSend["success"]) { |
| 427 | continue; |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | TblInvoiceReminders::create(array( |
| 432 | "invoice_number" => $invoice_number, |
| 433 | "client_name" => $client_name, |
| 434 | "client_email" => $client_email, |
| 435 | "cif" => $cif, |
| 436 | "issued_date" => $issued_date, |
| 437 | "expiration_date" => $expiration_date, |
| 438 | "amount" => $amount, |
| 439 | "collection_date" => $collection_date, |
| 440 | "region" => $region, |
| 441 | "reminder_type" => 3 |
| 442 | )); |
| 443 | } |
| 444 | |
| 445 | } |
| 446 | |
| 447 | public function sendInvoice($invoice_number, $client_name, $client_email, $issued_date, $expiration_date, $document, $amount, $reminder_type, $region, $table = null, $month = null) { |
| 448 | if ( |
| 449 | empty($invoice_number) || |
| 450 | empty($client_name) || |
| 451 | empty($client_email) || |
| 452 | empty($issued_date) || |
| 453 | empty($expiration_date) || |
| 454 | empty($document) || |
| 455 | empty($reminder_type) |
| 456 | ) { |
| 457 | Log::channel('g3w_invoices_not_send')->error("$invoice_number => { |
| 458 | 'client_name': $client_name, |
| 459 | 'client_email': $client_email, |
| 460 | 'issued_date': $issued_date, |
| 461 | 'expiration_date': $expiration_date, |
| 462 | 'reminder_type': $reminder_type |
| 463 | }"); |
| 464 | |
| 465 | return ['success' => false, 'message' => 'Campos obligatorios faltantes']; |
| 466 | } |
| 467 | |
| 468 | try { |
| 469 | $emailTemplate = TblInvoiceRemindersEmailTemplate::where('type', $reminder_type)->first(); |
| 470 | |
| 471 | if (!$emailTemplate) { |
| 472 | throw new \Exception('No se encontró la plantilla de correo para este tipo de recordatorio'); |
| 473 | } |
| 474 | |
| 475 | $clientEmails = preg_split('/\s*[,;\-\s]\s*/', $client_email, -1, PREG_SPLIT_NO_EMPTY); |
| 476 | $clientEmails = array_map('trim', $clientEmails); |
| 477 | $validEmails = []; |
| 478 | |
| 479 | foreach ($clientEmails as $email) { |
| 480 | if (filter_var($email, FILTER_VALIDATE_EMAIL)) { |
| 481 | $validEmails[] = $email; |
| 482 | } else { |
| 483 | Log::channel('g3w_invoices_not_send')->warning("Email inválido omitido: $email para factura $invoice_number"); |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | if (empty($validEmails)) { |
| 488 | Log::channel('g3w_invoices_not_send')->error("No hay emails válidos para enviar factura $invoice_number"); |
| 489 | return ['success' => false, 'message' => 'No hay direcciones de correo válidas']; |
| 490 | } |
| 491 | |
| 492 | $sendgrid = new \SendGrid(env('SENDGRID_API_KEY', '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')); |
| 493 | $successCount = 0; |
| 494 | $failCount = 0; |
| 495 | |
| 496 | foreach ($validEmails as $toEmail) { |
| 497 | try { |
| 498 | $email = new \SendGrid\Mail\Mail(); |
| 499 | |
| 500 | $fromEmail = 'recordatorio.factura@fire.es'; |
| 501 | $fromName = 'Recordatorio Facturas Grupo Fire'; |
| 502 | $email->setFrom($fromEmail, $fromName); |
| 503 | $email->setReplyTo('recordatorio.facturas@fire.es', 'Recordatorio Facturas Grupo Fire'); |
| 504 | |
| 505 | if($reminder_type != 4){ |
| 506 | $subject = str_replace( |
| 507 | //['{{invoice_number}}', '{{expiration_date}}', '{{amount}}'], |
| 508 | ['{{invoice_number}}', '{{expiration_date}}'], |
| 509 | [$invoice_number, $expiration_date, $amount], |
| 510 | $emailTemplate->subject |
| 511 | ); |
| 512 | } else { |
| 513 | $subject = str_replace( |
| 514 | //['{{invoice_number}}', '{{expiration_date}}', '{{amount}}'], |
| 515 | ['{{expiration_date}}'], |
| 516 | [$month], |
| 517 | $emailTemplate->subject |
| 518 | ); |
| 519 | } |
| 520 | |
| 521 | $baseUrl = "http://aiwf.fire.es/sepa/form/"; |
| 522 | |
| 523 | $params = array( |
| 524 | 'nombre_deudor' => $client_name, |
| 525 | //'cif_deudor' => $datos['cif'], |
| 526 | //'direccion_deudor' => urlencode($datos['direccion_deudor']), |
| 527 | //'codigo_postal' => urlencode($datos['codigo_postal']), |
| 528 | //'provincia_pais' => urlencode($datos['provincia_pais']), |
| 529 | //'iban' => $datos['iban'], |
| 530 | //'swift' => $datos['swift'], |
| 531 | //'nombre_apoderado' => urlencode($datos['nombre_apoderado']), |
| 532 | //'nif_apoderado' => $datos['nif_apoderado'], |
| 533 | 'fecha' => $issued_date, |
| 534 | 'email_contacto' => $client_email, |
| 535 | 'region' => $region |
| 536 | ); |
| 537 | |
| 538 | $url = $baseUrl . '?' . http_build_query($params); |
| 539 | |
| 540 | |
| 541 | Carbon::setLocale('es'); |
| 542 | |
| 543 | if($reminder_type < 4){ |
| 544 | $body = str_replace( |
| 545 | ['{{invoice_number}}', '{{client_name}}', '{{issued_date}}', '{{expiration_date}}', '{{amount}}', '{{url}}'], |
| 546 | [ |
| 547 | $invoice_number, |
| 548 | $client_name, |
| 549 | Carbon::createFromFormat('Y-m-d', $issued_date)->isoFormat('D [de] MMMM [de] YYYY'), |
| 550 | $expiration_date, |
| 551 | $amount, |
| 552 | $url |
| 553 | ], |
| 554 | $emailTemplate->html_content |
| 555 | ); |
| 556 | }else{ |
| 557 | $body = str_replace( |
| 558 | ['{{invoice_number}}', '{{client_name}}', '{{issued_date}}', '{{expiration_date}}', '{{amount}}','{{table}}'], |
| 559 | [ |
| 560 | $invoice_number, |
| 561 | $client_name, |
| 562 | Carbon::createFromFormat('Y-m-d', $issued_date)->isoFormat('D [de] MMMM [de] YYYY'), |
| 563 | $expiration_date, |
| 564 | $amount, |
| 565 | $table |
| 566 | ], |
| 567 | $emailTemplate->html_content |
| 568 | ); |
| 569 | } |
| 570 | |
| 571 | |
| 572 | |
| 573 | $email->setSubject($subject); |
| 574 | $email->addContent("text/html", $body); |
| 575 | $email->addTo($toEmail, $client_name); |
| 576 | |
| 577 | if(is_array($document)){ |
| 578 | foreach ($document as $doc) { |
| 579 | if (!base64_decode($doc["content"], true)) { |
| 580 | throw new \Exception('El documento no es un base64 válido'); |
| 581 | } |
| 582 | |
| 583 | $attachment = new \SendGrid\Mail\Attachment(); |
| 584 | $attachment->setContent($doc["content"]); |
| 585 | $attachment->setType("application/pdf"); |
| 586 | $attachment->setFilename($doc["filename"]); |
| 587 | $attachment->setDisposition("attachment"); |
| 588 | $attachment->setContentId("factura_" . uniqid()); |
| 589 | $email->addAttachment($attachment); |
| 590 | |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | if (!is_array($document)) { |
| 595 | if (!base64_decode($document, true)) { |
| 596 | throw new \Exception('El documento no es un base64 válido'); |
| 597 | } |
| 598 | |
| 599 | $attachment = new \SendGrid\Mail\Attachment(); |
| 600 | $attachment->setContent($document); |
| 601 | $attachment->setType("application/pdf"); |
| 602 | $attachment->setFilename("Factura_{$invoice_number}.pdf"); |
| 603 | $attachment->setDisposition("attachment"); |
| 604 | $attachment->setContentId("factura_" . uniqid()); |
| 605 | $email->addAttachment($attachment); |
| 606 | } |
| 607 | |
| 608 | $response = $sendgrid->send($email); |
| 609 | |
| 610 | if ($response->statusCode() == 202) { |
| 611 | $successCount++; |
| 612 | Log::channel('g3w_invoices_sent')->info("Factura $invoice_number enviada exitosamente a: $toEmail"); |
| 613 | } else { |
| 614 | $failCount++; |
| 615 | Log::channel('g3w_invoices_not_send')->error("Error enviando a $toEmail: " . $response->body()); |
| 616 | } |
| 617 | |
| 618 | } catch (\Exception $e) { |
| 619 | $failCount++; |
| 620 | Log::channel('g3w_invoices_not_send')->error("Error enviando a $toEmail: " . $e->getMessage()); |
| 621 | continue; |
| 622 | } |
| 623 | } |
| 624 | |
| 625 | if ($successCount > 0) { |
| 626 | return [ |
| 627 | 'success' => true, |
| 628 | 'message' => "Enviados: $successCount, Fallidos: $failCount", |
| 629 | 'sent_count' => $successCount, |
| 630 | 'failed_count' => $failCount |
| 631 | ]; |
| 632 | } else { |
| 633 | return [ |
| 634 | 'success' => false, |
| 635 | 'message' => "Todos los envÃos fallaron", |
| 636 | 'sent_count' => $successCount, |
| 637 | 'failed_count' => $failCount |
| 638 | ]; |
| 639 | } |
| 640 | |
| 641 | } catch (\Exception $e) { |
| 642 | Log::channel('g3w_invoices_not_send')->error("Error general enviando factura $invoice_number: " . $e->getMessage()); |
| 643 | return ['success' => false, 'message' => $e->getMessage()]; |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | public function getAllInvoices($region = "Cataluña"){ |
| 648 | if($region === "All"){ |
| 649 | $invoices = TblInvoiceReminders::orderBy('id', 'desc') |
| 650 | ->paginate(50); |
| 651 | } else { |
| 652 | $invoices = TblInvoiceReminders::where('region', $region) |
| 653 | ->orderBy('id', 'desc') |
| 654 | ->paginate(50); |
| 655 | } |
| 656 | |
| 657 | return response()->json([ |
| 658 | 'invoices' => $invoices->items(), |
| 659 | 'pagination' => [ |
| 660 | 'total' => $invoices->total(), |
| 661 | 'current_page' => $invoices->currentPage(), |
| 662 | 'per_page' => $invoices->perPage(), |
| 663 | 'last_page' => $invoices->lastPage() |
| 664 | ] |
| 665 | ]); |
| 666 | } |
| 667 | |
| 668 | public function getAllInvoicesExceptions(){ |
| 669 | $invoices = TblInvoicesExceptions::orderBy('id', 'desc')->paginate(50); |
| 670 | |
| 671 | return response()->json([ |
| 672 | 'invoices' => $invoices->items(), |
| 673 | 'pagination' => [ |
| 674 | 'total' => $invoices->total(), |
| 675 | 'current_page' => $invoices->currentPage(), |
| 676 | 'per_page' => $invoices->perPage(), |
| 677 | 'last_page' => $invoices->lastPage() |
| 678 | ] |
| 679 | ]); |
| 680 | } |
| 681 | |
| 682 | public function sendCyCInvoices($region){ |
| 683 | try{ |
| 684 | if(!$region){ |
| 685 | throw new Exception("No region provided"); |
| 686 | } |
| 687 | |
| 688 | $fromDay = 45; |
| 689 | $toDay = 35; |
| 690 | $today = Carbon::createFromDate(date('Y'), date('m'), date('d')); |
| 691 | $todayFormatted = Carbon::now()->format('Y-m-d'); |
| 692 | |
| 693 | |
| 694 | $documentName = $todayFormatted . '.csv'; |
| 695 | |
| 696 | $filePath = storage_path('app/public/uploads/CyC/' . $todayFormatted . '/' . $region .'/' . $documentName); |
| 697 | |
| 698 | if (!file_exists(dirname($filePath))) { |
| 699 | mkdir(dirname($filePath), 0777, true); |
| 700 | } |
| 701 | |
| 702 | $file = fopen($filePath, 'w'); |
| 703 | |
| 704 | fputcsv($file, [ |
| 705 | 'Service', |
| 706 | 'Language', |
| 707 | 'Cyc Poliza', |
| 708 | 'SP Tax Idenfication Number', |
| 709 | 'SP Country', |
| 710 | 'BP TaxIdentificationNumber', |
| 711 | 'SP Corporate Name', |
| 712 | 'BP Country', |
| 713 | 'Invoice Number', |
| 714 | 'Invoice Issue Date', |
| 715 | 'PD Installment Due Date', |
| 716 | 'PD Payment Means', |
| 717 | 'Total Amount', |
| 718 | 'Taxable Base', |
| 719 | 'Tax Rate', |
| 720 | 'Tax Amount' |
| 721 | ], ";"); |
| 722 | |
| 723 | while ($fromDay > $toDay) { |
| 724 | $date = $today->copy()->subDays($fromDay)->format('Y-m-d'); |
| 725 | $invoices = $this->request('get', 'factura/vence/' . $date, $region, []); |
| 726 | foreach ($invoices["facturas"] as $invoice) { |
| 727 | $invoiceData = $this->request('get', 'factura/' . $invoice["ID"], $region, []); |
| 728 | $invoiceData = $invoiceData["factura"]; |
| 729 | |
| 730 | $dataClient = $this->request('get', "cliente/" . $invoiceData["cod_cliente"], $region, []); |
| 731 | $invoiceCobrada = $invoiceData["cobrada"]; |
| 732 | $invoiceDocument = $invoiceData["documento"]; |
| 733 | $invoiceNumber = $invoiceData["n_factura"]; |
| 734 | |
| 735 | if( |
| 736 | !$invoiceDocument || |
| 737 | $invoiceCobrada !== "NO"){ |
| 738 | continue; |
| 739 | } |
| 740 | |
| 741 | if($region === "Cataluña" && stripos($invoiceNumber, "R") === false){ |
| 742 | continue; |
| 743 | } |
| 744 | |
| 745 | fputcsv($file, [ |
| 746 | 'grabacionFacturas', |
| 747 | 'ESP', |
| 748 | '156547', |
| 749 | 'B67795088', |
| 750 | 'ESP', |
| 751 | $dataClient['cliente']['cliente_cif'], |
| 752 | $dataClient['cliente']['empresa'], |
| 753 | 'ESP', |
| 754 | $invoiceNumber, |
| 755 | $invoiceData["fecha_creacion"], |
| 756 | $invoiceData["vencimientos"][0]["fecha_vencimiento"], |
| 757 | $invoiceData["forma_pago_factura"], |
| 758 | $invoiceData["importe_total_factura"], |
| 759 | $invoiceData["base_imponible_factura"], |
| 760 | $invoiceData["iva_factura"]*100, |
| 761 | $invoiceData["importe_iva_factura"] |
| 762 | ],";"); |
| 763 | |
| 764 | } |
| 765 | --$fromDay; |
| 766 | } |
| 767 | |
| 768 | fclose($file); |
| 769 | |
| 770 | return ['success' => true]; |
| 771 | } catch (\Exception $e) { |
| 772 | Log::channel('g3w_invoices')->error($e->getMessage()); |
| 773 | return ['success' => false, 'error' => $e->getMessage()]; |
| 774 | } |
| 775 | } |
| 776 | public function setAllMonthAdministratorsInvoices($region){ |
| 777 | if(!$region){ |
| 778 | return ['success'=> false,'error'=> 'No region provided']; |
| 779 | } |
| 780 | |
| 781 | try { |
| 782 | $now = Carbon::now(); |
| 783 | $month = $now->format('m'); |
| 784 | } catch (\Exception $e) { |
| 785 | Log::channel('g3w_invoices')->error("Formato de fecha de mes no válido (esperado YYYY-mm)"); |
| 786 | return ['success'=> false, 'error'=> 'Formato de fecha de mes no válido (esperado YYYY-mm)']; |
| 787 | } |
| 788 | |
| 789 | $invoices = $this->request('get', 'factura/vencemesadministrador/' . $month, $region, []); |
| 790 | |
| 791 | foreach ($invoices["facturas"] as $invoice) { |
| 792 | $invoiceData = $this->request('get', 'factura/' . $invoice["ID"], $region, []); |
| 793 | $invoiceData = $invoiceData["factura"]; |
| 794 | |
| 795 | // Continue if region is Comunidad Valenciana and the invoice starts with M |
| 796 | if( |
| 797 | $region == "Comunidad Valenciana" |
| 798 | && strpos($invoice["ID"], 'M') === 0 |
| 799 | ){ |
| 800 | continue; |
| 801 | } |
| 802 | |
| 803 | $cobrada = $invoiceData["cobrada"]; |
| 804 | $formaPago = $invoiceData["forma_pago_factura"]; |
| 805 | //Invoice already paied |
| 806 | //Invoice that the payment method is not "transferencia" |
| 807 | if ( |
| 808 | $cobrada !== "NO" |
| 809 | || stripos($formaPago, "tr") === false |
| 810 | ) { |
| 811 | continue; |
| 812 | } |
| 813 | |
| 814 | if ($invoiceData["cod_cliente"]) { |
| 815 | $dataClient = $this->request('get', "cliente/" . $invoiceData["cod_cliente"], $region, []); |
| 816 | $dataClient = $dataClient["cliente"]; |
| 817 | } |
| 818 | |
| 819 | if ( |
| 820 | $dataClient["tipo_cliente"] !== "Administrador" |
| 821 | ){ |
| 822 | continue; |
| 823 | } |
| 824 | |
| 825 | $codService = $invoiceData["lineas"][0]["cod_servicio"]; |
| 826 | $dataService = $this->request('get', "servicio/" . $codService, $region, []); |
| 827 | $dataService = $dataService["servicio"]; |
| 828 | |
| 829 | TblInvoiceAdministrators::create(array( |
| 830 | 'invoice_number' => $invoiceData['n_factura'], |
| 831 | 'region' => $region, |
| 832 | 'name' => $dataClient['empresa'], |
| 833 | 'CIF' => $dataClient["cliente_cif"], |
| 834 | 'email' => $dataClient["email"], |
| 835 | 'service_name' => $dataService["nombre_servicio"], |
| 836 | 'service_addres' => $dataService["direccion"], |
| 837 | 'send_date' => Carbon::now('Europe/Madrid')->toDateString(), |
| 838 | 'expiration_date' => Carbon::parse($invoice["fecha_vencimiento"])->toDateString(), |
| 839 | 'amount' => $invoiceData["importe_total_factura"], |
| 840 | )); |
| 841 | } |
| 842 | |
| 843 | |
| 844 | return [ |
| 845 | 'success' => true, |
| 846 | 'message' => "Proceso completado para el mes $month en la región $region.", |
| 847 | ]; |
| 848 | } |
| 849 | |
| 850 | public function sendAdministratorsInvoices($region){ |
| 851 | if (!$region) { |
| 852 | return ['success' => false, 'error' => 'No region provided']; |
| 853 | } |
| 854 | |
| 855 | $month = [ |
| 856 | 1 => "Enero", |
| 857 | 2 => "Febrero", |
| 858 | 3 => "Marzo", |
| 859 | 4 => "Abril", |
| 860 | 5 => "Mayo", |
| 861 | 6 => "Junio", |
| 862 | 7 => "Julio", |
| 863 | 8 => "Agosto", |
| 864 | 9 => "Septiembre", |
| 865 | 10 => "Octubre", |
| 866 | 11 => "Noviembre", |
| 867 | 12 => "Diciembre", |
| 868 | ]; |
| 869 | |
| 870 | $startOfMonth = Carbon::now()->startOfMonth()->toDateString(); |
| 871 | $endOfMonth = Carbon::now()->endOfMonth()->toDateString(); |
| 872 | |
| 873 | $currentMonthInvoices = TblInvoiceAdministrators::where('region', $region) |
| 874 | ->where('paid', 0) |
| 875 | ->whereBetween('expiration_date', [$startOfMonth, $endOfMonth]) |
| 876 | ->orderBy('expiration_date', 'asc') |
| 877 | ->get(); |
| 878 | |
| 879 | $invoicesGroupedByAdministrator = $currentMonthInvoices->groupBy('CIF'); |
| 880 | |
| 881 | foreach ($invoicesGroupedByAdministrator as $administratorCIF => $administratorInvoices) { |
| 882 | $table = "<table style='border-collapse: collapse; width: 100%; font-family: Arial, sans-serif;'> |
| 883 | <tr> |
| 884 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Número Factura</th> |
| 885 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Nombre cliente de servicio</th> |
| 886 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Dirección del servicio</th> |
| 887 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de emisión</th> |
| 888 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Fecha de vencimiento</th> |
| 889 | <th style='border: 1px solid #999; padding: 8px; background-color: #f2f2f2; text-align: left;'>Importe</th> |
| 890 | </tr>"; |
| 891 | |
| 892 | $documentsBase64 = []; |
| 893 | |
| 894 | foreach ($administratorInvoices as $invoice) { |
| 895 | $invoiceNumber = $invoice->invoice_number; |
| 896 | $table .= "<tr> |
| 897 | <td class='invoice_number' style='border: 1px solid #999; padding: 8px;'>" . $invoiceNumber . "</td> |
| 898 | <td class='invoice_service_name' style='border: 1px solid #999; padding: 8px;'>" . $invoice->service_name . "</td> |
| 899 | <td class='invoice_service_addres' style='border: 1px solid #999; padding: 8px;'>" . $invoice->service_addres . "</td> |
| 900 | <td class='invoice_send_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice->send_date . "</td> |
| 901 | <td class='invoice_expiration_date' style='border: 1px solid #999; padding: 8px;'>" . $invoice->expiration_date . "</td> |
| 902 | <td class='invoice_amount' style='border: 1px solid #999; padding: 8px;'>" . $invoice->amount . "€</td> |
| 903 | </tr>"; |
| 904 | |
| 905 | |
| 906 | $invoice = $this->request('get', 'factura/' . $invoiceNumber, $region, []); |
| 907 | $invoiceData = $invoice["factura"]; |
| 908 | |
| 909 | $documentsBase64[] = [ |
| 910 | 'content' => $invoiceData["documento"], |
| 911 | 'filename' => "Factura_{$invoiceNumber}.pdf" |
| 912 | ]; |
| 913 | } |
| 914 | |
| 915 | $table .= "</table>"; |
| 916 | |
| 917 | $monthText = $month[Carbon::now()->format('n')]; |
| 918 | |
| 919 | $invoiceSendData = $this->sendInvoice( |
| 920 | $administratorInvoices->first()->invoice_number, |
| 921 | $administratorInvoices->first()->name, |
| 922 | $administratorInvoices->first()->email, |
| 923 | $administratorInvoices->first()->send_date, |
| 924 | $administratorInvoices->first()->expiration_date, |
| 925 | $documentsBase64, |
| 926 | $administratorInvoices->first()->amount, |
| 927 | 4, |
| 928 | $region, |
| 929 | $table, |
| 930 | $monthText . " de " . Carbon::now()->format('Y') |
| 931 | ); |
| 932 | |
| 933 | |
| 934 | } |
| 935 | |
| 936 | return ['success' => true, 'message' => 'Processing complete']; |
| 937 | } |
| 938 | |
| 939 | public function sendCallCenterInvoices() |
| 940 | { |
| 941 | $spreadsheetId = "1JxCICtqPtPrE8B_0nVJZYwIZ67Nv5gPYr9rW73Pz1_Q"; |
| 942 | $sheetName = 'Hoja 1'; |
| 943 | |
| 944 | try { |
| 945 | $googleSheetsService = $this->getGoogleSheetsService(); |
| 946 | |
| 947 | $region = [ |
| 948 | "G3W Cat"=>"Cataluña", |
| 949 | "G3W Mad"=>"Madrid", |
| 950 | "G3W Val"=>"Comunidad Valencia", |
| 951 | //"G3W Alm"=>"Andalucia", |
| 952 | ]; |
| 953 | |
| 954 | $fromEmail = [ |
| 955 | "G3W Cat"=>"fire@fire.es", |
| 956 | "G3W Mad"=>"atencion@fire.es", |
| 957 | "G3W Val"=>"facturacionval@fire.es", |
| 958 | //"G3W Alm"=>"documentacion.almeria@fire.es", |
| 959 | ]; |
| 960 | |
| 961 | // A: Entorno, B: ID Factura, C: Email, D: Enviado, E: Fecha EnvÃo |
| 962 | $range = $sheetName . '!A2:E'; |
| 963 | $response = $googleSheetsService->spreadsheets_values->get($spreadsheetId, $range); |
| 964 | $rows = $response->getValues(); |
| 965 | |
| 966 | if (empty($rows)) { |
| 967 | Log::info('No se encontraron datos en el Google Sheet.'); |
| 968 | return; |
| 969 | } |
| 970 | |
| 971 | foreach ($rows as $index => $row) { |
| 972 | $currentRowNumber = $index + 2; |
| 973 | |
| 974 | $entorno = $row[0] ?? null; |
| 975 | $idFactura = $row[1] ?? null; |
| 976 | $emailToSend = $row[2] ?? null; |
| 977 | $enviado = $row[3] ?? 'NO'; |
| 978 | |
| 979 | if ($idFactura && $emailToSend && strtoupper($enviado) !== 'Si') { |
| 980 | |
| 981 | try { |
| 982 | // --- AQUà LLAMAS A TU LÓGICA DE ENVÃO --- |
| 983 | if( |
| 984 | $region[$entorno] == "Comunidad Valenciana" |
| 985 | && strpos($idFactura, 'M') === 0 |
| 986 | ){ |
| 987 | continue; |
| 988 | } |
| 989 | |
| 990 | $invoiceData = $this->request('get', 'factura/' . $idFactura, $region[$entorno], []); |
| 991 | $invoiceData = $invoiceData["factura"]; |
| 992 | |
| 993 | $cobrada = $invoiceData["cobrada"]; |
| 994 | $formaPago = $invoiceData["forma_pago_factura"]; |
| 995 | //Invoice already paied |
| 996 | //Invoice that the payment method is not "transferencia" |
| 997 | if ( |
| 998 | $cobrada !== "NO" |
| 999 | || stripos($formaPago, "tr") === false |
| 1000 | ) { |
| 1001 | continue; |
| 1002 | } |
| 1003 | |
| 1004 | $html = '<!doctypehtml><html lang=es><meta charset=UTF-8><meta content="width=device-width,initial-scale=1"name=viewport><title>Recordatorio de Pago - Vencimiento en 1 semana</title><style>body{font-family:Arial,sans-serif;font-size:11;line-height:1.6;max-width:600px;margin:0 auto;padding:20px}@media only screen and (max-width:600px){body{padding:15px}}</style><p>Estimado cliente,</p><p>Le contactamos desde <strong>Grupo Fire</strong>, empresa responsable del mantenimiento de sus extintores, en relación con las <strong>facturas pendientes de pago</strong> que se detallan en los documentos adjuntos.</p><p>Tras revisar nuestra contabilidad, hemos comprobado que, a dÃa de hoy, los importes correspondientes aún no han sido abonados. Por ello, le agradecerÃamos que nos indicara si existe alguna incidencia o motivo que haya podido impedir el pago (por ejemplo: cambio de cuenta bancaria, domiciliación no autorizada, disconformidad con la factura, etc.).</p><p>En caso de no existir ninguna incidencia, le agradecerÃamos proceder al abono de los importes pendientes a la mayor brevedad. Puede realizar el pago mediante ingreso o transferencia a la cuenta bancaria que figura en las facturas adjuntas.</p><p>Un cordial saludo,<br><img alt=""src="data:image/png;base64,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" style=width:"height: 45px;"></div>'; |
| 1005 | |
| 1006 | $sendgrid = new \SendGrid(env('SENDGRID_API_KEY', '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')); |
| 1007 | |
| 1008 | $successCount = 0; |
| 1009 | $failCount = 0; |
| 1010 | |
| 1011 | $email = new \SendGrid\Mail\Mail(); |
| 1012 | |
| 1013 | //$fromEmail = 'recordatorio.factura@fire.es'; |
| 1014 | $fromName = 'Recordatorio Facturas Grupo Fire'; |
| 1015 | $email->setFrom($fromEmail[$entorno], $fromName); |
| 1016 | $email->setReplyTo($fromEmail[$entorno], 'Recordatorio Facturas Grupo Fire'); |
| 1017 | $email->setSubject("EnvÃo de factura " . $idFactura); |
| 1018 | $email->addContent("text/html", $html); |
| 1019 | $email->addTo($emailToSend); |
| 1020 | |
| 1021 | if (!base64_decode($invoiceData["documento"], true)) { |
| 1022 | throw new \Exception('El documento no es un base64 válido'); |
| 1023 | } |
| 1024 | |
| 1025 | $attachment = new \SendGrid\Mail\Attachment(); |
| 1026 | $attachment->setContent($invoiceData["documento"]); |
| 1027 | $attachment->setType("application/pdf"); |
| 1028 | $attachment->setFilename($idFactura); |
| 1029 | $attachment->setDisposition("attachment"); |
| 1030 | $attachment->setContentId("factura_" . uniqid()); |
| 1031 | $email->addAttachment($attachment); |
| 1032 | |
| 1033 | $response = $sendgrid->send($email); |
| 1034 | |
| 1035 | if ($response->statusCode() != 202) { |
| 1036 | throw new Exception("Envio fallido"); |
| 1037 | } |
| 1038 | |
| 1039 | // --- AQUà LLAMAS A TU LÓGICA DE ENVÃO --- |
| 1040 | |
| 1041 | //$updateRange = $sheetName . '!D' . $currentRowNumber . ':E' . $currentRowNumber; |
| 1042 | $updateData = [[$entorno, $idFactura, $emailToSend, 'Si', date('Y-m-d H:i:s')]]; |
| 1043 | |
| 1044 | $this->writeToGoogleSheet( |
| 1045 | $googleSheetsService, |
| 1046 | $spreadsheetId, |
| 1047 | $sheetName, |
| 1048 | $updateData, |
| 1049 | $currentRowNumber |
| 1050 | ); |
| 1051 | |
| 1052 | } catch (\Exception $e) { |
| 1053 | continue; |
| 1054 | } |
| 1055 | } |
| 1056 | } |
| 1057 | |
| 1058 | return ['success' => true, 'message' => 'Processing complete']; |
| 1059 | |
| 1060 | } catch (\Exception $e) { |
| 1061 | Log::error('Error en sendCallCenterInvoices: ' . $e->getMessage()); |
| 1062 | return response()->json(['success' => false, 'message' => $e->getMessage()]); |
| 1063 | } |
| 1064 | } |
| 1065 | |
| 1066 | |
| 1067 | //Google sheets |
| 1068 | public function addToSheets($codCliente, $invoice, $region, $spreadsheetId = "15Lc9tJnHDOGp33V9RH86mXtIybJBAWWlW4fe7knhDZY"){ |
| 1069 | $sheetName = 'Hoja 1'; |
| 1070 | $googleSheetsService = null; |
| 1071 | $nextRow = 1; |
| 1072 | |
| 1073 | try { |
| 1074 | $googleSheetsService = $this->getGoogleSheetsService(); |
| 1075 | $range = $sheetName . '!A:A'; |
| 1076 | $response = $googleSheetsService->spreadsheets_values->get($spreadsheetId, $range); |
| 1077 | $values = $response->getValues(); |
| 1078 | $nextRow = $values ? count($values) + 1 : 1; |
| 1079 | |
| 1080 | } catch (\Exception $e) { |
| 1081 | if (strpos($e->getMessage(), 'Primera configuración requerida') !== false) { |
| 1082 | return [ |
| 1083 | 'success' => false, |
| 1084 | 'message' => $e->getMessage(), |
| 1085 | 'requires_auth' => true |
| 1086 | ]; |
| 1087 | } |
| 1088 | |
| 1089 | return [ |
| 1090 | 'success' => false, |
| 1091 | 'message' => 'Error de conexión con Google Sheets: ' . $e->getMessage(), |
| 1092 | ]; |
| 1093 | } |
| 1094 | |
| 1095 | if ($googleSheetsService && $nextRow === 1) { |
| 1096 | $this->writeToGoogleSheet($googleSheetsService, $spreadsheetId, $sheetName, [ |
| 1097 | ['ID Cliente', 'Número de Factura', 'Fecha de Compromiso de Pago', 'Fecha de Compromiso de Pago + 10 dÃas', 'Region'] |
| 1098 | ], 1); |
| 1099 | $nextRow = 2; |
| 1100 | } |
| 1101 | |
| 1102 | $fechaCompromiso = date('Y-m-d'); |
| 1103 | $fechaCompromisoMas10 = date('Y-m-d', strtotime('+10 days')); |
| 1104 | |
| 1105 | $data = [ |
| 1106 | $codCliente, |
| 1107 | $invoice, |
| 1108 | $fechaCompromiso, |
| 1109 | $fechaCompromisoMas10, |
| 1110 | $region |
| 1111 | ]; |
| 1112 | |
| 1113 | if ($googleSheetsService) { |
| 1114 | $this->writeToGoogleSheet($googleSheetsService, $spreadsheetId, $sheetName, [$data], $nextRow); |
| 1115 | $nextRow++; |
| 1116 | } |
| 1117 | |
| 1118 | } |
| 1119 | |
| 1120 | private function getGoogleSheetsService() |
| 1121 | { |
| 1122 | |
| 1123 | $redirectUrl = ""; |
| 1124 | if(env("APP_ENV") === "production"){ |
| 1125 | $redirectUrl = "https://fireservicetitan.com/api/google-sheets-callback"; |
| 1126 | } |
| 1127 | |
| 1128 | if(env("APP_ENV") === "staging"){ |
| 1129 | $redirectUrl = "https://stg.fireservicetitan.com/api/google-sheets-callback"; |
| 1130 | } |
| 1131 | |
| 1132 | if(env("APP_ENV") === "local"){ |
| 1133 | $redirectUrl = "http://localhost:8000/api/google-sheets-callback"; |
| 1134 | } |
| 1135 | |
| 1136 | $client = new \Google\Client(); |
| 1137 | $client->setAuthConfig(storage_path('app/credentials.json')); |
| 1138 | $client->addScope(\Google\Service\Sheets::SPREADSHEETS); |
| 1139 | $client->setAccessType('offline'); |
| 1140 | $client->setPrompt('select_account consent'); |
| 1141 | $client->setRedirectUri($redirectUrl); |
| 1142 | |
| 1143 | $tokenPath = storage_path('app/token.json'); |
| 1144 | |
| 1145 | if (file_exists($tokenPath)) { |
| 1146 | $accessToken = json_decode(file_get_contents($tokenPath), true); |
| 1147 | $client->setAccessToken($accessToken); |
| 1148 | } |
| 1149 | |
| 1150 | if ($client->isAccessTokenExpired()) { |
| 1151 | if ($client->getRefreshToken()) { |
| 1152 | $newToken = $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); |
| 1153 | file_put_contents($tokenPath, json_encode($newToken)); |
| 1154 | } else { |
| 1155 | $authUrl = $client->createAuthUrl(); |
| 1156 | throw new \Exception("Initial setup required. Visit this URL to authorize: " . $authUrl); |
| 1157 | } |
| 1158 | } |
| 1159 | |
| 1160 | return new \Google\Service\Sheets($client); |
| 1161 | } |
| 1162 | |
| 1163 | private function writeToGoogleSheet($service, $spreadsheetId, $sheetName, $data, $rowNumber) |
| 1164 | { |
| 1165 | try { |
| 1166 | $range = $sheetName . '!A' . $rowNumber . ':E' . $rowNumber; |
| 1167 | |
| 1168 | $body = new \Google\Service\Sheets\ValueRange([ |
| 1169 | 'values' => $data |
| 1170 | ]); |
| 1171 | |
| 1172 | $params = [ |
| 1173 | 'valueInputOption' => 'USER_ENTERED' |
| 1174 | ]; |
| 1175 | |
| 1176 | $result = $service->spreadsheets_values->update( |
| 1177 | $spreadsheetId, |
| 1178 | $range, |
| 1179 | $body, |
| 1180 | $params |
| 1181 | ); |
| 1182 | |
| 1183 | Log::info('✅ Datos escritos en Google Sheets - Fila: ' . $rowNumber); |
| 1184 | return $result; |
| 1185 | |
| 1186 | } catch (\Exception $e) { |
| 1187 | Log::error('⌠Error escribiendo en Google Sheets: ' . $e->getMessage()); |
| 1188 | throw $e; |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | public function handleGoogleAuthCallback(Request $request) |
| 1193 | { |
| 1194 | try { |
| 1195 | // Verificar que el código existe |
| 1196 | if (!$request->get('code')) { |
| 1197 | Log::error('⌠No code parameter found'); |
| 1198 | return response()->json([ |
| 1199 | 'success' => false, |
| 1200 | 'message' => 'No authorization code provided' |
| 1201 | ], 400); |
| 1202 | } |
| 1203 | |
| 1204 | $redirectUrl = ""; |
| 1205 | |
| 1206 | if(env("APP_ENV") === "production"){ |
| 1207 | $redirectUrl = "https://fireservicetitan.com/api/google-sheets-callback"; |
| 1208 | } |
| 1209 | |
| 1210 | if(env("APP_ENV") === "staging"){ |
| 1211 | $redirectUrl = "https://stg.fireservicetitan.com/api/google-sheets-callback"; |
| 1212 | } |
| 1213 | |
| 1214 | if(env("APP_ENV") === "local"){ |
| 1215 | $redirectUrl = "http://localhost:8000/api/google-sheets-callback"; |
| 1216 | } |
| 1217 | |
| 1218 | $client = new \Google\Client(); |
| 1219 | $client->setAuthConfig(storage_path('app/credentials.json')); |
| 1220 | $client->addScope(\Google\Service\Sheets::SPREADSHEETS); |
| 1221 | $client->setRedirectUri($redirectUrl); |
| 1222 | |
| 1223 | $token = $client->fetchAccessTokenWithAuthCode($request->get('code')); |
| 1224 | |
| 1225 | file_put_contents(storage_path('app/token.json'), json_encode($token)); |
| 1226 | |
| 1227 | return response()->json([ |
| 1228 | 'success' => true, |
| 1229 | 'message' => 'Google Sheets authentication complete! You can now run the invoicing function.' |
| 1230 | ]); |
| 1231 | |
| 1232 | } catch (\Exception $e) { |
| 1233 | Log::error('Error on Google sheets callback: ' . $e->getMessage()); |
| 1234 | Log::error('Error details: ' . $e->getTraceAsString()); |
| 1235 | return response()->json([ |
| 1236 | 'success' => false, |
| 1237 | 'message' => 'Error: ' . $e->getMessage() |
| 1238 | ], 400); |
| 1239 | } |
| 1240 | } |
| 1241 | |
| 1242 | private function getVencimientosFormateados($dataInvoice) |
| 1243 | { |
| 1244 | $vencimientos = $dataInvoice["factura"]["vencimientos"] ?? []; |
| 1245 | |
| 1246 | if (count($vencimientos) === 0) { |
| 1247 | return null; |
| 1248 | } |
| 1249 | |
| 1250 | $fechasFormateadas = array_map(function($vencimiento) { |
| 1251 | return Carbon::createFromFormat('Y-m-d', $vencimiento['fecha_vencimiento']) |
| 1252 | ->isoFormat('D [de] MMMM [de] YYYY'); |
| 1253 | }, $vencimientos); |
| 1254 | |
| 1255 | return implode(' ó ', $fechasFormateadas); |
| 1256 | } |
| 1257 | |
| 1258 | } |