Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 248
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResendEmails
0.00% covered (danger)
0.00%
0 / 248
0.00% covered (danger)
0.00%
0 / 5
3306
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
 handle
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 1
1332
 isEmailValid
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getEmailBody
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 1
132
 calculateEmailRequestSize
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3namespace App\Console\Commands;
4
5use App\Models\TblBlockedDomains;
6use App\Models\TblCcBcc;
7use App\Models\TblCompanyEmails;
8use App\Models\TblEmailConfiguration;
9use App\Models\TblEmailFiles;
10use App\Models\TblFiles;
11use App\Models\TblQuotations;
12use App\Models\TblResendEmails;
13use App\Models\TblSendgridWebhook;
14use Illuminate\Console\Command;
15use Illuminate\Support\Facades\DB;
16use Illuminate\Support\Facades\Log;
17use Illuminate\Support\Facades\Storage;
18use SendGrid\Mail\Mail;
19
20class ResendEmails extends Command
21{
22    /**
23     * The name and signature of the console command.
24     *
25     * @var string
26     */
27    protected $signature = 'command:resend-emails {id?} {type?}';
28
29    private $email;
30
31    /**
32     * The console command description.
33     *
34     * @var string
35     */
36    protected $description = 'Command description';
37
38    /**
39     * Create a new command instance.
40     *
41     * @return void
42     */
43    public function __construct()
44    {
45        parent::__construct();
46    }
47
48    /**
49     * Execute the console command.
50     *
51     * @return int
52     */
53    public function handle()
54    {
55
56        try {
57
58            $this->email = new \SendGrid\Mail\Mail;
59
60            $id = $this->argument('id') ?? null;
61            $typeId = (int) ($this->argument('type') ?? 0);
62
63            $type = 'followUps';
64
65            if ($typeId == 1) {
66                $type = 'sendToClient';
67            }
68
69            if ($id !== null) {
70
71                $result = TblQuotations::where('id', $id)->first();
72
73                $emailTemplate = TblEmailConfiguration::where('type', $type)->where('is_default', 1)->get()->keyBy('company_id');
74
75                if ($result) {
76
77                    $budgetTypeId = $result->budget_type_id;
78                    $quoteId = $result->id;
79                    $commercialUser = $result->commercial;
80                    $companyId = $result->company_id;
81
82                    $emailContent = $this->getEmailBody(
83                        $emailTemplate[$companyId]->getAttribute('html'),
84                        $emailTemplate[$companyId]->getAttribute('subject'),
85                        $emailTemplate[$companyId]->getAttribute('email_template_id'),
86                        $result
87                    );
88
89                    $html = $emailContent[0];
90                    $subject = $emailContent[1];
91
92                    $blockedDomains = TblBlockedDomains::where('company_id', $result->company_id)->pluck('domain')->toArray();
93
94                    if (env('SENDGRID_STAGING')) {
95                        $toEmail = 'christian@ibventur.es';
96                    } else {
97                        $toEmail = $result->email;
98                    }
99
100                    if ($toEmail != null) {
101
102                        $toEmail = explode(',', $toEmail);
103                        $toEmail = array_map('trim', $toEmail);
104
105                        $companyEmail = null;
106
107                        $queryUsers = "SELECT 
108                                            sender_email AS from_email, 
109                                            `name` AS from_name 
110                                        FROM tbl_users
111                                        WHERE 
112                                            sender_enabled = 1 
113                                        AND response_id IS NOT NULL 
114                                        AND verified = 1 
115                                        AND `name` = '{$commercialUser}'";
116
117                        $commercialEmail = DB::select($queryUsers);
118
119                        if (count($commercialEmail) > 0) {
120                            $companyEmail = $commercialEmail[0];
121                        } else {
122                            if ($emailTemplate[$companyId]->getAttribute('from_id') != null) {
123                                $companyEmail = TblCompanyEmails::where('id', $emailTemplate[$companyId]->getAttribute('from_id'))->first();
124                            } else {
125                                $companyEmail = TblCompanyEmails::where('is_active', 1)->where('verified', 1)->where('company_id', $result->company_id)->first();
126                            }
127                        }
128
129                        if (! $companyEmail) {
130                            $error = __('language.no_active_verified_sender');
131                            Log::channel('resend_emails')->error("ID:{$quoteId}{$error}");
132                        }
133
134                        $ccBcc = TblCcBcc::where('company_id', $result->company_id)->get();
135
136                        $this->email->setFrom($companyEmail->from_email, $companyEmail->from_name);
137                        $this->email->setSubject($subject);
138
139                        $s = 0;
140                        $addTo = [];
141                        foreach ($toEmail as $clientEmail) {
142                            $isValid = $this->isEmailValid($clientEmail);
143
144                            if ($isValid) {
145                                $domain = substr($clientEmail, strpos($clientEmail, '@') + 1);
146
147                                if (! in_array($domain, $blockedDomains)) {
148                                    $this->email->addTo($clientEmail);
149                                    array_push($addTo, $clientEmail);
150                                    $s++;
151                                }
152                            } else {
153                                Log::channel('resend_emails')->error("ID:{$quoteId}: Invalid email {$clientEmail}");
154                            }
155                        }
156
157                        if ($s == 0) {
158                            $toEmail = json_encode($toEmail);
159                            Log::channel('resend_emails')->error("[NO-VALID-EMAILS:{$s}] ID:{$quoteId}{$toEmail}}");
160                        }
161
162                        if (env('SENDGRID_STAGING')) {
163
164                        } else {
165                            if (count($ccBcc) > 0) {
166                                foreach ($ccBcc as $data) {
167                                    if (! in_array($data->email, $toEmail)) {
168                                        $this->email->addBcc($data->email);
169                                    }
170                                }
171                            }
172                        }
173
174                        $this->email->addContent('text/html', $html);
175
176                        $sendgrid = new \SendGrid(env('SENDGRID_API_KEY'));
177
178                        $files = TblFiles::where('quotation_id', $result->id)->where('is_internal', null)->get();
179                        $requestSize = $this->calculateEmailRequestSize($this->email);
180
181                        foreach ($files as $key => $value) {
182                            if ($files[$key]->filename && Storage::disk('s3')->exists('uploads/'.$files[$key]->filename)) {
183
184                                $fileContent = Storage::disk('s3')->get('uploads/'.$files[$key]->filename);
185                                $fileSize = strlen($fileContent) / 1048576;
186                                $fileSize = (int) ceil($fileSize);
187
188                                $originalName = $files[$key]->original_name ?: basename($files[$key]->filename);
189                            } elseif ($files[$key]->getAttribute('file')) {
190                                $fileContent = $files[$key]->getAttribute('file');
191
192                                if (is_string($fileContent) && base64_decode($fileContent, true)) {
193                                    $fileContent = base64_decode($fileContent);
194                                }
195
196                                $fileSize = strlen($fileContent) / 1048576;
197                                $fileSize = (int) ceil($fileSize);
198
199                                $originalName = $files[$key]->original_name ?: ($files[$key]->getAttribute('file_name') ?: 'file');
200                            } else {
201                                continue;
202                            }
203
204                            if ($fileSize > 0) {
205                                if ($requestSize + $fileSize < 25) {
206                                    $attachment = new \SendGrid\Mail\Attachment;
207                                    $attachment->setFilename($originalName);
208                                    $attachment->setDisposition('attachment');
209
210                                    $attachment->setContent(base64_encode($fileContent));
211
212                                    if ($files[$key]->mime_type) {
213                                        $attachment->setType($files[$key]->mime_type);
214                                    }
215
216                                    $this->email->addAttachment($attachment);
217                                    $requestSize = $this->calculateEmailRequestSize($this->email);
218                                } else {
219                                    Log::channel('resend_emails')->error('File omitted due to size limit: '.$originalName);
220                                }
221                            }
222                        }
223
224                        $sentWithBackup = false;
225                        $response = $sendgrid->send($this->email);
226
227                        if ($response->statusCode() == 202) {
228
229                            $messageId = null;
230
231                            foreach ($response->headers() as $header) {
232                                if (strpos(strtolower($header), 'x-message-id:') === 0) {
233                                    $messageId = trim(substr($header, strpos($header, ':') + 1));
234                                    break;
235                                }
236                            }
237
238                            $typeArray = [
239                                'y_message_id' => $messageId,
240                                'y_status' => 'Processing',
241                            ];
242
243                            if ($type == 'sendToClient') {
244                                $typeArray = [
245                                    'x_message_id' => $messageId,
246                                    'x_status' => 'Processing',
247                                ];
248                            }
249
250                            TblQuotations::where('id', $result->id)->update($typeArray);
251
252                            $jsonBody = [];
253
254                            foreach ($toEmail as $clientEmail) {
255                                $isValid = $this->isEmailValid($clientEmail);
256                                $eventStatus = 'processed';
257                                $eventResponse = '';
258                                if (! $isValid) {
259                                    $eventStatus = 'invalid';
260                                    $eventResponse = 'Invalid email address';
261                                }
262
263                                array_push(
264                                    $jsonBody,
265                                    [
266                                        'email' => $clientEmail,
267                                        'event' => $eventStatus,
268                                        'sg_message_id' => $messageId,
269                                        'smtp-id' => $messageId,
270                                        'timestamp' => strtotime(date('Y-m-d H:i:s')),
271                                        'response' => $eventResponse,
272                                    ]
273                                );
274                            }
275
276                            TblSendgridWebhook::create(
277                                [
278                                    'quotation_id' => $result->id,
279                                    'type' => $type,
280                                    'json_body' => json_encode($jsonBody),
281                                    'x_message_id' => $messageId,
282                                ]
283                            );
284
285                            TblResendEmails::where('quotation_id', $quoteId)->where('type', $typeId)->delete();
286
287                            $toEmail = json_encode($toEmail);
288                            Log::channel('resend_emails')->error("[SENT-OK] ID:{$quoteId}{$toEmail}}");
289                        } else {
290                            Log::channel('resend_emails')->error('[ERROR-SG] ID:'.$result->id.' - '.$response->body());
291                        }
292                    }
293                } else {
294                    Log::channel('resend_emails')->error('[ERROR-ID] ID does not exist');
295                }
296            } else {
297                Log::channel('resend_emails')->error('[ERROR-ID] Missing ID');
298            }
299
300        } catch (\Exception $e) {
301            Log::channel('resend_emails')->error($e);
302        }
303
304        return 0;
305    }
306
307    public function isEmailValid($email)
308    {
309        // Regular expression pattern for email validation
310        $pattern = '/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/';
311
312        // Check if the email matches the pattern
313        if (preg_match($pattern, $email)) {
314            return true; // Valid email
315        } else {
316            return false; // Invalid email
317        }
318    }
319
320    public function getEmailBody($body, $subject, $emailTemplateId, $result)
321    {
322
323        $availableParameters = [
324            'quote_id',
325            'company_id',
326            'client',
327            'client_type',
328            'phone_number',
329            'email',
330            'issue_date',
331            'request_date',
332            'duration',
333            'invoice_number',
334            'type',
335            'acceptance_date',
336            'status',
337            'source',
338            'amount',
339            'reason_for_not_following_up',
340            'last_follow_up_date',
341            'last_follow_up_comment',
342            'reason_for_rejection_id',
343            'reason_for_rejection',
344            'commercial',
345            'created_at',
346            'created_by',
347            'updated_at',
348            'updated_by',
349        ];
350
351        $dateParameters = [
352            'issue_date',
353            'request_date',
354            'acceptance_date',
355            'last_follow_up_date',
356            'created_at',
357            'updated_at',
358        ];
359
360        preg_match_all('/{{(.*?)}}/', $body, $matches);
361
362        $parameters = $matches[1];
363
364        foreach ($parameters as $parameter) {
365
366            if (in_array($parameter, $dateParameters)) {
367                if ($result->{$parameter}) {
368                    $result->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime('%A, %B %d, %Y', strtotime($result->{$parameter})));
369                }
370            }
371
372            if (in_array($parameter, $availableParameters)) {
373                $body = str_replace('{{'.$parameter.'}}', $result->{$parameter}, $body);
374            }
375        }
376
377        preg_match_all('/{{(.*?)}}/', $subject, $matches);
378
379        $parameters = $matches[1];
380
381        foreach ($parameters as $parameter) {
382
383            if (in_array($parameter, $dateParameters)) {
384                if ($result->{$parameter}) {
385                    $result->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime('%A, %B %d, %Y', strtotime($result->{$parameter})));
386                }
387            }
388
389            if (in_array($parameter, $availableParameters)) {
390                $subject = str_replace('{{'.$parameter.'}}', $result->{$parameter}, $subject);
391            }
392        }
393
394        $templateFiles = TblEmailFiles::where('email_template_id', $emailTemplateId)->orderBy('order', 'asc')->get();
395
396        foreach ($templateFiles as $item) {
397            $f = storage_path('app/public/uploads/'.$item->filename);
398
399            if (file_exists($f)) {
400                $imgpath = file_get_contents($f);
401                $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
402                $mimeType = mime_content_type($f);
403
404                $this->email->addAttachment(
405                    $imgpath,
406                    $mimeType,
407                    str_replace(' ', '', $item->original_name),
408                    'inline',
409                    str_replace(' ', '', $item->original_name),
410                );
411
412                $body .= "<img src='cid:{$item->original_name}' style='height: 45px; padding-right: 6px' />";
413            } else {
414                Log::channel('resend_emails')->error("[{$result->id}] File not found: ".$f);
415            }
416        }
417
418        $html = '<!DOCTYPE html>';
419        $html .= '<html>';
420        $html .= '<head>';
421        $html .= '<meta charset="UTF-8">';
422        $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
423        $html .= '</head>';
424        $html .= '<body>';
425        $html .= $body;
426        $html .= '</body>';
427        $html .= '</html>';
428
429        return [$html, $subject];
430    }
431
432    public function calculateEmailRequestSize(Mail $email)
433    {
434
435        $size = 0;
436
437        // Add size of 'from', 'to', 'subject', 'content'
438        $from = $email->getFrom();
439        $size += strlen(json_encode([
440            'from' => $from->getEmail().' '.$from->getName(),
441            'subject' => $email->getSubject(),
442        ]));
443
444        // Add size of 'to' (recipients)
445        $personalizations = $email->getPersonalizations() ?? [];
446        foreach ($personalizations as $personalization) {
447            foreach ($personalization->getTos() as $to) {
448                $size += strlen($to->getEmail().' '.$to->getName());
449            }
450        }
451
452        // Add size of content
453        foreach ($email->getContents() as $content) {
454            $size += strlen($content->getValue());
455        }
456
457        // Add size of attachments (if any)
458
459        if ($email->getAttachments() != null && $email->getAttachments() != '') {
460            foreach ($email->getAttachments() as $attachment) {
461                $size += strlen($attachment->getContent()); // Base64 encoded size
462                $size += strlen($attachment->getFilename());
463                $size += strlen($attachment->getType());
464            }
465        }
466
467        $sizeInMegabytes = $size / 1048576; // 1 MB = 1,048,576 bytes
468
469        return (int) ceil($sizeInMegabytes);
470    }
471}