Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.13% covered (danger)
0.13%
8 / 5968
0.00% covered (danger)
0.00%
0 / 92
CRAP
0.00% covered (danger)
0.00%
0 / 1
Quotations
0.13% covered (danger)
0.13%
8 / 5968
0.00% covered (danger)
0.00%
0 / 92
2242463.87
0.00% covered (danger)
0.00%
0 / 1
 __construct
61.54% covered (warning)
61.54%
8 / 13
0.00% covered (danger)
0.00%
0 / 1
4.91
 create_quotation
0.00% covered (danger)
0.00%
0 / 81
0.00% covered (danger)
0.00%
0 / 1
210
 currency
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 send_approval_notification
0.00% covered (danger)
0.00%
0 / 121
0.00% covered (danger)
0.00%
0 / 1
552
 send_approval_margin_notification
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 1
272
 approve_quotation
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
240
 send_approved_notification
0.00% covered (danger)
0.00%
0 / 98
0.00% covered (danger)
0.00%
0 / 1
110
 reject_quotation
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
240
 send_rejected_notification
0.00% covered (danger)
0.00%
0 / 98
0.00% covered (danger)
0.00%
0 / 1
110
 update_quotation
0.00% covered (danger)
0.00%
0 / 468
0.00% covered (danger)
0.00%
0 / 1
23256
 compareArrays
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 isEmpty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
20
 convertValue
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
56
 callDeleteQuotation
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 get_quotation
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 get_quotation_log
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 send_notification
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 1
42
 delete_quotation
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
90
 getBlacklistEmails
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 validate_email
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
42
 isBlacklistedEmail
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 list_quotations
0.00% covered (danger)
0.00%
0 / 394
0.00% covered (danger)
0.00%
0 / 1
18090
 get_dates
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 list_quotation_analytics_by_source
0.00% covered (danger)
0.00%
0 / 120
0.00% covered (danger)
0.00%
0 / 1
1722
 list_quotation_analytics_send_budgets
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
380
 list_quotation_analytics_track_budgets
0.00% covered (danger)
0.00%
0 / 119
0.00% covered (danger)
0.00%
0 / 1
1332
 list_quotation_analytics_types_budgets
0.00% covered (danger)
0.00%
0 / 121
0.00% covered (danger)
0.00%
0 / 1
1056
 download_quotations
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 download_quotations_csv
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 bulk_upload
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
30
 list_bulk_upload
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 delete_number
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 get_number
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
132
 get_years
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
12
 human_filesize
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 get_files
0.00% covered (danger)
0.00%
0 / 130
0.00% covered (danger)
0.00%
0 / 1
1892
 download_file
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
56
 delete_file
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 send_email_to_client
0.00% covered (danger)
0.00%
0 / 322
0.00% covered (danger)
0.00%
0 / 1
7140
 send_email_follow_ups
0.00% covered (danger)
0.00%
0 / 422
0.00% covered (danger)
0.00%
0 / 1
9506
 create_sender_identity
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
56
 get_sender_identity
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 get_all_sender_identity
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 delete_sender_identity
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 create_template
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
72
 get_email_files
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 download_email_template_file
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 delete_email_template_file
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 update_email_template_order
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 update_email_template
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
90
 delete_template
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 get_email_template
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 update_sender_identity
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
20
 resend_verification
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 list_quotation_analytics_by_performance
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
30
 list_orders_update_logs
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 list_g3w_orders_update_logs
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 list_g3w_orders_failed
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
 update_budget_status_rejected_manual
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 update_budget_status_rejected
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
110
 bulk_update_quotation
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
30
 move_budget_and_job
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
30
 list_quotation_analytics_by_types_of_budgets_created_per_week
0.00% covered (danger)
0.00%
0 / 193
0.00% covered (danger)
0.00%
0 / 1
3782
 preview_file
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
20
 get_past_added_quotation
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
30
 send_acceptance_notification
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
156
 get_total_quotations_by_budget_status
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 1
90
 sendgrid_webhook_receiver
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
42
 isEmailValid
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 list_email_status
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 list_quotation_analytics_commercial
0.00% covered (danger)
0.00%
0 / 186
0.00% covered (danger)
0.00%
0 / 1
2970
 clear_open_data
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
30
 list_quotation_analytics_order_size
0.00% covered (danger)
0.00%
0 / 194
0.00% covered (danger)
0.00%
0 / 1
4422
 send_email_template_preview
0.00% covered (danger)
0.00%
0 / 105
0.00% covered (danger)
0.00%
0 / 1
272
 list_quotation_analytics_by_types_of_budgets_company_per_week
0.00% covered (danger)
0.00%
0 / 190
0.00% covered (danger)
0.00%
0 / 1
3782
 request_permission_commercial
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 1
72
 confirm_update_commercial
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 calculateEmailRequestSize
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
 list_quotation_analytics_commercial_productivity
0.00% covered (danger)
0.00%
0 / 234
0.00% covered (danger)
0.00%
0 / 1
4422
 list_quotations_deleted
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 delete_sengrid
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 download_productivity_commercial
0.00% covered (danger)
0.00%
0 / 378
0.00% covered (danger)
0.00%
0 / 1
2450
 update_commercial_numbers
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 list_quotation_analytics_by_service_type
0.00% covered (danger)
0.00%
0 / 156
0.00% covered (danger)
0.00%
0 / 1
1260
 getIdsFromInternalQuoteIds
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 checkQuotationExistByInternalQuoteId
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
72
 addUpdateLog
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 1
2070
 setSolicitudDuplicity
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
20
 getQuoteIdOfDuplicityById
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 download_s3_files
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
72
 getQuotationStatusByInternalId
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 1
110
 findQuotationByInternalId
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\StructureData;
6use App\Models\TblBlockedDomains;
7use App\Models\TblBudgetStatus;
8use App\Models\TblBudgetTypeGroups;
9use App\Models\TblBudgetTypes;
10use App\Models\TblBulkUpload;
11use App\Models\TblBusinessGoals;
12use App\Models\TblCcAcceptanceNotifications;
13use App\Models\TblCcBcc;
14use App\Models\TblCompanies;
15use App\Models\TblCompanyEmails;
16use App\Models\TblCompanyUsers;
17use App\Models\TblCustomerTypes;
18use App\Models\TblEmailConfiguration;
19use App\Models\TblEmailFiles;
20use App\Models\TblFiles;
21use App\Models\TblFollowUpLogs;
22use App\Models\TblG3WOrdersUpdateLogs;
23use App\Models\TblLastFollowUpDate;
24use App\Models\TblNotifications;
25use App\Models\Client;
26use App\Models\TblOngoingJobs;
27use App\Models\TblOrdersUpdateLogs;
28use App\Models\TblProjectTypes;
29use App\Models\TblQuotations;
30use App\Models\TblQuotationsLog;
31use App\Models\TblSegments;
32use App\Models\TblSendgridWebhook;
33use App\Models\TblSources;
34use App\Models\TblToAcceptanceNotifications;
35use App\Models\TblUsers;
36use App\Models\TblVisitTypeGroups;
37use App\Models\TblWorkflowQuestions;
38use Illuminate\Http\Request;
39use Illuminate\Support\Facades\App;
40use Illuminate\Support\Facades\Cache;
41use Illuminate\Support\Facades\DB;
42use Illuminate\Support\Facades\File;
43use Illuminate\Support\Facades\Log;
44use Illuminate\Support\Facades\Response;
45use Illuminate\Support\Facades\Storage;
46use PhpOffice\PhpSpreadsheet\IOFactory;
47use PhpOffice\PhpSpreadsheet\Spreadsheet;
48use SendGrid\Mail\Mail;
49use ZipArchive;
50use Illuminate\Contracts\Routing\ResponseFactory;
51use Illuminate\Http\Response as HttpResponse;
52use function PHPUnit\Framework\isEmpty;
53use function PHPUnit\Framework\isNull;
54use App\Exceptions\AppException;
55
56class Quotations extends Controller
57{
58    private $locale;
59
60    private $userId;
61
62    private $region;
63
64    private $companyIds;
65    private readonly string $companyId;
66
67    public function __construct(){
68        $this->locale = request()->header('Locale-Id');
69        $this->userId = request()->header('User-Id');
70        $this->region = request()->header('Region');
71
72        App::setLocale($this->locale);
73
74        $this->companyIds = [];
75
76        if($this->region != null && $this->region != "" && $this->region != "All"){
77            $this->region = urldecode((string) $this->region);
78
79            $query = 'SELECT
80                        b.company_id
81                    FROM
82                        tbl_company_users a
83                        LEFT JOIN tbl_companies b ON a.company_id = b.company_id
84                    WHERE
85                        a.user_id = ?
86                        AND b.region = ?';
87
88            $this->companyIds = DB::select($query, [intval($this->userId), $this->region]);
89
90            $this->companyIds = collect($this->companyIds)->pluck('company_id')->toArray();
91        } else {
92            $this->companyIds = TblCompanyUsers::where('user_id', $this->userId)->pluck('company_id')->all();
93        }
94
95        $this->companyId = implode(',', $this->companyIds);
96    }
97
98    public function create_quotation(Request $request): ResponseFactory|HttpResponse{
99
100        try {
101
102            $data = $request->all();
103            $data['updated_at'] = date('Y-m-d H:i:s');
104
105            if (isset($data['request_date']) && isset($data['issue_date'])) {
106                $requestDate = strtotime($data['request_date']);
107                $issueDate = strtotime($data['issue_date']);
108                $dateDiff = $issueDate - $requestDate;
109                $data['duration'] = round($dateDiff / (60 * 60 * 24));
110            }
111
112            $r = new Request([
113                'created_by' => $data['created_by'],
114            ]);
115
116            $result = $this->get_number($r, @$data['company_id']);
117            $id = $result->original['id'];
118
119            $files = $request->file('files');
120            if ($files) {
121                $uploadedFiles = [];
122
123                foreach ($files as $file) {
124                    $filename = time().'_'.$file->getClientOriginalName();
125                    $fileSize = $file->getSize();
126                    // $fileContent = file_get_contents($file->getRealPath());
127                    // $fileHash = hash('sha256', $fileContent);
128
129                    $s3path = Storage::disk('s3')->putFileAs(
130                        'uploads',
131                        $file,
132                        $filename,
133                        [
134                            'ContentType' => $file->getMimeType(),
135                        ]
136                    );
137
138                    TblFiles::create(
139                        [
140                            'quotation_id' => $id,
141                            'original_name' => $file->getClientOriginalName(),
142                            'filename' => $filename,
143                            'uploaded_by' => $data['updated_by'],
144                            // 'file' => $fileContent,
145                            'file_size' => $file->getSize(),
146                            // 'file_hash' => $fileHash,
147                            'mime_type' => $file->getMimeType(),
148                            'uploaded_at' => now(),
149                        ]
150                    );
151
152                    // $this->addUpdateLog($id, $data['updated_by'], "upload_attachment", null, $filename, 4);
153                }
154            }
155
156            $query = 'SELECT COUNT(*) as count FROM tbl_files WHERE quotation_id = ?';
157            $fileCount = DB::select($query, [$id])[0]->count;
158
159            $data['has_attachment'] = $fileCount > 0 ? 1 : 0;
160
161            $data = array_diff_key($data, array_flip(['files', '_token', 'otros_campos_no_necesarios']));
162
163            $data['for_add'] = 0;
164
165            // If email is blacklisted and status is sendable, set to 22 (Correo erroneo)
166            if (isset($data['email']) && $this->isBlacklistedEmail($data['email'])
167                && isset($data['budget_status_id']) && in_array($data['budget_status_id'], [1, 2, 11, 17])) {
168                $data['budget_status_id'] = 22;
169            }
170
171            TblQuotations::where('id', $id)->update($data);
172
173            $result = TblQuotations::where('id', $id)->first();
174
175            if ($result->budget_status_id == 6) {
176                $data = [
177                    'id' => $result->id ?? null,
178                    'client' => $result->client ?? null,
179                    'email' => $result->email ?? null,
180                    'phone_number' => $result->phone_number ?? null,
181                    'last_follow_up_comment' => $result->last_follow_up_comment ?? null,
182                    'quote_id' => $result->quote_id ?? null,
183                    'request_date' => date('Y-m-d'),
184                    'updated_by' => 'IA',
185                    'user_id' => $result->getAttribute('user_id') ?? null,
186                    'commercial' => $result->commercial ?? null,
187                    'budget_status_id' => $result->budget_status_id ?? null,
188                    'internal_quote_id' => $result->internal_quote_id ?? null,
189                ];
190
191                $ch = curl_init('https://2lsarnb35o6evhgwmfedrsxk3i0lqzzq.lambda-url.eu-west-2.on.aws/checkduplicate');
192                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
193                curl_setopt($ch, CURLOPT_POST, true);
194                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
195                curl_setopt($ch, CURLOPT_HTTPHEADER, [
196                    'Content-Type: application/json',
197                ]);
198
199                $response = curl_exec($ch);
200                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
201
202                if (curl_errno($ch)) {
203                    error_log('Error en cURL: '.curl_error($ch));
204                }
205
206                curl_close($ch);
207            }
208
209            $logCategory = $result->budget_status_id == 6 ? 0 : 2;
210            $this->addUpdateLog($result->id, $result->getAttribute('user_id'), 'create_quotation', null, null, $logCategory);
211            Cache::flush();
212
213            return response(['message' => 'OK', 'data' => $result]);
214
215        } catch (\Exception $e) {
216            report(AppException::fromException($e, 'CREATE_QUOTATION_EXCEPTION'));
217            return response(['message' => 'KO', 'error' => $e->getMessage()]);
218        }
219
220    }
221
222    public function currency($amount, $withEuro = '')
223    {
224
225        if ($withEuro != null) {
226            $withEuro = ' €';
227        }
228
229        return number_format($amount, 2, ',', '.').$withEuro;
230    }
231
232    function send_approval_notification($amount, $budgetTypeId, $customerTypeId, $minimumOrderSize, string $quoteId, $id, $companyName, $createdBy, $userId, $action, $commercialEmail, $companyId, $endpoint, $isQuestion, $questionIdsNo, $n, $locale = null): void{
233
234        if(!is_null($locale)){
235            $this->locale = $locale;
236        }
237
238        if ($action != 1) {
239            if ($this->locale == 'es') {
240                $action = 'actualizado';
241            } else {
242                $action = 'updated';
243            }
244
245        } else {
246            if ($this->locale == 'es') {
247                $action = 'creado';
248            } else {
249                $action = 'created';
250            }
251        }
252
253        $fendpoint = '';
254
255        if ($endpoint == 'orders') {
256            if ($this->locale == 'es') {
257                $fendpoint = 'presupuesto';
258            } else {
259                $fendpoint = 'budget';
260            }
261
262        } else {
263            if ($this->locale == 'es') {
264                $fendpoint = 'trabajo';
265            } else {
266                $fendpoint = 'job';
267            }
268        }
269
270        $user = TblUsers::where('id', $userId)->first();
271
272        $query = "SELECT
273                    a.approver_id,
274                    a.user_id,
275                    b.name,
276                    b.email
277                FROM
278                    tbl_approvers a
279                    INNER JOIN tbl_users b ON a.user_id = b.id
280                WHERE a.company_id = {$companyId}
281                ";
282
283        if ($n == 3) {
284            $query = "SELECT
285                        a.approver_id,
286                        a.user_id,
287                        u.name,
288                        u.email
289                    FROM tbl_approvers a
290                    INNER JOIN tbl_users u ON a.user_id = u.id
291                    WHERE a.company_id = {$companyId}
292
293                    UNION ALL
294
295                    SELECT
296                        c.approver_id,
297                        c.user_id,
298                        u2.name,
299                        u2.email
300                    FROM tbl_approvers_v2 c
301                    INNER JOIN tbl_users u2 ON c.user_id = u2.id
302                    WHERE c.company_id = {$companyId}";
303        }
304
305        $approvers = DB::select($query);
306
307        if (count($approvers) > 0) {
308            $amount = $this->currency($amount, 1);
309            $minimumOrderSize = $this->currency($minimumOrderSize, 1);
310
311            $budgetType = TblBudgetTypes::where('budget_type_id', $budgetTypeId)->first();
312            $clientType = TblCustomerTypes::where('customer_type_id', $customerTypeId)->first();
313
314            $imgpath = File::get('fireservicetitan.png');
315            $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
316
317            $subject = __('language.send_approval_notification.subject');
318            $subject = str_replace('{{type}}', $budgetType->name, $subject);
319            $subject = str_replace('{{amount}}', $amount, $subject);
320            $subject = str_replace('{{endpoint}}', ucfirst($fendpoint), $subject);
321
322            $url = config('app.frontend_url') . "{$endpoint}/{$id}?company_id={$companyId}";
323            $href = "<a href='{$url}'>{$quoteId}</a>";
324            $cc = false;
325            foreach ($approvers as $item) {
326
327                $toEmail = $item->email;
328
329                $body = __('language.send_approval_notification.body_hello');
330                $body = str_replace('{{approver}}', $item->name, $body);
331
332                $body .= __('language.send_approval_notification.body_message');
333                $body = str_replace('{{endpoint}}', $fendpoint, $body);
334                $body = str_replace('{{creator}}', $createdBy, $body);
335                $body = str_replace('{{action}}', $action, $body);
336                $body = str_replace('{{company}}', $companyName, $body);
337                $body = str_replace('{{type}}', $budgetType->name, $body);
338                $body = str_replace('{{amount}}', $amount, $body);
339                $body = str_replace('{{quote_id}}', $href, $body);
340
341                if ($isQuestion == 1) {
342                    $body .= __('language.send_approval_notification.note_question');
343                } else {
344                    $body .= __('language.send_approval_notification.note');
345                }
346
347                $body = str_replace('{{company}}', $companyName, $body);
348                $body = str_replace('{{client_type}}', $clientType->name, $body);
349                $body = str_replace('{{project_type}}', $budgetType->name, $body);
350                $body = str_replace('{{amount}}', $minimumOrderSize, $body);
351
352                if ($isQuestion == 1) {
353                    if ($questionIdsNo) {
354                        $questions = TblWorkflowQuestions::whereIn('question_id', $questionIdsNo)->where('company_id', $companyId)->get();
355
356                        if ($questions->isNotEmpty()) {
357                            $ul = '<ul>';
358                            $li = '';
359                            foreach ($questions as $item) {
360                                $li .= "<li>{$item->question}</li>";
361                            }
362                            $ul .= $li.'</ul><br><br>';
363                            $body .= $ul;
364                        }
365                    }
366                }
367
368                $content = $body;
369
370                $body .= '<p>Fire Service Titan</p>';
371                $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
372
373                $html = '<!DOCTYPE html>';
374                $html .= '<html>';
375                $html .= '<head>';
376                $html .= '<meta charset="UTF-8">';
377                $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
378                $html .= '</head>';
379                $html .= '<body>';
380                $html .= $body;
381                $html .= '</body>';
382                $html .= '</html>';
383
384                if ($toEmail != null) {
385                    $email = new \SendGrid\Mail\Mail;
386
387                    if(config('services.sendgrid.staging')){
388                        $toEmail = $user->email;
389                        $item->user_id = $userId;
390                    }
391
392                    if ($cc == false) {
393                        $cc = true;
394                        if ($user->email != $toEmail) {
395                            if ($user->email != $commercialEmail && $commercialEmail != null) {
396                                $email->addBcc($user->email);
397                                $email->addBcc($commercialEmail);
398                            } else {
399                                $email->addBcc($user->email);
400                            }
401                        }
402                    }
403
404                    $email->setFrom('fire@fire.es', 'Fire Service Titan');
405                    $email->setSubject($subject);
406                    $email->addTo($toEmail);
407                    $email->addContent('text/html', $html);
408
409                    $email->addAttachment(
410                        $imgpath,
411                        'image/png',
412                        'fireservicetitan.png',
413                        'inline',
414                        'fireservicetitan'
415                    );
416
417                    $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
418
419                    $response = $sendgrid->send($email);
420                    if ($response->statusCode() == 202) {
421                        Log::channel('email_log')->info('ID:'.$quoteId.' : '.$toEmail.' - APPROVAL EMAIL NOTIFICATION SENT');
422
423                        $this->addUpdateLog($id, $userId, 'send_approval_notification', null, null, 5);
424
425                        TblNotifications::create(
426                            [
427                                'user_id' => $item->user_id,
428                                'content' => $content,
429                                'is_open' => 1,
430                                'created_by' => 'System',
431                                'link' => $url
432                            ]
433                        );
434                    } else {
435                        $error = true;
436                        Log::channel('email_log')->error('ID:'.$quoteId.' : '.$toEmail.' - '.$response->body());
437                    }
438                }
439
440            }
441        }
442    }
443
444    function send_approval_margin_notification($amount, $budgetTypeId, $customerTypeId, $minimumMargin, string $quoteId, $id, $companyName, $createdBy, $userId, $action, $commercialEmail, $invoiceMargin, $companyId, $endpoint): void{
445
446        if ($action != 1) {
447            if ($this->locale == 'es') {
448                $action = 'actualizado';
449            } else {
450                $action = 'updated';
451            }
452
453        } else {
454            if ($this->locale == 'es') {
455                $action = 'creado';
456            } else {
457                $action = 'created';
458            }
459        }
460
461        $fendpoint = '';
462
463        if ($endpoint == 'orders') {
464            if ($this->locale == 'es') {
465                $fendpoint = 'presupuesto';
466            } else {
467                $fendpoint = 'budget';
468            }
469
470        } else {
471            if ($this->locale == 'es') {
472                $fendpoint = 'trabajo';
473            } else {
474                $fendpoint = 'job';
475            }
476        }
477
478        $invoiceMargin = number_format($invoiceMargin, 2);
479        $minimumMargin = number_format($minimumMargin, 2);
480
481        $user = TblUsers::where('id', $userId)->first();
482
483        $query = "SELECT
484                    a.approver_id,
485                    a.user_id,
486                    b.name,
487                    b.email
488                FROM
489                    tbl_approvers a
490                    INNER JOIN tbl_users b ON a.user_id = b.id
491                WHERE a.company_id = {$companyId}
492                ";
493
494        $approvers = DB::select($query);
495
496        if (count($approvers) > 0) {
497
498            $amount = $this->currency($amount, 1);
499
500            $budgetType = TblBudgetTypes::where('budget_type_id', $budgetTypeId)->first();
501            $clientType = TblCustomerTypes::where('customer_type_id', $customerTypeId)->first();
502
503            $imgpath = File::get('fireservicetitan.png');
504            $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
505
506            $subject = __('language.send_approval_margin_notification.subject');
507            $subject = str_replace('{{type}}', $budgetType->name, $subject);
508            $subject = str_replace('{{amount}}', $amount, $subject);
509            $subject = str_replace('{{margin}}', $invoiceMargin, $subject);
510            $subject = str_replace('{{endpoint}}', ucfirst($fendpoint), $subject);
511
512            $url = config('app.frontend_url') . "{$endpoint}/{$id}?company_id={$companyId}";
513            $href = "<a href='{$url}'>{$quoteId}</a>";
514            $cc = false;
515            foreach ($approvers as $item) {
516
517                $toEmail = $item->email;
518
519                $body = __('language.send_approval_margin_notification.body_hello');
520                $body = str_replace('{{approver}}', $item->name, $body);
521
522                $body .= __('language.send_approval_margin_notification.body_message');
523                $body = str_replace('{{endpoint}}', $fendpoint, $body);
524                $body = str_replace('{{creator}}', $createdBy, $body);
525                $body = str_replace('{{action}}', $action, $body);
526                $body = str_replace('{{company}}', $companyName, $body);
527                $body = str_replace('{{type}}', $budgetType->name, $body);
528                $body = str_replace('{{amount}}', $amount, $body);
529                $body = str_replace('{{quote_id}}', $href, $body);
530                $body = str_replace('{{margin}}', $invoiceMargin, $body);
531
532                $body .= __('language.send_approval_margin_notification.note');
533                $body = str_replace('{{company}}', $companyName, $body);
534                $body = str_replace('{{client_type}}', $clientType->name, $body);
535                $body = str_replace('{{project_type}}', $budgetType->name, $body);
536                $body = str_replace('{{margin}}', $minimumMargin, $body);
537
538                $content = $body;
539
540                $body .= '<p>Fire Service Titan</p>';
541                $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
542
543                $html = '<!DOCTYPE html>';
544                $html .= '<html>';
545                $html .= '<head>';
546                $html .= '<meta charset="UTF-8">';
547                $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
548                $html .= '</head>';
549                $html .= '<body>';
550                $html .= $body;
551                $html .= '</body>';
552                $html .= '</html>';
553
554                if ($toEmail != null) {
555                    $email = new \SendGrid\Mail\Mail;
556
557                    if(config('services.sendgrid.staging')){
558                        $toEmail = $user->email;
559                        $item->user_id = $userId;
560                    }
561
562                    if ($cc == false) {
563                        $cc = true;
564
565                        if ($user->email != $toEmail) {
566                            if ($user->email != $commercialEmail && $commercialEmail != null) {
567                                $email->addBcc($user->email);
568                                $email->addBcc($commercialEmail);
569                            } else {
570                                $email->addBcc($user->email);
571                            }
572                        }
573                    }
574
575                    $email->setFrom('fire@fire.es', 'Fire Service Titan');
576                    $email->setSubject($subject);
577                    $email->addTo($toEmail);
578                    $email->addContent('text/html', $html);
579
580                    $email->addAttachment(
581                        $imgpath,
582                        'image/png',
583                        'fireservicetitan.png',
584                        'inline',
585                        'fireservicetitan'
586                    );
587
588                    $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
589
590                    $response = $sendgrid->send($email);
591                    if ($response->statusCode() == 202) {
592                        Log::channel('email_log')->info('ID:'.$quoteId.' : '.$toEmail.' - MARGIN APPROVAL EMAIL NOTIFICATION SENT');
593
594                        $this->addUpdateLog($quoteId, $userId, 'send_approval_margin_notification', null, null, 5);
595
596                        TblNotifications::create(
597                            [
598                                'user_id' => $item->user_id,
599                                'content' => $content,
600                                'is_open' => 1,
601                                'created_by' => 'System',
602                                'link' => $url
603                            ]
604                        );
605                    } else {
606                        $error = true;
607                        Log::channel('email_log')->error('ID:'.$quoteId.' : '.$toEmail.' - '.$response->body());
608                    }
609                }
610
611            }
612        }
613    }
614
615    function approve_quotation($id): ResponseFactory|HttpResponse{
616
617        try {
618
619            $id = addslashes((string) $id);
620
621            $result = TblQuotations::where('id', $id)->first();
622            $company = TblCompanies::where('company_id', $result->company_id)->first();
623            $budgetType = TblBudgetTypes::where('budget_type_id', $result->budget_type_id)->first();
624
625            if($result->created_by != $result->commercial){
626                $creatorAndCommercial = [$result->created_by, $result->commercial];
627                foreach ($creatorAndCommercial as $name) {
628                    $user = TblUsers::where('name', $name)->first();
629                    if ($user) {
630                        $this->send_approved_notification($user->id, $user->name, $user->email, $company->name, $budgetType->name, $result->amount, $id, $result->quote_id, $company->company_id, 'orders');
631                    }
632                }
633            } else {
634                $user = TblUsers::where('name', $result->created_by)->first();
635                if ($user) {
636                    $this->send_approved_notification($user->id, $user->name, $user->email, $company->name, $budgetType->name, $result->amount, $id, $result->quote_id, $company->company_id, 'orders');
637                }
638            }
639
640            if ($result->for_approval == 3) {
641                $result = TblQuotations::where('id', $id)->first();
642
643                $approved = 0;
644                $rejected = 0;
645
646                if ($result->approved_by !== null) {
647                    $approved++;
648                }
649                if ($result->approved_by_v2 !== null) {
650                    $approved++;
651                }
652
653                if ($result->rejected_by !== null) {
654                    $rejected++;
655                }
656                if ($result->rejected_by_v2 !== null) {
657                    $rejected++;
658                }
659
660                if ($approved === 2) {
661                    TblQuotations::where('id', $id)->update(['for_approval' => null]);
662                } elseif ($rejected >= 1 && ($approved + $rejected) === 2) {
663                    TblQuotations::where('id', $id)->update(['for_approval' => null]);
664                }
665            } elseif ($result->for_approval == 1) {
666                TblQuotations::where('id', $id)->update(['for_approval' => null]);
667            }
668
669            $this->addUpdateLog($id, $user->id, 'approve_quotation', null, null, 5);
670
671            Cache::flush();
672
673            return response(['message' => 'OK']);
674
675        } catch (\Exception $e) {
676            report(AppException::fromException($e, 'APPROVE_QUOTATION_EXCEPTION'));
677            return response(['message' => 'KO', 'error' => $e->getMessage()]);
678        }
679
680    }
681
682    function send_approved_notification($userId, $username, $email, $companyName, $budgetType, $amount, $id, string $quoteId, $companyId, $endpoint): void{
683
684        $fendpoint = '';
685
686        if ($endpoint == 'orders') {
687            if ($this->locale == 'es') {
688                $fendpoint = 'presupuesto';
689            } else {
690                $fendpoint = 'budget';
691            }
692
693        } else {
694            if ($this->locale == 'es') {
695                $fendpoint = 'trabajo';
696            } else {
697                $fendpoint = 'job';
698            }
699        }
700
701        $query = "SELECT
702                    u.id AS user_id,
703                    u.name,
704                    u.email,
705                    u.sender_email,
706                    CASE
707                        WHEN a.user_id IS NOT NULL AND c.user_id IS NOT NULL THEN 'both'
708                        WHEN a.user_id IS NOT NULL THEN 'approvers'
709                        WHEN c.user_id IS NOT NULL THEN 'approvers_v2'
710                        ELSE 'none'
711                    END AS exists_in
712                FROM tbl_users u
713                LEFT JOIN tbl_approvers a
714                    ON u.id = a.user_id AND a.company_id = {$companyId}
715                LEFT JOIN tbl_approvers_v2 c
716                    ON u.id = c.user_id AND c.company_id = {$companyId}
717                WHERE u.id = {$this->userId}";
718
719        $user = DB::select($query);
720
721        $user = $user[0] ?? null;
722
723        $toEmail = $email;
724
725        $amount = $this->currency($amount, 1);
726
727        $imgpath = File::get('fireservicetitan.png');
728        $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
729
730        $url = config('app.frontend_url') . "{$endpoint}/{$id}?company_id={$companyId}";
731        $href = "<a href='{$url}'>{$quoteId}</a>";
732
733        $body = __('language.send_approved_notification.body_hello');
734        $body = str_replace('{{creator}}', $username, $body);
735
736        $body .= __('language.send_approved_notification.body_message');
737        $body = str_replace('{{approver}}', $user->name, $body);
738        $body = str_replace('{{company}}', $companyName, $body);
739        $body = str_replace('{{type}}', $budgetType, $body);
740        $body = str_replace('{{amount}}', $amount, $body);
741        $body = str_replace('{{quote_id}}', $href, $body);
742        $body = str_replace('{{endpoint}}', $fendpoint, $body);
743
744        $content = $body;
745
746        $body .= '<p>Fire Service Titan</p>';
747        $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
748
749        $html = '<!DOCTYPE html>';
750        $html .= '<html>';
751        $html .= '<head>';
752        $html .= '<meta charset="UTF-8">';
753        $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
754        $html .= '</head>';
755        $html .= '<body>';
756        $html .= $body;
757        $html .= '</body>';
758        $html .= '</html>';
759
760        $subject = __('language.send_approved_notification.subject');
761        $subject = str_replace('{{quote_id}}', $quoteId, $subject);
762        $subject = str_replace('{{endpoint}}', ucfirst($fendpoint), $subject);
763
764        if ($toEmail != null) {
765            $email = new \SendGrid\Mail\Mail;
766
767            if(config('services.sendgrid.staging')){
768                $toEmail = $user->email;
769                $userId = $this->userId;
770            }
771
772            $email->setFrom('fire@fire.es', 'Fire Service Titan');
773            $email->setSubject($subject);
774            $email->addTo($toEmail);
775            $email->addContent('text/html', $html);
776
777            $email->addAttachment(
778                $imgpath,
779                'image/png',
780                'fireservicetitan.png',
781                'inline',
782                'fireservicetitan'
783            );
784
785            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
786
787            $response = $sendgrid->send($email);
788            if ($response->statusCode() == 202) {
789                Log::channel('email_log')->info('ID:'.$quoteId.' : '.$toEmail.' - APPROVED EMAIL NOTIFICATION SENT');
790                $this->addUpdateLog($id, $userId, 'send_approved_notification', null, null, 5);
791
792                if ($endpoint == 'orders') {
793                    if ($user->exists_in == 'approvers') {
794                        TblQuotations::where('id', $id)->update(
795                            [
796                                'approved_at' => date('Y-m-d H:i:s'),
797                                'approved_by' => $user->name
798                            ]
799                        );
800                    } elseif ($user->exists_in == 'approvers_v2') {
801                        TblQuotations::where('id', $id)->update(
802                            [
803                                'approved_at_v2' => date('Y-m-d H:i:s'),
804                                'approved_by_v2' => $user->name
805                            ]
806                        );
807                    }
808                } else {
809                    TblOngoingJobs::where('id', $id)->update(
810                        [
811                            'approved_at' => date('Y-m-d H:i:s'),
812                            'approved_by' => $user->name
813                        ]
814                    );
815                }
816
817                TblNotifications::create(
818                    [
819                        'user_id' => $userId,
820                        'content' => $content,
821                        'is_open' => 1,
822                        'created_by' => 'System',
823                        'link' => $url
824                    ]
825                );
826            } else {
827                $error = true;
828                Log::channel('email_log')->error('ID:'.$quoteId.' : '.$toEmail.' - '.$response->body());
829            }
830        }
831
832    }
833
834    function reject_quotation($id): ResponseFactory|HttpResponse{
835
836        try {
837
838            $id = addslashes((string) $id);
839
840            $result = TblQuotations::where('id', $id)->first();
841            $company = TblCompanies::where('company_id', $result->company_id)->first();
842            $budgetType = TblBudgetTypes::where('budget_type_id', $result->budget_type_id)->first();
843
844            if($result->created_by != $result->commercial){
845                $creatorAndCommercial = [$result->created_by, $result->commercial];
846                foreach ($creatorAndCommercial as $name) {
847                    $user = TblUsers::where('name', $name)->first();
848                    if ($user) {
849                        $this->send_rejected_notification($user->id, $user->name, $user->email, $company->name, $budgetType->name, $result->amount, $id, $result->quote_id, $company->company_id, 'orders');
850                    }
851                }
852            } else {
853                $user = TblUsers::where('name', $result->created_by)->first();
854                if ($user) {
855                    $this->send_rejected_notification($user->id, $user->name, $user->email, $company->name, $budgetType->name, $result->amount, $id, $result->quote_id, $company->company_id, 'orders');
856                }
857            }
858
859            if ($result->for_approval == 3) {
860                $result = TblQuotations::where('id', $id)->first();
861
862                $approved = 0;
863                $rejected = 0;
864
865                if ($result->approved_by !== null) {
866                    $approved++;
867                }
868                if ($result->approved_by_v2 !== null) {
869                    $approved++;
870                }
871
872                if ($result->rejected_by !== null) {
873                    $rejected++;
874                }
875                if ($result->rejected_by_v2 !== null) {
876                    $rejected++;
877                }
878
879                if ($approved === 2) {
880                    TblQuotations::where('id', $id)->update(['for_approval' => null]);
881                } elseif ($rejected >= 1 && ($approved + $rejected) === 2) {
882                    TblQuotations::where('id', $id)->update(['for_approval' => null]);
883                }
884            } elseif ($result->for_approval == 1) {
885                TblQuotations::where('id', $id)->update(['for_approval' => null]);
886            }
887
888            $this->addUpdateLog($id, $user->id, 'reject_quotation', null, null, 5);
889
890            Cache::flush();
891
892            return response(['message' => 'OK']);
893
894        } catch (\Exception $e) {
895            report(AppException::fromException($e, 'REJECT_QUOTATION_EXCEPTION'));
896            return response(['message' => 'KO', 'error' => $e->getMessage()]);
897        }
898
899    }
900
901    function send_rejected_notification($userId, $username, $email, $companyName, $budgetType, $amount, $id, string $quoteId, $companyId, $endpoint): void{
902
903        $fendpoint = '';
904
905        if ($endpoint == 'orders') {
906            if ($this->locale == 'es') {
907                $fendpoint = 'presupuesto';
908            } else {
909                $fendpoint = 'budget';
910            }
911
912        } else {
913            if ($this->locale == 'es') {
914                $fendpoint = 'trabajo';
915            } else {
916                $fendpoint = 'job';
917            }
918        }
919
920        $query = "SELECT
921                    u.id AS user_id,
922                    u.name,
923                    u.email,
924                    u.sender_email,
925                    CASE
926                        WHEN a.user_id IS NOT NULL AND c.user_id IS NOT NULL THEN 'both'
927                        WHEN a.user_id IS NOT NULL THEN 'approvers'
928                        WHEN c.user_id IS NOT NULL THEN 'approvers_v2'
929                        ELSE 'none'
930                    END AS exists_in
931                FROM tbl_users u
932                LEFT JOIN tbl_approvers a
933                    ON u.id = a.user_id AND a.company_id = {$companyId}
934                LEFT JOIN tbl_approvers_v2 c
935                    ON u.id = c.user_id AND c.company_id = {$companyId}
936                WHERE u.id = {$this->userId}";
937
938        $user = DB::select($query);
939
940        $user = $user[0] ?? null;
941
942        $toEmail = $email;
943        $amount = $this->currency($amount, 1);
944
945        $imgpath = File::get('fireservicetitan.png');
946        $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
947
948        $url = config('app.frontend_url') . "{$endpoint}/{$id}?company_id={$companyId}";
949        $href = "<a href='{$url}'>{$quoteId}</a>";
950
951        $body = __('language.send_rejected_notification.body_hello');
952        $body = str_replace('{{creator}}', $username, $body);
953
954        $body .= __('language.send_rejected_notification.body_message');
955        $body = str_replace('{{approver}}', $user->name, $body);
956        $body = str_replace('{{company}}', $companyName, $body);
957        $body = str_replace('{{type}}', $budgetType, $body);
958        $body = str_replace('{{amount}}', $amount, $body);
959        $body = str_replace('{{quote_id}}', $href, $body);
960        $body = str_replace('{{endpoint}}', $fendpoint, $body);
961
962        $content = $body;
963
964        $body .= '<p>Fire Service Titan</p>';
965        $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
966
967        $html = '<!DOCTYPE html>';
968        $html .= '<html>';
969        $html .= '<head>';
970        $html .= '<meta charset="UTF-8">';
971        $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
972        $html .= '</head>';
973        $html .= '<body>';
974        $html .= $body;
975        $html .= '</body>';
976        $html .= '</html>';
977
978        $subject = __('language.send_rejected_notification.subject');
979        $subject = str_replace('{{quote_id}}', $quoteId, $subject);
980        $subject = str_replace('{{endpoint}}', ucfirst($fendpoint), $subject);
981
982        if ($toEmail != null) {
983            $email = new \SendGrid\Mail\Mail;
984
985            if(config('services.sendgrid.staging')){
986                $toEmail = $user->email;
987                $userId = $this->userId;
988            }
989
990            $email->setFrom('fire@fire.es', 'Fire Service Titan');
991            $email->setSubject($subject);
992            $email->addTo($toEmail);
993            $email->addContent('text/html', $html);
994
995            $email->addAttachment(
996                $imgpath,
997                'image/png',
998                'fireservicetitan.png',
999                'inline',
1000                'fireservicetitan'
1001            );
1002
1003            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
1004
1005            $response = $sendgrid->send($email);
1006            if ($response->statusCode() == 202) {
1007                Log::channel('email_log')->info('ID:'.$quoteId.' : '.$toEmail.' - REJECTED EMAIL NOTIFICATION SENT');
1008                $this->addUpdateLog($id, $userId, 'send_rejected_notification', null, null, 5);
1009
1010                if ($endpoint == 'orders') {
1011
1012                    if ($user->exists_in == 'approvers') {
1013                        TblQuotations::where('id', $id)->update(
1014                            [
1015                                'rejected_at' => date('Y-m-d H:i:s'),
1016                                'rejected_by' => $user->name
1017                            ]
1018                        );
1019                    } elseif ($user->exists_in == 'approvers_v2') {
1020                        TblQuotations::where('id', $id)->update(
1021                            [
1022                                'rejected_at_v2' => date('Y-m-d H:i:s'),
1023                                'rejected_by_v2' => $user->name
1024                            ]
1025                        );
1026                    }
1027                } else {
1028                    TblOngoingJobs::where('id', $id)->update(
1029                        [
1030                            'rejected_at' => date('Y-m-d H:i:s'),
1031                            'rejected_by' => $user->name
1032                        ]
1033                    );
1034                }
1035
1036                TblNotifications::create(
1037                    [
1038                        'user_id' => $userId,
1039                        'content' => $content,
1040                        'is_open' => 1,
1041                        'created_by' => 'System',
1042                        'link' => $url
1043                    ]
1044                );
1045            } else {
1046                $error = true;
1047                Log::channel('email_log')->error('ID:'.$quoteId.' : '.$toEmail.' - '.$response->body());
1048            }
1049        }
1050
1051    }
1052
1053    public function update_quotation(Request $request, $id): ResponseFactory|HttpResponse{
1054        $approvalMinimumOrderSize = null;
1055        $approvalIsQuestion = null;
1056        $approvalQuestionIdsNo = null;
1057        $approvalN = null;
1058        $approvalMinimumMargin = null;
1059        $approvalId = null;
1060        $approvalForAdd = null;
1061        $approvalInvoiceMargin = null;
1062        $sendApprovalNotification = false;
1063        $sendApprovalMarginNotification = false;
1064        $needToSendReminder = false;
1065
1066        // try {
1067
1068        $data = $request->all();
1069        $id = addslashes((string) $id);
1070        $userId = addslashes((string) $data['user_id']);
1071        if (! TblQuotationsLog::where('quotation_id', $id)->exists()) {
1072            $categoryLog = $data['budget_status_id'] == 6 ? 0 : 2;
1073            $this->addUpdateLog($id, $data['user_id'], 'create_quotation', null, null, $categoryLog);
1074        }
1075        $forApproval = null;
1076        unset($data['user_id']);
1077
1078        $r = ['amount', 'order_number', 'budget_type_id', 'acceptance_date'];
1079        $job = [];
1080
1081        foreach ($data as $key => $value) {
1082            if ($value == 'null') {
1083                $data[$key] = null;
1084            }
1085
1086            if (in_array($key, $r)) {
1087                $job[$key] = $value;
1088            }
1089        }
1090
1091        $files = $request->file('files');
1092        unset($data['files']);
1093
1094        $internalFiles = $request->file('internal_files');
1095        unset($data['internal_files']);
1096
1097        $query = '
1098            SELECT
1099                SUM(CASE WHEN is_internal IS NULL THEN 1 ELSE 0 END) as external_count,
1100                SUM(CASE WHEN is_internal = 1 THEN 1 ELSE 0 END) as internal_count
1101            FROM tbl_files
1102            WHERE quotation_id = ?';
1103
1104        $counts = DB::select($query, [$id]);
1105        $fileCount = $counts[0]->external_count;
1106        $internalFileCount = $counts[0]->internal_count;
1107
1108        if ($fileCount > 0 || ! empty($files)) {
1109            $data['has_attachment'] = 1;
1110        }
1111
1112        if ($files) {
1113            $totalFiles = $fileCount + count($files);
1114            if ($totalFiles > 10) {
1115                return response(['message' => 'KO', 'error' => __('language.file_count_exceeded')]);
1116            }
1117        }
1118
1119        if ($internalFileCount > 0 || ! empty($internalFiles)) {
1120            $data['has_attachment'] = 1;
1121        }
1122
1123        if ($internalFiles) {
1124            $totalInternalFileCount = $internalFileCount + count($internalFiles);
1125            if ($totalInternalFileCount > 10) {
1126                return response(['message' => 'KO', 'error' => __('language.file_count_exceeded')]);
1127            }
1128        }
1129
1130        if (isset($data['request_date']) && isset($data['issue_date'])) {
1131            $requestDate = strtotime($data['request_date']);
1132            $issueDate = strtotime($data['issue_date']);
1133            $dateDiff = $issueDate - $requestDate;
1134            $data['duration'] = round($dateDiff / (60 * 60 * 24));
1135        }
1136
1137        $result = TblQuotations::where('id', $id)->first();
1138
1139        if ($result->quote_id != $data['quote_id']) {
1140
1141            $c = TblQuotations::where('quote_id', (string) $data['quote_id'])->where('company_id', $result->company_id)->count();
1142
1143            if ($c > 0) {
1144
1145                if ($result->for_add == 0) {
1146                    $latestBudget = TblQuotations::where('company_id', $result->company_id)->orderByRaw('CAST(quote_id AS DOUBLE) DESC')->first();
1147
1148                    $number = $latestBudget->quote_id;
1149                    $x = true;
1150
1151                    while ($x) {
1152
1153                        if(is_numeric(substr((string) $number, -1))) {
1154                            $number++;
1155                        }else{
1156                            $number .= "1";
1157                        }
1158
1159                        $check = 0;
1160
1161                        $check = TblQuotations::where('company_id', $result->company_id)->where('quote_id', (string) $number)->count();
1162
1163                        if ($check == 0) {
1164                            $x = false;
1165                        }
1166                    }
1167
1168                    return response(['message' => 'KO', 'error' => 'quote_exists', 'number' => $number]);
1169                }
1170
1171                return response(['message' => 'KO', 'error' => 'quote_exists']);
1172            }
1173        }
1174
1175        $action = 0;
1176        if ($result->created_by == null || $result->for_add == 1) {
1177            $action = 1;
1178            $data['created_by'] = $data['updated_by'];
1179            $data['for_add'] = 0;
1180        }
1181
1182        $company = TblCompanies::where('company_id', $result->company_id)->first();
1183        $commercial = TblUsers::where('name', $data['commercial'])->first();
1184        $status = TblBudgetStatus::where('budget_status_id', $data['budget_status_id'])->first();
1185
1186        // $checkQuotation = TblQuotations::where(function($query) use ($data, $result) {
1187        //     $query->where('internal_quote_id', $data['internal_quote_id'])
1188        //         ->orWhere('internal_quote_id', 'O-25/'.$data['internal_quote_id']);
1189        // })
1190        //     ->where('company_id', $result->company_id)
1191        //     ->first();
1192
1193        // if($checkQuotation) {
1194        //     $url = "orders/" . $checkQuotation->id . "?company_id=" . $checkQuotation->company_id;
1195        //     return response([
1196        //         'message' => 'KO',
1197        //         'error' => "Presupuesto ya creado. Puedes verlo <a href='$url' target='_blank'>aquí</a>."
1198        //     ]);
1199        // }
1200
1201        $limitReminderEmails = $company->limit_reminder_emails ?? 3;
1202
1203        if ($result->total_sent == $limitReminderEmails) {
1204            $data['total_sent'] = 0;
1205        }
1206
1207        if ($result->budget_status_id != $data['budget_status_id'] || $result->commercial != $data['commercial']) {
1208            if ($data['budget_status_id'] == 12) {
1209                if ($company && $commercial) {
1210                    $inProgressCount = TblQuotations::where('budget_status_id', 12)->where('company_id', $result->company_id)->where('commercial', $data['commercial'])->count();
1211                    if ($company->process_limit <= $inProgressCount) {
1212                        return response(['message' => 'KO', 'error' => 'in_progress', 'limit' => $company->process_limit]);
1213                    }
1214                } else {
1215                    return response(['message' => 'KO', 'error' => 'in_progress', 'limit' => 0]);
1216                }
1217            }
1218        }
1219
1220        $sendNotification = false;
1221        if ($result->commercial != $data['commercial']) {
1222            if (! empty($commercial)) {
1223                $createdByX = ($result->created_by == null) ? $data['created_by'] : $result->created_by;
1224                if ($createdByX != $data['commercial']) {
1225                    $action = 0;
1226                    $sendNotification = true;
1227                }
1228            }
1229        }
1230
1231        if (isset($data['amount']) || isset($data['budget_type_id']) || isset($data['customer_type_id']) || isset($data['invoice_margin']) || isset($data['question_enabled'])) {
1232            if ($company) {
1233
1234                $n = 0;
1235                $invoiceMargin = 0;
1236                $minimumMargin = 0;
1237                $minimumOrderSize = 0;
1238                
1239                if($result->amount != $data['amount'] ||
1240                    $result->budget_type_id != $data['budget_type_id'] ||
1241                    $result->customer_type_id != $data['customer_type_id'] ||
1242                    $result->invoice_margin != $data['invoice_margin']
1243                ){
1244                    $project = TblProjectTypes::where('company_id', $company->company_id)->where('budget_type_id', $data['budget_type_id'])->first();
1245                    $customerTypeIds = [];
1246
1247                    if($project){
1248                        if(!empty($project->customer_type_ids)){
1249                            $customerTypeIds = array_map(intval(...), explode(',', (string) $project->customer_type_ids));
1250                        }
1251                        if($project->minimum_order_size != null && in_array($data['customer_type_id'], $customerTypeIds)){
1252                            if($data['amount'] >= $project->minimum_order_size){
1253                                $data['for_approval'] = 1;
1254                                $n = 1;
1255                            }
1256                        }
1257                        $minimumOrderSize = $project->minimum_order_size;
1258
1259                        if ($n == 1 && $project->minimum_order_size_v2 != null && in_array($data['customer_type_id'], $customerTypeIds)) {
1260                            if ($data['amount'] >= $project->minimum_order_size_v2) {
1261                                $data['for_approval'] = 3;
1262                                $n = 3;
1263                            }
1264                        }
1265                    }
1266
1267                } else {
1268                    if (! empty($company->customer_type_ids)) {
1269                        $customerTypeIds = array_map(intval(...), explode(',', (string) $company->customer_type_ids));
1270                    }
1271                    if ($company->minimum_order_size != null && in_array($data['customer_type_id'], $customerTypeIds)) {
1272                        if ($data['amount'] >= $company->minimum_order_size) {
1273                            $data['for_approval'] = 1;
1274                            $n = 1;
1275                        }
1276                        $minimumOrderSize = $company->minimum_order_size;
1277
1278                        if ($n == 1 && $company->minimum_order_size_v2 != null && in_array($data['customer_type_id'], $customerTypeIds)) {
1279                            if ($data['amount'] >= $company->minimum_order_size_v2) {
1280                                $data['for_approval'] = 3;
1281                                $n = 3;
1282                            }
1283                        }
1284                    }
1285                }
1286
1287                if ($data['budget_margin_enabled'] > 0) {
1288                    $costOfLabor = $data['cost_of_labor'];
1289                    $totalCostOfJob = $data['total_cost_of_job'];
1290
1291                    if ($totalCostOfJob > 0) {
1292                        $invoiceMargin = $data['invoice_margin'] ?? 0;
1293                    }
1294
1295                    $minimumMargin = $company->minimum_margin;
1296                    if (! empty($company->customer_type_ids)) {
1297                        $customerTypeIds = array_map(intval(...), explode(',', (string) $company->customer_type_ids));
1298                    }
1299
1300                    if ($project) {
1301                        $minimumMargin = $project->minimum_margin;
1302                        $minimumOrderSize = $project->minimum_order_size;
1303                        if (! empty($project->customer_type_ids)) {
1304                            $customerTypeIds = array_map(intval(...), explode(',', (string) $project->customer_type_ids));
1305                        }
1306                    }
1307
1308                    if ($invoiceMargin < $minimumMargin && $invoiceMargin != null && $invoiceMargin != 0) {
1309                        if (in_array($data['customer_type_id'], $customerTypeIds)) {
1310                            $data['for_approval'] = 1;
1311                            $n = 2;
1312                        }
1313                    }
1314                }
1315
1316                $isQuestion = 0;
1317                $questionIdsNo = [];
1318                if (isset($data['question_enabled'])) {
1319                    if ($data['question_ids_no'] != $result->question_ids_no
1320                        || $data['budget_type_id'] != $result->budget_type_id
1321                        || $data['customer_type_id'] != $result->customer_type_id
1322                        || $data['amount'] != $result->amount) {
1323                        if ($data['question_enabled'] > 0 && $n == 0) {
1324                            if (! empty($data['question_ids_no'])) {
1325                                $questionIdsNo = array_map(intval(...), explode(",", (string) $data['question_ids_no']));
1326
1327                                if ($company->workflow_budget_size != null) {
1328                                    if ($data['amount'] >= $company->workflow_budget_size) {
1329                                        $isQuestion = 1;
1330                                        $data['for_approval'] = 1;
1331                                        $n = 1;
1332                                    }
1333                                }
1334                                $minimumOrderSize = $company->workflow_budget_size;
1335
1336                            }
1337                        }
1338                    }
1339                }
1340
1341                if (($n == 1 || $n == 3) && $result->for_approval != $n) {
1342                    $sendApprovalNotification = true;
1343                    $approvalMinimumOrderSize = $minimumOrderSize;
1344                    $approvalId = $result->id;
1345                    $approvalForAdd = $result->for_add;
1346                    $approvalIsQuestion = $isQuestion;
1347                    $approvalQuestionIdsNo = $questionIdsNo;
1348                    $approvalN = $n;
1349                }
1350
1351                if ($n == 2) {
1352                    $sendApprovalMarginNotification = true;
1353                    $approvalMinimumMargin = $minimumMargin;
1354                    $approvalId = $result->id;
1355                    $approvalForAdd = $result->for_add;
1356                    $approvalInvoiceMargin = $invoiceMargin;
1357                }
1358            }
1359        }
1360
1361        $data['updated_at'] = date('Y-m-d H:i:s');
1362        $job['updated_at'] = $data['updated_at'];
1363
1364        $data['g3w_warning'] = 0;
1365
1366        if ($result->for_add == 1) {
1367            TblCompanies::where('company_id', $result->company_id)->update(['last_id' => $data['quote_id'], 'before_last_id' => $data['quote_id']]);
1368        }
1369
1370        $forApproval = @$data['for_approval'] ?? null;
1371
1372        if ($forApproval != null) {
1373            $data['approved_at'] = null;
1374            $data['approved_by'] = null;
1375            $data['rejected_at'] = null;
1376            $data['rejected_by'] = null;
1377            $data['approved_at_v2'] = null;
1378            $data['approved_by_v2'] = null;
1379            $data['rejected_at_v2'] = null;
1380            $data['rejected_by_v2'] = null;
1381        }
1382
1383        $actualQuotationValue = TblQuotations::where('id', $id)->first();
1384
1385        $differences = $this->compareArrays($data, $actualQuotationValue);
1386        $primaryAprovalsFields = ['budget_type_id', 'customer_type_id', 'budget_margin_enabled', 'amount', 'invoice_margin', 'margin_for_the_company', 'margin_on_invoice_per_day_per_worker', 'gross_margin'];
1387
1388        if (is_null($actualQuotationValue->customer_type_id)) {
1389            $needToSendReminder = true;
1390        }
1391
1392        foreach ($differences as $field => $value) {
1393            if (in_array($field, $primaryAprovalsFields)) {
1394                $needToSendReminder = true;
1395            }
1396            $statusId = $data['budget_status_id'] ?? $actualQuotationValue->budget_status_id;
1397            $categoryLog = $statusId == 6 ? 1 : 4;
1398            $this->addUpdateLog($id, $userId, $field, $value['oldData'], $value['newData'], $categoryLog);
1399        }
1400
1401        // check if the quotation are a solicitud and the user write the internal quote id
1402
1403        $budgetRequest = TblQuotations::where('id', $id)->first();
1404
1405        if (
1406            ($budgetRequest->budget_status_id == 6 || $budgetRequest->budget_status_id == 8)
1407            && (! is_null($data['internal_quote_id'] ?? null) && $data['internal_quote_id'] !== '')
1408            && (TblQuotations::where('internal_quote_id', $data['internal_quote_id'])
1409                ->where('company_id', $company->company_id)
1410                ->whereNotIn('budget_status_id', [6, 8])
1411                ->exists())
1412            && ($id !== '')
1413        ) {
1414            $createdAt = $budgetRequest->created_at;
1415            $lastFollowUpComment = $budgetRequest->last_follow_up_comment;
1416
1417            TblQuotations::where('id', $id)->first()->update(['internal_quote_id', $data['internal_quote_id']]);
1418
1419            $solicitudLogs = TblQuotationsLog::where('quotation_id', $id)->get();
1420
1421            $budget = TblQuotations::where('internal_quote_id', $data['internal_quote_id'])->where('company_id', $company->company_id)->first();
1422            TblFiles::where('quotation_id', $id)->where('is_internal', 1)->update(['quotation_id' => $budget->id]);
1423
1424            $this->callDeleteQuotation($id, $company->company_id, $userId, $data['updated_by']);
1425
1426            $id = $budget->id;
1427
1428            $dataToChange = array_intersect_key($data, array_flip([
1429                'internal_quote_id',
1430                'client',
1431                'phone_number',
1432                'email',
1433                'source_id',
1434                'created_by',
1435                'updated_by',
1436                'updated_at',
1437                'request_date',
1438                'last_follow_up_comment',
1439            ]));
1440
1441            if ($data['phone_number'] === null || $data['phone_number'] === '' || empty($dataToChange['phone_number'])) {
1442                unset($dataToChange['phone_number']);
1443            }
1444
1445            //check if the quotation are a solicitud and the user write the internal quote id
1446
1447            $budgetRequest = TblQuotations::where('id', $id)->first();
1448
1449            if(
1450                ($budgetRequest->budget_status_id == 6 || $budgetRequest->budget_status_id == 8)
1451                && (!is_null($data["internal_quote_id"] ?? null) && $data["internal_quote_id"] !== "")
1452                && TblQuotations::where('internal_quote_id', $data["internal_quote_id"])->where("company_id", $company->company_id)->exists()
1453                && (!is_null($id) && $id !== "")
1454            ){
1455                $createdAt = $budgetRequest->created_at;
1456                $lastFollowUpComment = $budgetRequest->last_follow_up_comment;
1457
1458                TblQuotations::where('id', $id)->first()->update(["internal_quote_id", $data["internal_quote_id"]]);
1459
1460                $solicitudLogs = TblQuotationsLog::where("quotation_id", $id)->get();
1461
1462                $budget = TblQuotations::where('internal_quote_id', $data["internal_quote_id"])->where("company_id", $company->company_id)->first();
1463                TblFiles::where('quotation_id', $id)->where('is_internal', 1)->update(['quotation_id' => $budget->id]);
1464
1465                $this->callDeleteQuotation($id, $company->company_id, $userId, $data['updated_by']);
1466
1467                $id = $budget->id;
1468
1469                $dataToChange = array_intersect_key($data, array_flip([
1470                    'internal_quote_id',
1471                    'client',
1472                    'phone_number',
1473                    'email',
1474                    'source_id',
1475                    'created_by',
1476                    'updated_by',
1477                    'updated_at',
1478                    'request_date',
1479                    'last_follow_up_comment'
1480                ]));
1481
1482                if($data["phone_number"] === null || $data["phone_number"] === "" || empty($dataToChange["phone_number"])){
1483                    unset($dataToChange['phone_number']);
1484                }
1485
1486                $dataToChange['budget_status_id'] = $budget->budget_status_id;
1487                $dataToChange["created_at"] = $createdAt;
1488                $dataToChange["last_follow_up_comment"] = $budget->last_follow_up_comment . "\n" . $lastFollowUpComment;
1489
1490                $data["g3w_warning"] = 0;
1491
1492                if(
1493                    in_array($budget->budget_status_id, [13, 14]) ||
1494                    !$dataToChange["source_id"] ||
1495                    (!$budget->budget_type_id || $budget->budget_type_id == 0 || $budget->budget_type_id == 16) ||
1496                    empty(trim((string) $dataToChange["client"])) ||
1497                    empty(trim((string) $dataToChange["email"]))
1498                ){
1499                    $data["g3w_warning"] = 1;
1500                }
1501
1502                TblQuotations::where('id', $id)->update($dataToChange);
1503
1504                if (!empty($solicitudLogs)) {
1505                    $nuevosLogs = array_map(function(array $log) use ($budget): array {
1506                        unset($log['id']); 
1507                        $log['quotation_id'] = $budget->id; 
1508                        return $log;
1509                    }, $solicitudLogs);
1510
1511                    TblQuotationsLog::insert($nuevosLogs);
1512                }
1513
1514
1515                $data['g3w_warning'] = 0;
1516
1517                if (
1518                    in_array($budget->budget_status_id, [13, 14]) ||
1519                    ! $dataToChange['source_id'] ||
1520                    (! $budget->budget_type_id || $budget->budget_type_id == 16) ||
1521                    empty(trim($dataToChange['client'])) ||
1522                    empty(trim($dataToChange['email']))
1523                ) {
1524                    $data['g3w_warning'] = 1;
1525                }
1526
1527                TblQuotations::where('id', $id)->update($dataToChange);
1528
1529                if ($solicitudLogs->isNotEmpty()) {
1530                    $nuevosLogs = array_map(function ($log) use ($budget) {
1531                        unset($log['id']);
1532                        $log['quotation_id'] = $budget->id;
1533
1534                        return $log;
1535                    }, $solicitudLogs->toArray());
1536
1537                    TblQuotations::where('id', $id)->update(
1538                        [
1539                            'accepted_at' => $data['updated_at'],
1540                            'accepted_by' => $data['updated_by']
1541                        ]
1542                    );
1543                }
1544
1545            }
1546
1547        } else {
1548            // If email is blacklisted and status is sendable, set to 22 (Correo erroneo)
1549            if (isset($data['email']) && $this->isBlacklistedEmail($data['email'])
1550                && isset($data['budget_status_id']) && in_array($data['budget_status_id'], [1, 2, 11, 17])) {
1551                $data['budget_status_id'] = 22;
1552            }
1553
1554            TblQuotations::where('id', $id)->update($data);
1555        }
1556
1557        TblOngoingJobs::where('quotation_id', $id)->update($job);
1558
1559        $this->update_commercial_numbers($result->company_id);
1560
1561        if ($result->budget_status_id != $data['budget_status_id']
1562            && $data['budget_status_id'] == 3) {
1563            $this->send_acceptance_notification($result->id, $result->company_id, $userId, $data['updated_by']);
1564
1565            TblQuotations::where('id', $id)->update(
1566                [
1567                    'accepted_at' => $data['updated_at'],
1568                    'accepted_by' => $data['updated_by'],
1569                ]
1570            );
1571
1572            // Promote lead to general when quotation is accepted
1573            $quotation = TblQuotations::find($id);
1574            if ($quotation && $quotation->client_id) {
1575                Client::where('id', $quotation->client_id)
1576                    ->where('client_type_id', Client::TYPE_LEAD)
1577                    ->update(['client_type_id' => Client::TYPE_GENERAL]);
1578            }
1579        }
1580
1581        if ($files) {
1582
1583            $uploadedFiles = [];
1584            $i = 0;
1585
1586            $combinedFilesSize = 0;
1587            foreach ($files as $file) {
1588                $i++;
1589                $origFilename = $file->getClientOriginalName();
1590                $filename = $result->id.'-'.$result->company_id.'-FC'.time().'-'.$origFilename;
1591
1592                $combinedFilesSize = $combinedFilesSize + $file->getSize();
1593
1594                if ($combinedFilesSize > 25000000) {
1595                    return response(['message' => 'KO', 'error' => __('language.file_size_exceeded')]);
1596                }
1597
1598                if (in_array($origFilename, $uploadedFiles)) {
1599                    $origFilename = $origFilename.$i;
1600                }
1601
1602                // $fileContent = file_get_contents($file->getRealPath());
1603                // $fileHash = hash('sha256', $fileContent);
1604
1605                $s3path = Storage::disk('s3')->putFileAs(
1606                    'uploads',
1607                    $file,
1608                    $filename,
1609                    [
1610                        'ContentType' => $file->getMimeType(),
1611                    ]
1612                );
1613
1614                TblFiles::create(
1615                    [
1616                        'quotation_id' => $id,
1617                        'original_name' => $origFilename,
1618                        'filename' => $filename,
1619                        'uploaded_by' => $data['updated_by'],
1620                        // 'file' => $fileContent,
1621                        'file_size' => $file->getSize(),
1622                        // 'file_hash' => $fileHash,
1623                        'mime_type' => $file->getMimeType(),
1624                        'uploaded_at' => now(),
1625                    ]
1626                );
1627                $this->addUpdateLog($id, $data['updated_by'], 'upload_attachment', null, $filename, 4);
1628
1629                $uploadedFiles[] = $file->getClientOriginalName();
1630            }
1631        }
1632
1633        if ($internalFiles) {
1634
1635            $uploadedFiles = [];
1636            $i = 0;
1637
1638            $combinedFilesSize = 0;
1639            foreach ($internalFiles as $file) {
1640                $i++;
1641                $origFilename = $file->getClientOriginalName();
1642                $filename = $result->id.'-'.$result->company_id.'-FI'.time().'-'.$origFilename;
1643
1644                $combinedFilesSize = $combinedFilesSize + $file->getSize();
1645
1646                if ($combinedFilesSize > 25000000) {
1647                    return response(['message' => 'KO', 'error' => __('language.file_size_exceeded')]);
1648                }
1649
1650                if (in_array($origFilename, $uploadedFiles)) {
1651                    $origFilename = $origFilename.$i;
1652                }
1653
1654                // $fileContent = file_get_contents($file->getRealPath());
1655                // $fileHash = hash('sha256', $fileContent);
1656
1657                $s3path = Storage::disk('s3')->putFileAs(
1658                    'uploads',
1659                    $file,
1660                    $filename,
1661                    [
1662                        'ContentType' => $file->getMimeType(),
1663                    ]
1664                );
1665
1666                TblFiles::create(
1667                    [
1668                        'quotation_id' => $id,
1669                        'original_name' => $origFilename,
1670                        'filename' => $filename,
1671                        'uploaded_by' => $data['updated_by'],
1672                        // 'file' => $fileContent,
1673                        'file_size' => $file->getSize(),
1674                        // 'file_hash' => $fileHash,
1675                        'mime_type' => $file->getMimeType(),
1676                        'uploaded_at' => now(),
1677                        'is_internal' => 1,
1678                    ]
1679                );
1680                $this->addUpdateLog($id, $data['updated_by'], 'upload_attachment', null, $filename, 4);
1681
1682                $uploadedFiles[] = $file->getClientOriginalName();
1683            }
1684        }
1685
1686        $query = "SELECT
1687                        a.id,
1688                        a.quote_id,
1689                        a.company_id,
1690                        b.name company_name,
1691                        a.client,
1692                        c.name client_type,
1693                        c.customer_type_id,
1694                        a.request_date,
1695                        a.visit_date,
1696                        a.issue_date,
1697                        a.acceptance_date,
1698                        a.internal_quote_id,
1699                        DATE_FORMAT(a.request_date, '%d/%m/%Y') request_date_translate,
1700                        DATE_FORMAT(a.issue_date, '%d/%m/%Y') issue_date_translate,
1701                        DATE_FORMAT(a.acceptance_date, '%d/%m/%Y') acceptance_date_translate,
1702                        DATE_FORMAT(a.last_follow_up_date, '%d/%m/%Y') last_follow_up_date_translate,
1703                        -- DATEDIFF(a.issue_date, a.request_date) duration,
1704                        a.phone_number,
1705                        a.email,
1706                        a.duration,
1707                        a.order_number,
1708                        d.name 'type',
1709                        d.budget_type_id,
1710                        e.name 'status',
1711                        e.budget_status_id,
1712                        f.name source,
1713                        f.source_id,
1714                        a.amount,
1715                        g.name reason_for_not_following_up,
1716                        a.reason_for_not_following_up_id,
1717                        a.reason_for_rejection_id,
1718                        a.last_follow_up_date,
1719                        a.last_follow_up_comment,
1720                        CASE WHEN a.reason_for_rejection_id IS NULL THEN a.reason_for_rejection ELSE h.name END reason_for_rejection,
1721                        a.commercial,
1722                        a.created_by,
1723                        a.created_at,
1724                        a.updated_by,
1725                        a.for_approval,
1726                        a.box_work_g3w,
1727                        a.people_assigned_to_the_job,
1728                        a.duration_of_job_in_days,
1729                        a.estimated_cost_of_materials,
1730                        a.budget_margin_enabled,
1731                        a.question_enabled,
1732                        a.cost_of_labor,
1733                        a.total_cost_of_job,
1734                        CASE WHEN a.budget_margin_enabled > 0 THEN a.invoice_margin ELSE NULL END invoice_margin,
1735                        CASE WHEN a.budget_margin_enabled > 0 THEN a.margin_for_the_company ELSE NULL END margin_for_the_company,
1736                        a.margin_on_invoice_per_day_per_worker,
1737                        a.revenue_per_date_per_worked,
1738                        a.commission_cost,
1739                        a.commission_pct,
1740                        a.gross_margin,
1741                        a.labor_percentage,
1742                        a.question_ids,
1743                        a.question_ids_no,
1744                        a.approved_at,
1745                        a.approved_by,
1746                        a.rejected_at,
1747                        a.rejected_by,
1748                        a.approved_at_v2,
1749                        a.approved_by_v2,
1750                        a.rejected_at_v2,
1751                        a.rejected_by_v2,
1752                        a.accepted_at,
1753                        a.accepted_by,
1754                        a.is_validated,
1755                        a.resource_id,
1756                        a.x_status,
1757                        a.likehood,
1758                        a.updated_at
1759                    FROM
1760                        tbl_quotations a
1761                        LEFT JOIN tbl_companies b ON a.company_id = b.company_id
1762                        LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
1763                        LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
1764                        LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
1765                        LEFT JOIN tbl_sources f ON a.source_id = f.source_id
1766                        LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
1767                        LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
1768                    WHERE a.id = {$id}";
1769
1770        $result = DB::select($query);
1771
1772        if ($sendNotification) {
1773            $this->send_notification(
1774                $commercial->email,
1775                $userId,
1776                $data['quote_id'],
1777                $id,
1778                $status->name,
1779                $commercial->id,
1780                $company->name,
1781                0,
1782                $company->company_id,
1783                $data['internal_quote_id']
1784            );
1785        }
1786
1787        if ($sendApprovalNotification && ($needToSendReminder || is_null($result[0]->for_approval ?? null))) {
1788            $this->send_approval_notification(
1789                $data['amount'],
1790                $data['budget_type_id'],
1791                $data['customer_type_id'],
1792                $approvalMinimumOrderSize,
1793                $data['quote_id'],
1794                $approvalId,
1795                $company->name,
1796                @$data['created_by'] ?? @$data['updated_by'],
1797                $userId,
1798                $approvalForAdd,
1799                $commercial ? $commercial->email : null,
1800                $company->company_id,
1801                'orders',
1802                $approvalIsQuestion,
1803                $approvalQuestionIdsNo,
1804                $approvalN
1805            );
1806        }
1807
1808        if ($sendApprovalMarginNotification && $needToSendReminder) {
1809            $this->send_approval_margin_notification(
1810                $data['amount'],
1811                $data['budget_type_id'],
1812                $data['customer_type_id'],
1813                $approvalMinimumMargin,
1814                $data['quote_id'],
1815                $approvalId,
1816                $company->name,
1817                @$data['created_by'] ?? @$data['updated_by'],
1818                $userId,
1819                $approvalForAdd,
1820                $commercial ? $commercial->email : null,
1821                $approvalInvoiceMargin,
1822                $company->company_id,
1823                'orders'
1824            );
1825        }
1826
1827        if (isset($result['budget_status_id']) && $result['budget_status_id'] == 6) {
1828            $data = [
1829                'id' => $result['id'] ?? null,
1830                'client' => $result['client'] ?? null,
1831                'email' => $result['email'] ?? null,
1832                'phone_number' => $result['phone_number'] ?? null,
1833                'last_follow_up_comment' => $result['last_follow_up_comment'] ?? null,
1834                'quote_id' => $result['quote_id'] ?? null,
1835                'request_date' => date('Y-m-d'),
1836                'updated_by' => 'IA',
1837                'user_id' => $result['user_id'] ?? null,
1838                'commercial' => $result['commercial'] ?? null,
1839                'budget_status_id' => $result['budget_status_id'],
1840                'internal_quote_id' => $result['internal_quote_id'] ?? null,
1841            ];
1842
1843            $ch = curl_init('https://2lsarnb35o6evhgwmfedrsxk3i0lqzzq.lambda-url.eu-west-2.on.aws/checkduplicate');
1844            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1845            curl_setopt($ch, CURLOPT_POST, true);
1846            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
1847            curl_setopt($ch, CURLOPT_HTTPHEADER, [
1848                'Content-Type: application/json',
1849            ]);
1850
1851            $response = curl_exec($ch);
1852            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1853
1854            if (curl_errno($ch)) {
1855                error_log('Error en cURL: '.curl_error($ch));
1856            }
1857
1858            curl_close($ch);
1859        }
1860
1861        Cache::flush();
1862
1863        return response(['message' => 'OK', 'data' => $result, 'for_approval' => $forApproval]);
1864
1865        // } catch (\Exception $e) {
1866        //     return response(['message' => 'KO', 'error' => $e->getMessage()]);
1867        // }
1868
1869    }
1870
1871    /**
1872     * @return array{newData: mixed, oldData: mixed}[]
1873     */
1874    private function compareArrays($data, $quotations): array {
1875        $differences = [];
1876        $attributes = $quotations->getAttributes();
1877
1878        foreach ($data as $field => $valueData) {
1879            if (array_key_exists($field, $attributes)) {
1880                $valueQuotations = $quotations->$field;
1881
1882                $valueData = $this->convertValue($valueData);
1883                $valueQuotations = $this->convertValue($valueQuotations);
1884
1885                if ($valueData !== $valueQuotations) {
1886                    if ($this->isEmpty($valueData) && $this->isEmpty($valueQuotations)) {
1887                        continue;
1888                    }
1889
1890                    $differences[$field] = [
1891                        'newData' => $valueData,
1892                        'oldData' => $valueQuotations,
1893                    ];
1894                }
1895            }
1896        }
1897
1898        return $differences;
1899    }
1900
1901    private function isEmpty($value): bool
1902    {
1903        return is_null($value) || $value === '' || $value === 0 || $value === '0';
1904    }
1905
1906    private function convertValue($value)
1907    {
1908        if ($value === null) {
1909            return null;
1910        }
1911
1912        if (is_numeric($value)) {
1913            return (int) $value;
1914        }
1915
1916        if ($value === 'NULL' || $value === 'null') {
1917            return null;
1918        }
1919
1920        if (is_string($value)) {
1921            if (preg_match('/^(\d{4}-\d{2}-\d{2})( \d{2}:\d{2}:\d{2})?$/', $value, $matches)) {
1922                return $matches[1];
1923            }
1924        }
1925
1926        return $value;
1927    }
1928
1929    protected function callDeleteQuotation($id, $company_id, $userId, $user)
1930    {
1931        $request = new \Illuminate\Http\Request([
1932            'company_id' => $company_id,
1933            'user_id' => $userId,
1934            'updated_by' => $user,
1935            'ids' => [$id],
1936            'filterModel' => [],
1937            'sortModel' => [],
1938            'searchText' => '',
1939            'ids_not_in' => [],
1940        ]);
1941
1942        return $this->delete_quotation($request, true);
1943    }
1944
1945    public function get_quotation($id): ResponseFactory|HttpResponse{
1946
1947        try {
1948
1949            $id = addslashes((string) $id);
1950
1951            $query = "SELECT
1952                        a.id,
1953                        a.quote_id,
1954                        a.company_id,
1955                        b.name company_name,
1956                        a.client,
1957                        c.name client_type,
1958                        c.customer_type_id,
1959                        s.name segment,
1960                        s.segment_id,
1961                        a.request_date,
1962                        a.visit_date,
1963                        a.issue_date,
1964                        a.acceptance_date,
1965                        a.internal_quote_id,
1966                        DATE_FORMAT(a.request_date, '%d/%m/%Y') request_date_translate,
1967                        DATE_FORMAT(a.issue_date, '%d/%m/%Y') issue_date_translate,
1968                        DATE_FORMAT(a.acceptance_date, '%d/%m/%Y') acceptance_date_translate,
1969                        DATE_FORMAT(a.last_follow_up_date, '%d/%m/%Y') last_follow_up_date_translate,
1970                        a.phone_number,
1971                        a.email,
1972                        a.duration,
1973                        a.order_number,
1974                        d.name 'type',
1975                        d.budget_type_id,
1976                        e.name 'status',
1977                        e.budget_status_id,
1978                        f.name source,
1979                        f.source_id,
1980                        a.amount,
1981                        g.name reason_for_not_following_up,
1982                        a.reason_for_not_following_up_id,
1983                        a.reason_for_rejection_id,
1984                        a.last_follow_up_date,
1985                        a.last_follow_up_comment,
1986                        CASE WHEN a.reason_for_rejection_id IS NULL THEN a.reason_for_rejection ELSE h.name END reason_for_rejection,
1987                        a.commercial,
1988                        a.created_by,
1989                        a.created_at,
1990                        a.updated_by,
1991                        a.updated_at,
1992                        a.for_approval,
1993                        a.box_work_g3w,
1994                        a.people_assigned_to_the_job,
1995                        a.duration_of_job_in_days,
1996                        a.estimated_cost_of_materials,
1997                        a.budget_margin_enabled,
1998                        a.question_enabled,
1999                        a.cost_of_labor,
2000                        a.total_cost_of_job,
2001                        CASE WHEN a.budget_margin_enabled > 0 THEN a.invoice_margin ELSE NULL END invoice_margin,
2002                        CASE WHEN a.budget_margin_enabled > 0 THEN a.margin_for_the_company ELSE NULL END margin_for_the_company,
2003                        a.margin_on_invoice_per_day_per_worker,
2004                        a.revenue_per_date_per_worked,
2005                        a.commission_cost,
2006                        a.commission_pct,
2007                        a.gross_margin,
2008                        a.labor_percentage,
2009                        a.question_ids,
2010                        a.question_ids_no,
2011                        a.approved_at,
2012                        a.approved_by,
2013                        a.rejected_at,
2014                        a.rejected_by,
2015                        a.approved_at_v2,
2016                        a.approved_by_v2,
2017                        a.rejected_at_v2,
2018                        a.rejected_by_v2,
2019                        a.accepted_at,
2020                        a.accepted_by,
2021                        a.is_validated,
2022                        a.resource_id,
2023                        a.sync_import,
2024                        a.sync_import_edited,
2025                        a.g3w_warning,
2026                        a.g3w_warning_fields,
2027                        a.id_solicitud_duplicity,
2028                        a.x_status
2029                    FROM
2030                        tbl_quotations a
2031                        LEFT JOIN tbl_companies b ON a.company_id = b.company_id
2032                        LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
2033                        LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
2034                        LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
2035                        LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
2036                        LEFT JOIN tbl_sources f ON a.source_id = f.source_id
2037                        LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
2038                        LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
2039                    WHERE a.id = {$id}";
2040
2041            $result = DB::select($query);
2042
2043            Cache::flush();
2044
2045            return response(['message' => 'OK', 'data' => $result]);
2046
2047        } catch (\Exception $e) {
2048            report(AppException::fromException($e, 'GET_QUOTATION_EXCEPTION'));
2049            return response(['message' => 'KO', 'error' => $e->getMessage()]);
2050        }
2051    }
2052
2053    public function get_quotation_log($id)
2054    {
2055        return TblQuotationsLog::where('quotation_id', $id)->get();
2056    }
2057
2058    public function send_notification($toEmail = null, $userId = null, $quoteId = null, $id = null, $status = null, $sendUserId = null, $companyName = null, $action = null, $companyId = null, $g3wQuoteId = null): void{
2059
2060        $user = TblUsers::where('id', $userId)->first();
2061
2062        $imgpath = File::get('fireservicetitan.png');
2063
2064        $url = config('app.frontend_url') . "orders/{$id}?company_id={$companyId}";
2065        $href = "<a href='{$url}'>{$quoteId}</a>";
2066
2067        $body = '';
2068        $subject = '';
2069
2070        if ($g3wQuoteId) {
2071            $quoteId = $quoteId." - G3W #{$g3wQuoteId}";
2072        }
2073
2074        if ($action == 1) {
2075            $body = __('language.email_notification.body_created');
2076            $body = str_replace('{{creator}}', $user->name, $body);
2077            $subject = str_replace('{{quote_id}}', $quoteId, __('language.email_notification.subject_created'));
2078        } else {
2079            $body = __('language.email_notification.body_assigned');
2080            $body = str_replace('{{assignee}}', $user->name, $body);
2081            $subject = str_replace('{{quote_id}}', $quoteId, __('language.email_notification.subject_assigned'));
2082        }
2083
2084        $body = str_replace('{{quote_id}}', $href, $body);
2085        $body = str_replace('{{company}}', $companyName, $body);
2086        $body = str_replace('{{status}}', $status, $body);
2087
2088        $content = $body;
2089
2090        $body .= '<p>Fire Service Titan</p>';
2091        $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
2092
2093        $html = '<!DOCTYPE html>';
2094        $html .= '<html>';
2095        $html .= '<head>';
2096        $html .= '<meta charset="UTF-8">';
2097        $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
2098        $html .= '</head>';
2099        $html .= '<body>';
2100        $html .= $body;
2101        $html .= '</body>';
2102        $html .= '</html>';
2103
2104        if ($toEmail != null) {
2105            $email = new \SendGrid\Mail\Mail;
2106
2107            if(config('services.sendgrid.staging')){
2108                $toEmail = $user->email;
2109            }
2110
2111            $email->setFrom('fire@fire.es', 'Fire Service Titan');
2112            $email->setSubject($subject);
2113            $email->addTo($toEmail);
2114            $email->addContent('text/html', $html);
2115
2116            $email->addAttachment(
2117                $imgpath,
2118                'image/png',
2119                'fireservicetitan.png',
2120                'inline',
2121                'fireservicetitan'
2122            );
2123
2124            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
2125
2126            $response = $sendgrid->send($email);
2127            if ($response->statusCode() == 202) {
2128                Log::channel('email_log')->info('ID:'.$quoteId.' : '.$status.' : '.$toEmail.' - EMAIL NOTIFICATION SENT');
2129                $this->addUpdateLog($id, $sendUserId, 'send_notification', null, null, 5);
2130
2131                TblNotifications::create(
2132                    [
2133                        'user_id' => $sendUserId,
2134                        'content' => $content,
2135                        'is_open' => 1,
2136                        'created_by' => 'System',
2137                        'link' => $url
2138                    ]
2139                );
2140            } else {
2141                $error = true;
2142                Log::channel('email_log')->error('ID:'.$quoteId.' : '.$status.' : '.$toEmail.' - '.$response->body());
2143            }
2144        }
2145
2146    }
2147
2148    public function delete_quotation(Request $request, $isFromDeleteCall = false): ResponseFactory|HttpResponse{
2149
2150        try {
2151
2152            $data = $request->all();
2153            $result = [];
2154
2155            if (count($data['ids']) > 1) {
2156                $u = TblUsers::where('id', $data['user_id'])->first();
2157
2158                if ($u->role_id != 1) {
2159                    return response(['message' => 'KO', 'error' => 'more_than_one']);
2160                }
2161            }
2162
2163            $r = new Request([
2164                'filterModel' => $data['filterModel'],
2165                'sortModel' => $data['sortModel'],
2166                'start' => 0,
2167                'end' => 999999999,
2168                'company_id' => $data['company_id'],
2169                'user_id' => $data['user_id'],
2170                'ids' => $data['ids'],
2171                'searchText' => $data['searchText'],
2172                'ids_not_in' => $data['ids_not_in'],
2173            ]);
2174
2175            $d = [];
2176
2177            $result = $this->list_quotations($r);
2178            $result = $result->original['data'];
2179
2180            $outputArray = [];
2181
2182            foreach ($result as $item) {
2183                if (isset($item->id)) {
2184                    $outputArray[] = $item->id;
2185                }
2186            }
2187
2188            $ids = implode(',', $outputArray);
2189
2190            if ($outputArray) {
2191
2192                TblQuotations::whereIn('id', $outputArray)->update(
2193                    [
2194                        'updated_at' => date('Y-m-d H:i:s'),
2195                        'updated_by' => $data['updated_by'],
2196                        'for_add' => $isFromDeleteCall ? 2 : ($data['for_add'] ?? 0),
2197                        'reason_id' => ($data['reason_id'] ?? null),
2198                        'reason_for_deletion' => ($data['reason_for_deletion'] ?? null),
2199                    ]
2200                );
2201
2202                $query = "INSERT INTO tbl_quotations_deleted
2203                        SELECT * FROM tbl_quotations WHERE id IN ({$ids})";
2204
2205                DB::select($query);
2206
2207                TblQuotations::whereIn('id', $outputArray)->delete();
2208                TblFiles::whereIn('quotation_id', $outputArray)->delete();
2209            }
2210
2211            foreach ($outputArray as $id) {
2212                $this->addUpdateLog($id, $data['user_id'], 'delete', null, null, 6);
2213            }
2214
2215            Cache::flush();
2216
2217            return response(['message' => 'OK', 'data' => $result]);
2218
2219        } catch (\Exception $e) {
2220            report(AppException::fromException($e, 'DELETE_QUOTATION_EXCEPTION'));
2221            return response(['message' => 'KO', 'error' => $e->getMessage()]);
2222        }
2223
2224    }
2225
2226    function getBlacklistEmails(): array{
2227        return [
2228            "no\.no",
2229            "tiene\.email",
2230            "test\.com",
2231            "no\.tiene",
2232            "prueba\.com",
2233            "nomail@nomail\.com",
2234            "notiene@notiene\.notiene",
2235        ];
2236    }
2237
2238    public function validate_email(Request $request)
2239    {
2240        $email = $request->input('email');
2241
2242        if (! $email || trim($email) === '') {
2243            return response()->json([
2244                'valid' => false,
2245                'reason' => 'El email está vacío',
2246            ]);
2247        }
2248
2249        $emailPattern = '/^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}$/';
2250        $emails = explode(',', $email);
2251
2252        foreach ($emails as $e) {
2253            $trimmed = trim($e);
2254
2255            if (! preg_match($emailPattern, $trimmed)) {
2256                return response()->json([
2257                    'valid' => false,
2258                    'reason' => "El email '{$trimmed}' tiene un formato incorrecto",
2259                ]);
2260            }
2261
2262            if ($this->isBlacklistedEmail($trimmed)) {
2263                return response()->json([
2264                    'valid' => false,
2265                    'reason' => "El email '{$trimmed}' está en la lista de direcciones no válidas",
2266                ]);
2267            }
2268        }
2269
2270        return response()->json([
2271            'valid' => true,
2272            'reason' => null,
2273        ]);
2274    }
2275
2276    private function isBlacklistedEmail(?string $email): bool
2277    {
2278        if (! $email || trim($email) === '') {
2279            return true;
2280        }
2281
2282        $pattern = '/^no@|^nomail@nomail|^notiene@notiene|tiene\.email|test\.com|prueba\.com/i';
2283
2284        $emails = explode(',', $email);
2285        foreach ($emails as $e) {
2286            if (preg_match($pattern, trim($e))) {
2287                return true;
2288            }
2289        }
2290
2291        return false;
2292    }
2293
2294    public function list_quotations(Request $request)
2295    {
2296
2297        // try {
2298
2299        $data = $request->all();
2300        $companyId = addslashes((string) $data['company_id']);
2301        $userId = addslashes((string) $data['user_id']);
2302        $filter = $data['filterModel'];
2303        $sort = $data['sortModel'];
2304        $result = [];
2305        $subquery = "";
2306        $where = "";
2307        $having = "";
2308        $orderBy = "";
2309        $start = addslashes((string) $data['start']);
2310        $end = addslashes((string) $data['end']);
2311        $totalRowCount = 0;
2312        $withFilters = "";
2313        $logFilter = @$data['log_filter'];
2314        $isInvalidEmail = (isset($data['invalid_email']) && $data['invalid_email'] == 1);
2315        $isFollowUp = (isset($data['is_follow_up']) && $data['is_follow_up'] == 1);
2316
2317        $filterType = [
2318            'contains' => "LIKE '%[value]%'",
2319            'notContains' => "NOT LIKE '%[value]%'",
2320            'equals' => "= '[value]'",
2321            'notEqual' => "<> '[value]'",
2322            'startsWith' => "LIKE '[value]%'",
2323            'endsWith' => "LIKE '%[value]'",
2324            'blank' => 'IS NULL',
2325            'notBlank' => 'IS NOT NULL',
2326            'lessThan' => '< [value]',
2327            'lessThanOrEqual' => '<= [value]',
2328            'greaterThan' => '> [value]',
2329            'greaterThanOrEqual' => '>= [value]',
2330            'inRange' => 'BETWEEN [value1] AND [value2]',
2331            'in' => 'IN ([value])',
2332        ];
2333
2334        /*if(isset($data['internal_quote_id']) && count($data['internal_quote_id']) > 0){
2335            $internalIds = implode(",", $data['internal_quote_id']);
2336            $where = " AND a.internal_quote_id IN ({$internalIds}) ";
2337        }*/
2338
2339        if (isset($data['ids']) && count($data['ids']) > 0) {
2340            $quoteIds = implode(',', $data['ids']);
2341            $where .= " AND a.id IN ({$quoteIds}";
2342        }
2343
2344        if (isset($data['ids_not_in']) && count($data['ids_not_in']) > 0) {
2345            $quoteIds = implode(',', $data['ids_not_in']);
2346            $where .= " AND a.id NOT IN ({$quoteIds}";
2347        }
2348
2349        $lasLeftJoin = '';
2350        $whereBlocked = '';
2351
2352        if (isset($data['last_follow_up_date']) && ! empty($data['last_follow_up_date'])) {
2353            if ($data['last_follow_up_date'] == 1) {
2354
2355                $lasLeftJoin = " LEFT JOIN (
2356                        SELECT
2357                          a.id,
2358                          SUBSTRING_INDEX(
2359                            SUBSTRING_INDEX(a.email, ',', n.digit + 1),
2360                            ',',
2361                            -1
2362                          ) AS email_domain
2363                        FROM
2364                          tbl_quotations a
2365                          INNER JOIN (
2366                            SELECT
2367                              0 AS digit
2368                            UNION ALL
2369                            SELECT
2370                              1
2371                            UNION ALL
2372                            SELECT
2373                              2
2374                            UNION ALL
2375                            SELECT
2376                              3
2377                            UNION ALL
2378                            SELECT
2379                              4
2380                            UNION ALL
2381                            SELECT
2382                              5
2383                            UNION ALL
2384                            SELECT
2385                              6
2386                            UNION ALL
2387                            SELECT
2388                              7
2389                            UNION ALL
2390                            SELECT
2391                              8
2392                            UNION ALL
2393                            SELECT
2394                              9
2395                          ) n ON LENGTH(
2396                            REPLACE(a.email, ',', '')
2397                          ) <= LENGTH(a.email)- n.digit
2398                          GROUP BY a.id
2399                      ) temp ON a.id = temp.id ";
2400
2401                $whereBlocked = " AND a.last_follow_up_date < NOW()
2402                            AND a.budget_status_id IN (2)
2403                            AND a.email IS NOT NULL
2404                            AND a.email <> ''
2405                            AND NOT EXISTS (
2406                                SELECT
2407                                1
2408                                FROM
2409                                tbl_blocked_domains bd
2410                                WHERE
2411                                temp.email_domain LIKE CONCAT('%', bd.domain, '%')
2412                                AND bd.company_id = a.company_id
2413                            )
2414                            AND a.last_follow_up_date IS NOT NULL
2415                            AND a.reason_for_not_following_up_id IS NULL
2416                            AND a.last_follow_up_date > 0
2417                            AND a.total_sent < b.limit_reminder_emails
2418                            AND a.for_add = 0 ";
2419            }
2420        }
2421
2422        if (isset($data['visit_date']) && ! empty($data['visit_date'])) {
2423            if ($data['visit_date'] == 1) {
2424                $where = " AND DATE_FORMAT(a.visit_date, '%Y-%m-%d') <= DATE_FORMAT(NOW(), '%Y-%m-%d') ";
2425            }
2426        }
2427
2428        if ($companyId != 0) {
2429            $where .= " AND a.company_id = {$companyId} ";
2430        } elseif ($this->companyId) {
2431            $where .= " AND a.company_id IN ({$this->companyId}";
2432        }
2433
2434        $matchScoreCol = '';
2435        $matchScoreOrderBy = '';
2436
2437        if (isset($data['searchText']) && $data['searchText'] != null) {
2438
2439            $availableParameters = [
2440                'a.quote_id',
2441                'a.internal_quote_id',
2442                'a.box_work_g3w',
2443                's.name',
2444                'b.name',
2445                'a.client',
2446                'c.name',
2447                'a.phone_number',
2448                'a.email',
2449                'a.order_number',
2450                'a.request_date',
2451                'a.issue_date',
2452                'a.acceptance_date',
2453                'a.created_at',
2454                'a.updated_at',
2455                'a.rejected_at',
2456                'a.accepted_at',
2457                'd.name',
2458                'e.name',
2459                'f.name',
2460                'a.amount',
2461                'g.name',
2462                'a.last_follow_up_comment',
2463                'a.x_status',
2464                'h.name',
2465                'a.commercial',
2466                'a.user_commercial_by_g3w',
2467                'a.user_create_by_g3w',
2468                'a.created_by',
2469                'a.updated_by',
2470                'a.approved_by',
2471                'a.rejected_by',
2472                'a.accepted_by',
2473                'a.sync_import',
2474                'a.sync_import_edited',
2475                'a.g3w_warning',
2476            ];
2477
2478            $searchText = addslashes((string) $data['searchText']);
2479            $searchTextArray = explode(" ", $searchText);
2480
2481            $searchArray = [];
2482            $splitSearchArray = [];
2483            $matchScoreArray = [];
2484            $sc = 1;
2485            foreach ($availableParameters as $field) {
2486                if ($field == 'a.client' || $field == 'a.amount' || $field == 'a.created_at') {
2487                    $sc = 3;
2488                } elseif ($field == 'a.acceptance_date') {
2489                    $sc = 2;
2490                } else {
2491                    $sc = 1;
2492                }
2493
2494                $l = "{$field} LIKE '%{$searchText}%'";
2495                if ($field == 'a.last_follow_up_comment') {
2496                    $l = "{$field} = '{$searchText}'";
2497                } else {
2498
2499                    $d = "IFNULL((LENGTH(LOWER({$field})) - LENGTH(REPLACE(LOWER({$field}), LOWER('{$searchText}'), ''))) / LENGTH(LOWER('{$searchText}')), 0) * {$sc}";
2500
2501                    if (count($searchTextArray) > 1) {
2502                        foreach ($searchTextArray as $word) {
2503                            if (! is_numeric($word)) {
2504                                $d .= " + IFNULL((LENGTH(LOWER({$field})) - LENGTH(REPLACE(LOWER({$field}), LOWER('{$word}'), ''))) / LENGTH(LOWER('{$word}')), 0) * {$sc}";
2505                            }
2506                        }
2507                    }
2508
2509                    array_push($matchScoreArray, $d);
2510                }
2511
2512                if (is_numeric($searchText)) {
2513                    array_push($searchArray, "({$l} OR {$field} = CAST('{$searchText}' AS UNSIGNED))");
2514                } else {
2515                    array_push($searchArray, "({$l} OR DATE_FORMAT({$field}, '%d/%m/%Y') = DATE_FORMAT(STR_TO_DATE('{$searchText}', '%d/%m/%Y'), '%d/%m/%Y'))");
2516                }
2517
2518                if (count($searchTextArray) > 1) {
2519                    foreach ($searchTextArray as $word) {
2520
2521                        $l = "{$field} LIKE '%{$word}%'";
2522                        if ($field == 'a.last_follow_up_comment') {
2523                            $l = "{$field} = '{$word}'";
2524                        }
2525
2526                        if (is_numeric($word)) {
2527                            array_push($splitSearchArray, "{$l} OR {$field} = CAST('{$word}' AS UNSIGNED)");
2528                        } else {
2529                            array_push($splitSearchArray, "{$l} OR DATE_FORMAT({$field}, '%d/%m/%Y') = DATE_FORMAT(STR_TO_DATE('{$word}', '%d/%m/%Y'), '%d/%m/%Y')");
2530                        }
2531                    }
2532                }
2533
2534                $sc = 1;
2535            }
2536
2537            if (count($splitSearchArray) > 0) {
2538                $splitSearchArray = implode(' OR ', $splitSearchArray);
2539                $splitSearchArray = " OR ({$splitSearchArray}";
2540            } else {
2541                $splitSearchArray = '';
2542            }
2543
2544            $searchArray = implode(' OR ', $searchArray);
2545            $matchScoreArray = implode(',', $matchScoreArray);
2546            $matchScoreCol = ", GREATEST({$matchScoreArray}) match_score";
2547            $matchScoreOrderBy = 'match_score DESC,';
2548            $where .= " AND ({$searchArray} {$splitSearchArray})";
2549        }
2550
2551        if (count($sort) > 0) {
2552            $field = $sort[0]['colId'];
2553            $sortBy = $sort[0]['sort'];
2554
2555            if (strpos($field, 'translate') !== false) {
2556                $field = str_replace('_translate', '', $field);
2557            } else {
2558                if ($field == 'client_type') {
2559                    $field = 'c.name';
2560                } elseif ($field == 'segment') {
2561                    $field = 's.name';
2562                } elseif ($field == 'type') {
2563                    $field = 'd.name';
2564                } elseif ($field == 'status') {
2565                    $field = 'e.name';
2566                } elseif ($field == 'source') {
2567                    $field = 'g.name';
2568                } elseif ($field == 'reason_for_not_following_up') {
2569                    $field = 'g.name';
2570                } elseif ($field == 'reason_for_rejection') {
2571                    $field = 'h.name';
2572                } elseif ($field == 'amount') {
2573                    $field = 'CAST(a.amount AS DOUBLE)';
2574                } elseif ($field == 'duration') {
2575                    $field = 'CAST(a.duration AS DOUBLE)';
2576                } elseif ($field == 'quote_id' || $field == 'internal_quote_id') {
2577                    $field = "CAST(a.{$field} AS DOUBLE)";
2578                } elseif ($field == 'company_name') {
2579                    $field = 'b.name';
2580                }
2581
2582            }
2583
2584            if ($matchScoreOrderBy) {
2585                $matchScoreOrderBy = ', match_score DESC';
2586            }
2587
2588            $orderBy = " ORDER BY {$field} {$sortBy} {$matchScoreOrderBy}";
2589        } else {
2590            $orderBy = " ORDER BY {$matchScoreOrderBy} a.id DESC";
2591        }
2592
2593        foreach ($filter as $key => $data) {
2594            if (strpos($key, 'translate') !== false) {
2595
2596                $field = str_replace('_translate', '', $key);
2597                if ($field == 'created_at') {
2598                    $field = 'a.created_at';
2599                } elseif ($field == 'last_follow_up_date') {
2600                    $field = 'a.last_follow_up_date';
2601                } elseif ($field == 'issue_date') {
2602                    $field = 'a.issue_date';
2603                } elseif ($field == 'request_date') {
2604                    $field = 'a.request_date';
2605                } elseif ($field == 'acceptance_date') {
2606                    $field = 'a.acceptance_date';
2607                } elseif ($field == 'internal_quote_id') {
2608                    $field = 'a.internal_quote_id';
2609                }
2610
2611                $whereDates = '';
2612                $z = 0;
2613
2614                if (isset($data['filters']) && ! empty($data['filters'])) {
2615                    $yearsMonths = [];
2616                    $yearsWeeks = [];
2617                    $yearsMW = [];
2618                    foreach ($data['filters'] as $yearKey => $yearData) {
2619
2620                        if ($yearData['isChecked']) {
2621
2622                            if ($yearData['isCheckedAllMonths'] && $yearData['isCheckedAllWeeks']) {
2623                                if ($z > 0) {
2624                                    $whereDates .= " OR YEAR($field) = {$yearKey} ";
2625                                } else {
2626                                    $whereDates .= " YEAR($field) = {$yearKey} ";
2627                                }
2628                            } else {
2629
2630                                if ($yearData['isCheckedAllWeeks']) {
2631                                    for ($i = 0; $i < count($yearData['weeks']); $i++) {
2632                                        if ($yearData['weeks'][$i]['isChecked']) {
2633                                            array_push($yearsMW, " YEARWEEK({$field}, 1) = '{$yearKey}{$yearData['weeks'][$i]['value']}");
2634                                        }
2635                                    }
2636                                }
2637
2638                                if ($yearData['isCheckedAllMonths']) {
2639                                    for ($i = 0; $i < count($yearData['months']); $i++) {
2640                                        if ($yearData['months'][$i]['isChecked']) {
2641                                            array_push($yearsMW, " DATE_FORMAT({$field}, '%Y%m') = '{$yearKey}{$yearData['months'][$i]['value']}");
2642                                        }
2643                                    }
2644                                }
2645
2646                                if (! $yearsMW) {
2647                                    if (! $yearData['isCheckedAllMonths']) {
2648                                        if ($z > 0) {
2649                                            $whereDates .= " OR YEAR($field) = {$yearKey} ";
2650                                        } else {
2651                                            $whereDates .= " YEAR($field) = {$yearKey} ";
2652                                        }
2653                                    }
2654                                }
2655                            }
2656
2657                            $z++;
2658                        }
2659
2660                    }
2661
2662                    if ($yearsMW) {
2663                        $whereDates .= implode(' OR ', $yearsMW);
2664                    }
2665                }
2666
2667                $whereDataUptoToday = '';
2668                if (isset($data['isDataUptoToday'])) {
2669                    if ($data['isDataUptoToday']) {
2670                        $whereDates = '';
2671                        $whereDataUptoToday .= " AND {$field} < NOW() AND {$field} > 0 ";
2672                    }
2673                }
2674
2675                $whereBlanks = '';
2676                if (isset($data['isBlanks'])) {
2677                    if ($data['isBlanks']) {
2678                        $conj = 'OR';
2679                        if ($whereDates == '') {
2680                            $conj = '';
2681                        }
2682                        $whereBlanks .= " {$conj} {$field} IS NULL ";
2683                    } else {
2684                        $conj = 'AND';
2685                        if ($whereDates == '') {
2686                            $conj = '';
2687                        }
2688                        $whereBlanks .= " {$conj} {$field} IS NOT NULL ";
2689                    }
2690                }
2691
2692                $where .= " AND ({$whereDates} {$whereBlanks} {$whereDataUptoToday}";
2693            } else {
2694                if ($data['filterType'] == 'number') {
2695                    if (array_key_exists('operator', $data)) {
2696                        if ($data['condition1']['type'] != 'blank' && $data['condition2']['type'] != 'notBlank') {
2697                            $data['condition1']['filter'] = addslashes($data['condition1']['filter']);
2698                            $data['condition2']['filter'] = addslashes($data['condition2']['filter']);
2699
2700                            if ($data['condition1']['type'] == 'inRange') {
2701                                $data['condition1']['filterTo'] = addslashes($data['condition1']['filterTo']);
2702                                $inRange = str_replace('[value1]', $data['condition1']['filter'], $filterType['inRange']);
2703                                $val1 = str_replace('[value2]', $data['condition1']['filterTo'], $inRange);
2704                            } else {
2705                                $val1 = str_replace('[value]', $data['condition1']['filter'], $filterType[$data['condition1']['type']]);
2706                            }
2707
2708                            if ($data['condition2']['type'] == 'inRange') {
2709                                $data['condition2']['filterTo'] = addslashes($data['condition2']['filterTo']);
2710                                $inRange = str_replace('[value1]', $data['condition2']['filter'], $filterType['inRange']);
2711                                $val2 = str_replace('[value2]', $data['condition2']['filterTo'], $inRange);
2712                            } else {
2713                                $val2 = str_replace('[value]', $data['condition2']['filter'], $filterType[$data['condition2']['type']]);
2714                            }
2715
2716                        } else {
2717                            $val1 = $filterType[$data['condition1']['type']];
2718                            $val2 = $filterType[$data['condition2']['type']];
2719                        }
2720
2721                        $where .= " AND a.{$key} {$val1} {$data['operator']} a.{$key} {$val2} ";
2722                    } else {
2723                        if ($data['type'] != 'blank' && $data['type'] != 'notBlank') {
2724                            $data['filter'] = addslashes($data['filter']);
2725
2726                            if ($data['type'] == 'inRange') {
2727                                $data['filterTo'] = addslashes($data['filterTo']);
2728                                $inRange = str_replace('[value1]', $data['filter'], $filterType['inRange']);
2729                                $val = str_replace('[value2]', $data['filterTo'], $inRange);
2730                            } else {
2731                                $val = str_replace('[value]', $data['filter'], $filterType[$data['type']]);
2732                            }
2733                        } else {
2734                            $val = $filterType[$data['type']];
2735                        }
2736
2737                        $where .= " AND a.{$key} {$val} ";
2738                    }
2739                }
2740
2741                if ($data['filterType'] == 'text') {
2742                    if ($key == 'id') {
2743                        continue;
2744                    }
2745
2746                    if (array_key_exists('operator', $data)) {
2747                        $val1 = '';
2748                        $val2 = '';
2749                        if ($data['condition1']['type'] != 'blank' && $data['condition2']['type'] != 'notBlank') {
2750                            $data['condition1']['filter'] = addslashes($data['condition1']['filter']);
2751                            $val1 = str_replace('[value]', $data['condition1']['filter'], $filterType[$data['condition1']['type']]);
2752                        }
2753
2754                        if ($data['condition2']['type'] != 'blank' && $data['condition2']['type'] != 'notBlank') {
2755                            $data['condition2']['filter'] = addslashes($data['condition2']['filter']);
2756                            $val2 = str_replace('[value]', $data['condition2']['filter'], $filterType[$data['condition2']['type']]);
2757                        }
2758
2759                        $where .= " AND {$key} {$val1} {$data['operator']} {$key} {$val2} ";
2760                    } else {
2761
2762                        $type = $data['type'];
2763                        $filter = $data['filter'];
2764
2765                        if (($type === 'in' || $type === 'contains') && strpos($filter, ',') !== false) {
2766                            $values = explode(',', $filter);
2767                            $escaped = array_map('addslashes', $values);
2768                            $val = "IN ('".implode("','", $escaped)."')";
2769                        } elseif ($type !== 'blank' && $type !== 'notBlank') {
2770                            $data['filter'] = addslashes($data['filter']);
2771                            $val = str_replace('[value]', $data['filter'], $filterType[$type]);
2772                        } else {
2773                            $val = $filterType[$type];
2774                        }
2775
2776                        $where .= " AND {$key} {$val} ";
2777
2778                    }
2779                }
2780
2781                if ($data['filterType'] == 'set') {
2782                    $statusName = $key;
2783
2784                    if ($key == 'client_type') {
2785                        $statusName = 'c.name';
2786                    } elseif ($key == 'segment') {
2787                        $statusName = 's.name';
2788                    } elseif ($key == 'type') {
2789                        $statusName = 'd.name';
2790                    } elseif ($key == 'status') {
2791                        $statusName = 'e.name';
2792                    } elseif ($key == 'source') {
2793                        $statusName = 'f.name';
2794                    } elseif ($key == 'reason_for_not_following_up') {
2795                        $statusName = 'g.name';
2796                    } elseif ($key == 'reason_for_rejection') {
2797                        $statusName = 'h.name';
2798                    } elseif ($key == 'created_by') {
2799                        $statusName = 'a.created_by';
2800                    } elseif ($key == 'has_attachment') {
2801                        $statusName = 'a.has_attachment';
2802                        if ($data['values']) {
2803                            foreach ($data['values'] as $k => $v) {
2804                                if ($v == 'No') {
2805                                    $data['values'][$k] = 0;
2806                                } else {
2807                                    $data['values'][$k] = 1;
2808                                }
2809                            }
2810                        }
2811                    } elseif ($key == 'for_approval') {
2812                        $statusName = 'a.for_approval';
2813                        if ($data['values']) {
2814                            foreach ($data['values'] as $k => $v) {
2815                                if ($v == 'No') {
2816                                    $data['values'][$k] = 0;
2817                                } else {
2818                                    $data['values'][$k] = 1;
2819                                }
2820                            }
2821                        }
2822                    } elseif ($key == 'sync_import') {
2823                        $statusName = 'a.sync_import';
2824                        if ($data['values']) {
2825                            foreach ($data['values'] as $k => $v) {
2826                                if ($v == 'Manual') {
2827                                    $data['values'][$k] = 0;
2828                                } else {
2829                                    $data['values'][$k] = 1;
2830                                }
2831                            }
2832                        }
2833                    } elseif ($key == 'g3w_warning') {
2834                        $statusName = 'a.g3w_warning';
2835                        if ($data['values']) {
2836                            foreach ($data['values'] as $k => $v) {
2837                                if ($v == 'No') {
2838                                    $data['values'][$k] = 0;
2839                                } else {
2840                                    $data['values'][$k] = 1;
2841                                }
2842                            }
2843                        }
2844                    } elseif ($key == 'company_name') {
2845                        $statusName = 'b.name';
2846                    }
2847
2848                    $val = implode("','", $data['values']);
2849
2850                    if (in_array(null, $data['values'], true)) {
2851                        $where .= " AND ({$statusName} IN ('{$val}') OR {$statusName} IS NULL) ";
2852                    } else {
2853                        $where .= " AND {$statusName} IN ('{$val}') ";
2854                    }
2855                }
2856            }
2857        }
2858
2859        $whereSendToClient = $where;
2860
2861        $offset = $start;
2862        $limit = $end - $start;
2863
2864        $subquery = ",(SELECT can_write FROM tbl_company_users WHERE company_id = a.company_id AND user_id = {$userId}) can_write";
2865
2866        // Quotations accepted without acceptance_date
2867        // Quotations with state "No encontrado" or "Estado no reconocido en FST"
2868        // Quotations with not comercial in out database
2869        // Phone number on null
2870        // Source on null
2871        // Client name on null
2872        // Budget Type on null
2873        if (isset($data['g3w_warning'])) {
2874            $g3w_warning = $data['g3w_warning'] == 'No' ? 0 : 1;
2875            /*$where .= "
2876            AND a.sync_import = 1
2877                AND (
2878                    a.budget_status_id IN (13, 14)
2879                    OR (
2880                        a.commercial IS NULL
2881                        OR NOT EXISTS (
2882                            SELECT 1 FROM tbl_users u WHERE u.name = a.commercial
2883                        )
2884                        OR a.phone_number IS NULL
2885                        OR a.source_id IS NULL
2886                        OR a.budget_type_id IS NULL
2887                        OR (a.client IS NULL OR TRIM(a.client) = '')
2888                        OR (a.email IS NULL OR TRIM(a.email) = '')
2889                    )
2890                )
2891             ";*/
2892            $where .= '
2893                AND (a.sync_import = 1 OR a.sync_import_edited = 1)
2894                AND a.g3w_warning = '.$g3w_warning;
2895        }
2896
2897        if ($isInvalidEmail) {
2898            $blacklist = implode('|', $this->getBlacklistEmails());
2899
2900            $where .= "
2901                AND
2902                (
2903                    a.x_status IN ('Error','Error - Bounce','Error - Spam')
2904                    OR (
2905                        a.email IS NULL
2906                        OR TRIM(a.email) = ''
2907                        OR a.email NOT REGEXP '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}'
2908                        OR a.email REGEXP '($blacklist)'
2909                    )
2910                )
2911                AND a.budget_status_id IN(1, 2, 11, 17, 21, 22)";
2912        }
2913
2914        if ($isFollowUp) {
2915            $blacklist = implode('|', $this->getBlacklistEmails());
2916
2917            $where .= "
2918                AND (
2919                    a.email IS NOT NULL 
2920                    AND TRIM(a.email) != '' 
2921                    AND a.email REGEXP '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}'
2922                    AND a.email NOT REGEXP '($blacklist)'
2923                )";
2924        }
2925
2926        $query = "SELECT
2927                        a.id,
2928                        a.quote_id,
2929                        a.internal_quote_id,
2930                        a.company_id,
2931                        b.name company_name,
2932                        a.client,
2933                        c.name client_type,
2934                        c.customer_type_id,
2935                        s.name segment,
2936                        s.segment_id,
2937                        a.request_date,
2938                        a.visit_date,
2939                        a.issue_date,
2940                        a.acceptance_date,
2941                        a.internal_quote_id,
2942                        DATE_FORMAT(a.request_date, '%d/%m/%Y') request_date_translate,
2943                        DATE_FORMAT(a.issue_date, '%d/%m/%Y') issue_date_translate,
2944                        DATE_FORMAT(a.acceptance_date, '%d/%m/%Y') acceptance_date_translate,
2945                        DATE_FORMAT(a.last_follow_up_date, '%d/%m/%Y') last_follow_up_date_translate,
2946                        DATE_FORMAT(a.created_at, '%d/%m/%Y') created_at_translate,
2947                        DATE_FORMAT(a.accepted_at, '%d/%m/%Y') accepted_at_translate,
2948                        a.phone_number,
2949                        a.email,
2950                        a.duration,
2951                        a.order_number,
2952                        d.name 'type',
2953                        d.budget_type_id,
2954                        e.name 'status',
2955                        e.budget_status_id,
2956                        f.name as source,
2957                        f.source_id,
2958                        a.amount,
2959                        g.name reason_for_not_following_up,
2960                        a.reason_for_not_following_up_id,
2961                        a.reason_for_rejection_id,
2962                        a.last_follow_up_date,
2963                        a.last_follow_up_comment,
2964                        CASE WHEN a.reason_for_rejection_id IS NULL THEN a.reason_for_rejection ELSE h.name END reason_for_rejection,
2965                        a.commercial,
2966                        a.user_commercial_by_g3w,
2967                        a.user_create_by_g3w,
2968                        a.created_by,
2969                        a.created_at,
2970                        a.updated_by,
2971                        a.updated_at,
2972                        a.total_sent,
2973                        a.has_attachment,
2974                        a.for_approval,
2975                        a.box_work_g3w,
2976                        a.people_assigned_to_the_job,
2977                        a.duration_of_job_in_days,
2978                        a.estimated_cost_of_materials,
2979                        a.budget_margin_enabled,
2980                        a.question_enabled,
2981                        a.cost_of_labor,
2982                        a.total_cost_of_job,
2983                        CASE WHEN a.budget_margin_enabled > 0 THEN a.invoice_margin ELSE NULL END invoice_margin,
2984                        CASE WHEN a.budget_margin_enabled > 0 THEN a.margin_for_the_company ELSE NULL END margin_for_the_company,
2985                        a.margin_on_invoice_per_day_per_worker,
2986                        a.revenue_per_date_per_worked,
2987                        a.commission_cost,
2988                        a.commission_pct,
2989                        a.gross_margin,
2990                        a.labor_percentage,
2991                        a.question_ids,
2992                        a.question_ids_no,
2993                        a.approved_at,
2994                        a.approved_by,
2995                        a.rejected_at,
2996                        a.rejected_by,
2997                        a.approved_at_v2,
2998                        a.approved_by_v2,
2999                        a.rejected_at_v2,
3000                        a.rejected_by_v2,
3001                        a.accepted_at,
3002                        a.accepted_by,
3003                        a.is_validated,
3004                        a.resource_id,
3005                        a.x_status,
3006                        a.likehood,
3007                        a.sync_import,
3008                        a.sync_import_edited,
3009                        a.g3w_warning,
3010                        a.g3w_warning_fields,
3011                        a.id_solicitud_duplicity,
3012                        SUBSTRING_INDEX(a.email, '@', -1) domain
3013                        {$matchScoreCol}
3014                        {$subquery}
3015                    FROM
3016                        tbl_quotations a
3017                        LEFT JOIN tbl_companies b ON a.company_id = b.company_id
3018                        LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
3019                        LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
3020                        LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
3021                        LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
3022                        LEFT JOIN tbl_sources f ON a.source_id = f.source_id
3023                        LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
3024                        LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
3025                        {$lasLeftJoin}
3026                    WHERE a.for_add = 0 {$where} {$whereBlocked}
3027                    GROUP BY a.id
3028                    {$orderBy}
3029                    LIMIT {$offset}{$limit}
3030                    ";
3031        // return $query;
3032        $value = Cache::get(base64_encode($query));
3033
3034        if (! $value) {
3035            $result = DB::select($query);
3036
3037            Cache::put(base64_encode($query), $result, 600);
3038        } else {
3039            $result = $value;
3040        }
3041
3042        $totalQuery = "SELECT
3043                            COUNT(a.id) totalRowCount,
3044                            SUM(CAST(a.amount AS DECIMAL(10,2))) totalAmount
3045                        FROM
3046                            tbl_quotations a
3047                            LEFT JOIN tbl_companies b ON a.company_id = b.company_id
3048                            LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
3049                            LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
3050                            LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
3051                            LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
3052                            LEFT JOIN tbl_sources f ON a.source_id = f.source_id
3053                            LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
3054                            LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
3055                            {$lasLeftJoin}
3056                        WHERE a.for_add = 0
3057                        {$where} {$whereBlocked}";
3058
3059        $value = Cache::get(base64_encode($totalQuery));
3060
3061        if (! $value) {
3062            $countQuery = DB::select($totalQuery);
3063
3064            Cache::put(base64_encode($totalQuery), $countQuery, 600);
3065        } else {
3066            $countQuery = $value;
3067        }
3068
3069        $totalToFollowUpQuery = "SELECT
3070                                        COUNT(DISTINCT a.id) totalRowCount
3071                                    FROM
3072                                        tbl_quotations a
3073                                    LEFT JOIN tbl_companies b ON a.company_id = b.company_id
3074                                    LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
3075                                    LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
3076                                    LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
3077                                    LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
3078                                    LEFT JOIN tbl_sources f ON a.source_id = f.source_id
3079                                    LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
3080                                    LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
3081                                    LEFT JOIN (
3082                                        SELECT
3083                                        a.id,
3084                                        SUBSTRING_INDEX(
3085                                            SUBSTRING_INDEX(a.email, ',', n.digit + 1),
3086                                            ',',
3087                                            -1
3088                                        ) AS email_domain
3089                                        FROM
3090                                        tbl_quotations a
3091                                        INNER JOIN (
3092                                            SELECT
3093                                            0 AS digit
3094                                            UNION ALL
3095                                            SELECT
3096                                            1
3097                                            UNION ALL
3098                                            SELECT
3099                                            2
3100                                            UNION ALL
3101                                            SELECT
3102                                            3
3103                                            UNION ALL
3104                                            SELECT
3105                                            4
3106                                            UNION ALL
3107                                            SELECT
3108                                            5
3109                                            UNION ALL
3110                                            SELECT
3111                                            6
3112                                            UNION ALL
3113                                            SELECT
3114                                            7
3115                                            UNION ALL
3116                                            SELECT
3117                                            8
3118                                            UNION ALL
3119                                            SELECT
3120                                            9
3121                                        ) n ON LENGTH(
3122                                            REPLACE(a.email, ',', '')
3123                                        ) <= LENGTH(a.email)- n.digit
3124                                        GROUP BY a.id
3125                                    ) temp ON a.id = temp.id
3126                                    WHERE
3127                                    a.last_follow_up_date < NOW()
3128                                    AND a.budget_status_id IN (2)
3129                                    AND a.budget_type_id IS NOT NULL
3130                                    AND a.email IS NOT NULL
3131                                    AND a.email <> ''
3132                                    AND NOT EXISTS (
3133                                        SELECT
3134                                        1
3135                                        FROM
3136                                        tbl_blocked_domains bd
3137                                        WHERE
3138                                        temp.email_domain LIKE CONCAT('%', bd.domain, '%')
3139                                        AND bd.company_id = a.company_id
3140                                    )
3141                                    AND a.last_follow_up_date IS NOT NULL
3142                                    AND a.reason_for_not_following_up_id IS NULL
3143                                    AND a.last_follow_up_date > 0
3144                                    AND a.total_sent < b.limit_reminder_emails
3145                                    AND a.for_add = 0
3146                                    {$where}";
3147
3148        $value = Cache::get(base64_encode($totalToFollowUpQuery));
3149
3150        if (! $value) {
3151            $countToFollowUpQuery = DB::select($totalToFollowUpQuery);
3152
3153            Cache::put(base64_encode($totalToFollowUpQuery), $countToFollowUpQuery, 600);
3154        } else {
3155            $countToFollowUpQuery = $value;
3156        }
3157
3158        $query = "SELECT
3159                            COUNT(1) as count,
3160                            SUM(a.amount) as total_amount
3161                        FROM tbl_quotations a
3162                        LEFT JOIN tbl_companies b ON a.company_id = b.company_id
3163                        LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
3164                        LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
3165                        LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
3166                        LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
3167                        LEFT JOIN tbl_sources f ON a.source_id = f.source_id
3168                        LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
3169                        LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
3170                        WHERE a.budget_status_id = 11
3171                        AND a.email IS NOT NULL
3172                        AND a.budget_type_id IS NOT NULL
3173                        {$whereSendToClient}
3174                        ";
3175
3176        $value = Cache::get(base64_encode($query));
3177
3178        if (! $value) {
3179            $totalSendToClient = DB::select($query);
3180
3181            Cache::put(base64_encode($query), $totalSendToClient, 600);
3182        } else {
3183            $totalSendToClient = $value;
3184        }
3185
3186        return response([
3187            'message' => 'OK',
3188            'data' => $result,
3189            'totalAmount' => $countQuery[0]->totalAmount,
3190            'totalRowCount' => $countQuery[0]->totalRowCount,
3191            'totalToFollowUpRowCount' => $countToFollowUpQuery[0]->totalRowCount,
3192            'totalSendToClient' => $totalSendToClient[0]->count,
3193            'totalSendToClientAmount' => $totalSendToClient[0]->total_amount,
3194        ]);
3195
3196        // } catch (\Exception $e) {
3197        //     return response(['message' => 'KO', 'error' => $e->getMessage()]);
3198        // }
3199
3200    }
3201
3202    public function get_dates(Request $request): ResponseFactory|HttpResponse{
3203
3204        try {
3205
3206            $data = $request->all();
3207            $companyId = addslashes((string) $data['company_id']);
3208
3209            $where = '';
3210            if ($companyId != 0) {
3211                $where = " AND a.company_id = {$companyId} ";
3212            } else {
3213                $where = " AND a.company_id IN ({$this->companyId})";
3214            }
3215
3216            $query = "SELECT
3217                        DATE_FORMAT(a.request_date, '%d/%m/%Y') request_date_translate,
3218                        DATE_FORMAT(a.issue_date, '%d/%m/%Y') issue_date_translate,
3219                        DATE_FORMAT(a.acceptance_date, '%d/%m/%Y') acceptance_date_translate,
3220                        DATE_FORMAT(a.last_follow_up_date, '%d/%m/%Y') last_follow_up_date_translate,
3221                        DATE_FORMAT(a.created_at, '%d/%m/%Y') created_at_translate,
3222                        DATE_FORMAT(a.accepted_at, '%d/%m/%Y') accepted_at_translate
3223                    FROM tbl_quotations a
3224                    WHERE a.for_add = 0 {$where}";
3225
3226            $result = DB::select($query);
3227
3228            return response([
3229                'message' => 'OK',
3230                'data' => $result,
3231            ]);
3232
3233        } catch (\Exception $e) {
3234            report(AppException::fromException($e, 'GET_DATES_EXCEPTION'));
3235            return response(['message' => 'KO', 'error' => $e->getMessage()]);
3236        }
3237
3238    }
3239
3240    public function list_quotation_analytics_by_source(Request $request): ResponseFactory|HttpResponse{
3241
3242        try {
3243
3244            $data = $request->all();
3245            $companyId = addslashes((string) $data['company_id']);
3246
3247            $where = '';
3248            $whereYear = '';
3249
3250            $dateLflArray = [];
3251            $companyIds = $this->companyIds;
3252
3253            if($companyId != 0){
3254                $companyIds = [$companyId];
3255            }
3256
3257            $field = 'issue_date';
3258
3259            if (isset($data['years']) && $data['years'] != null) {
3260
3261                if (count($data['years']) > 0) {
3262                    foreach ($data['years'] as $year) {
3263                        if (isset($data['week']) && $data['week'] != null) {
3264                            $w = sprintf('%02d', $data['week']);
3265                            $whereYear .= " AND YEARWEEK(q.issue_date, 1) = '{$year}{$w}'";
3266                        } else {
3267                            $whereYear .= " AND YEAR(q.issue_date) = {$year}";
3268                        }
3269                    }
3270                }
3271            }
3272
3273            foreach ($companyIds as $v) {
3274
3275                $lflWhere = " AND q.company_id = {$v} ";
3276
3277                $query = "SELECT
3278                            CONCAT(
3279                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
3280                                ' - ',
3281                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
3282                            ) AS date_like,
3283                            YEAR(q.{$field}) 'year',
3284                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
3285                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
3286                            {$v} 'company_id'
3287                        FROM
3288                            tbl_quotations q
3289                        WHERE
3290                            q.{$field} IS NOT NULL
3291                            AND q.for_add = 0
3292                            {$lflWhere}
3293                            {$whereYear}
3294                        GROUP BY YEAR(q.{$field})
3295                        ORDER BY YEAR(q.{$field}) DESC";
3296
3297                $dateLike = DB::select($query);
3298
3299                $dateLflArray[$v] = $dateLike;
3300            }
3301
3302            $whereAcceptanceDate = 'q.acceptance_date IS NOT NULL ';
3303
3304            $isFy = true;
3305
3306            if (isset($data['issue_year_ytd']) && $data['issue_year_ytd'] != null && $data['issue_year_ytd'] == true) {
3307                $isFy = false;
3308                $ytdArray = [];
3309                $ytdAcceptanceArray = [];
3310                $lflCompanyIds = [];
3311                $lflCompanyIdsAcc = [];
3312                foreach ($dateLflArray as $k => $v) {
3313                    foreach ($dateLflArray[$k] as $item) {
3314                        $year = $item->year;
3315                        $now = date('m-d');
3316                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}' AND YEAR(q.acceptance_date) = YEAR(issue_date)");
3317                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
3318                    }
3319
3320                    $ytdArray = implode(' OR ', $ytdArray);
3321                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
3322                    $ytdArray = [];
3323
3324                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
3325                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
3326                    $ytdAcceptanceArray = [];
3327                }
3328
3329                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
3330                $where .= " AND ({$lflCompanyIds}";
3331
3332                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
3333                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
3334            }
3335
3336            if (isset($data['issue_year_lfl']) && $data['issue_year_lfl'] != null && $data['issue_year_lfl'] == true) {
3337                $isFy = false;
3338                $lflArray = [];
3339                $ytdAcceptanceArray = [];
3340                $lflCompanyIds = [];
3341                $lflCompanyIdsAcc = [];
3342                foreach ($dateLflArray as $k => $v) {
3343                    foreach ($dateLflArray[$k] as $item) {
3344                        $year = $item->year;
3345                        $min_date_like = $item->min_date_like;
3346                        $max_date_like = $item->max_date_like;
3347                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND YEAR(q.acceptance_date) = YEAR(issue_date)");
3348                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
3349                    }
3350
3351                    $lflArray = implode(' OR ', $lflArray);
3352                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
3353                    $lflArray = [];
3354
3355                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
3356                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
3357                    $ytdAcceptanceArray = [];
3358                }
3359
3360                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
3361                $where .= " AND ({$lflCompanyIds}";
3362
3363                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
3364                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
3365            }
3366
3367            if ($isFy) {
3368                if ($companyId != 0) {
3369                    $where .= " AND q.company_id = {$companyId} ";
3370                } else {
3371                    $where .= " AND q.company_id IN ({$this->companyId})";
3372                }
3373            }
3374
3375            if (isset($data['source']) && $data['source'] != null) {
3376                $where .= " AND s.name = '{$data['source']}'";
3377            }
3378
3379            if (isset($data['month']) && $data['month'] != null) {
3380                $where .= " AND MONTH(q.issue_date) = '{$data['month']}'";
3381            }
3382
3383            if (isset($data['commercial']) && $data['commercial'] != null) {
3384                $where .= " AND q.commercial = '{$data['commercial']}'";
3385            }
3386
3387            if (isset($data['created_by']) && $data['created_by'] != null) {
3388                $where .= " AND q.created_by = '{$data['created_by']}'";
3389            }
3390
3391            if (isset($data['budget_type']) && $data['budget_type'] != null) {
3392                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
3393            }
3394
3395            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
3396                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
3397            }
3398
3399            if (isset($data['budget_status']) && $data['budget_status'] != null) {
3400                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
3401            }
3402
3403            if (isset($data['client_type']) && $data['client_type'] != null) {
3404                $where .= " AND ct.customer_type_id = {$data['client_type']}";
3405            }
3406
3407            if (isset($data['segment_id']) && $data['segment_id'] != null) {
3408                $where .= " AND q.segment_id = {$data['segment_id']}";
3409            }
3410
3411            $query = "SELECT
3412                        YEAR(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) AS 'year',
3413                        LPAD(MONTH(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)), 2, 0) AS 'month',
3414                        LPAD(WEEK(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)), 2, 0) AS 'week',
3415                        DATE_FORMAT(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY), '%W, %M %e') issue_date,
3416                        COUNT(
3417                            CASE WHEN q.issue_date IS NOT NULL
3418                            THEN 1 END
3419                        ) AS totalIssue,
3420                        GROUP_CONCAT(
3421                            CASE WHEN q.issue_date IS NOT NULL
3422                            THEN q.id END
3423                        ) AS groupConcatIds,
3424                        SUM(
3425                            CASE WHEN q.issue_date IS NOT NULL THEN q.amount END
3426                        ) AS revenueIssue,
3427                        COUNT(
3428                            CASE WHEN {$whereAcceptanceDate} AND bs.name = 'Aceptado' THEN 1 END
3429                        ) AS totalAcceptance,
3430                        SUM(
3431                            CASE WHEN {$whereAcceptanceDate} AND bs.name = 'Aceptado' THEN q.amount END
3432                        ) AS revenueAcceptance,
3433                        SUM(
3434                            CASE WHEN {$whereAcceptanceDate} AND bs.name = 'Aceptado' THEN q.amount END
3435                        ) / SUM(
3436                            CASE WHEN q.issue_date IS NOT NULL THEN q.amount END
3437                        ) * 100 AS revenueAcceptanceIssuedPercentage,
3438                        COUNT(
3439                            CASE WHEN bs.name = 'Rechazado' THEN 1 END
3440                        ) AS totalRejected,
3441                        SUM(
3442                            CASE WHEN bs.name = 'Rechazado' THEN q.amount END
3443                        ) AS revenueRejected,
3444                        COUNT(
3445                            CASE WHEN bs.name = 'Rechazado - automaticamente' THEN 1 END
3446                        ) AS totalRejectedAutomatic,
3447                        SUM(
3448                            CASE WHEN bs.name = 'Rechazado - automaticamente' THEN q.amount END
3449                        ) AS revenueRejectedAutomatic
3450                    FROM
3451                        tbl_quotations q
3452                        LEFT JOIN tbl_sources s ON s.source_id = q.source_id
3453                        LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
3454                        LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
3455                        LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
3456                    WHERE
3457                        q.issue_date IS NOT NULL
3458                        AND q.budget_type_id != 7
3459                        AND q.budget_type_id IS NOT NULL
3460                        AND q.for_add = 0
3461                        AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
3462                        {$where}
3463                        {$whereYear}                        
3464                    GROUP BY
3465                        YEAR(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)),
3466                        MONTH(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)),
3467                        WEEK(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) WITH ROLLUP
3468                    ORDER BY
3469                        YEAR DESC,
3470                        MONTH ASC,
3471                        WEEK ASC,
3472                        DATE_FORMAT(q.issue_date, '%e') ASC";
3473            // return $query;
3474            $value = Cache::get(base64_encode($query));
3475
3476            if (! $value) {
3477                $result = DB::select($query);
3478
3479                Cache::put(base64_encode($query), $result, 600);
3480            } else {
3481                $result = $value;
3482            }
3483
3484            return response([
3485                'message' => 'OK',
3486                'data' => $result,
3487            ]);
3488
3489        } catch (\Exception $e) {
3490            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_BY_SOURCE_EXCEPTION'));
3491            return response(['message' => 'KO', 'error' => $e->getMessage()]);
3492        }
3493    }
3494
3495    public function list_quotation_analytics_send_budgets(Request $request): ResponseFactory|HttpResponse{
3496
3497        try {
3498
3499            $data = $request->all();
3500            $companyId = addslashes((string) $data['company_id']);
3501
3502            $where = '';
3503            $whereYear = '';
3504
3505            if ($companyId != 0) {
3506                $where = " AND q.company_id = {$companyId} ";
3507            } else {
3508                $where = " AND q.company_id IN ({$this->companyId}";
3509            }
3510
3511            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
3512                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
3513            }
3514
3515            if (isset($data['budget_status']) && $data['budget_status'] != null) {
3516                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
3517            }
3518
3519            if (isset($data['years']) && $data['years'] != null) {
3520                $years = implode(',', $data['years']);
3521                if (count($data['years']) > 0) {
3522                    $whereYear = " AND YEAR(q.issue_date) IN ({$years})";
3523                }
3524            }
3525
3526            $query = "SELECT
3527                            YEAR(q.issue_date) AS 'year',
3528                            MONTH(q.issue_date) AS 'month',
3529                            SUM(
3530                                CASE WHEN MONTH(request_date) = MONTH(issue_date) THEN 1 ELSE 0 END
3531                            ) totalRequest,
3532                            COUNT(
3533                                CASE WHEN q.issue_date IS NOT NULL THEN 1 END
3534                            ) AS totalIssue,
3535                            GROUP_CONCAT(
3536                                CASE WHEN q.issue_date IS NOT NULL
3537                                THEN q.id END
3538                            ) AS groupConcatIds,
3539                            SUM(
3540                                CASE WHEN MONTH(request_date) = MONTH(issue_date) THEN 1 ELSE 0 END
3541                            ) /
3542                            COUNT(
3543                                CASE WHEN q.issue_date IS NOT NULL THEN 1 END
3544                            ) * 100 issuePercentage,
3545                            AVG(
3546                                COALESCE(q.duration, 0)
3547                            ) AS averageDurationIssue,
3548                            COALESCE(
3549                                AVG(
3550                                    CASE WHEN bt.name = 'Mantenimiento' THEN COALESCE(q.duration, 0) ELSE NULL END
3551                                ), 0
3552                            ) AS averageDurationMaintenance,
3553                            COALESCE(
3554                                AVG(
3555                                    CASE WHEN bt.name = 'Nuevos' THEN COALESCE(q.duration, 0) ELSE NULL END
3556                                ), 0
3557                            ) AS averageDurationNew,
3558                            COALESCE(
3559                                AVG(
3560                                    CASE WHEN bt.name = 'Correctivos' THEN COALESCE(q.duration, 0) ELSE NULL END
3561                                ), 0
3562                            ) AS averageDurationCorretive,
3563                            COALESCE(
3564                                AVG(
3565                                    CASE WHEN bt.name = 'Anomalías' THEN COALESCE(q.duration, 0) ELSE NULL END
3566                                ), 0
3567                            ) AS averageDurationAnomalies,
3568                            COALESCE(
3569                                AVG(
3570                                    CASE WHEN bt.name = 'Precios Unitarios' THEN COALESCE(q.duration, 0) ELSE NULL END
3571                                ), 0
3572                            ) AS averageDurationUnitPrice,
3573                            COALESCE(
3574                                AVG(
3575                                    CASE WHEN bt.name = 'Instalaciones' THEN COALESCE(q.duration, 0) ELSE NULL END
3576                                ), 0
3577                            ) AS averageDurationFacilities,
3578                            COALESCE(
3579                                AVG(
3580                                    CASE WHEN bt.name = 'Alquiler' THEN COALESCE(q.duration, 0) ELSE NULL END
3581                                ), 0
3582                            ) AS averageDurationRent,
3583                            COALESCE(
3584                                AVG(
3585                                    CASE WHEN bt.name = 'OCA visita' THEN COALESCE(q.duration, 0) ELSE NULL END
3586                                ), 0
3587                            ) AS averageDurationOCA,
3588                            COALESCE(
3589                                AVG(
3590                                    CASE WHEN bt.name = 'Retirada y gestion de residuos' THEN COALESCE(q.duration, 0) ELSE NULL END
3591                                ), 0
3592                            ) AS averageDurationWRM,
3593                            COALESCE(
3594                                AVG(
3595                                    CASE WHEN bt.name = 'Formación' THEN COALESCE(q.duration, 0) ELSE NULL END
3596                                ), 0
3597                            ) AS averageDurationTraining,
3598                            COALESCE(
3599                                AVG(
3600                                    CASE WHEN bt.name = 'Anomalías de facilities' THEN COALESCE(q.duration, 0) ELSE NULL END
3601                                ), 0
3602                            ) AS averageDurationFacilityAnomalies
3603                        FROM
3604                            tbl_quotations q
3605                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
3606                            LEFT JOIN tbl_budget_type_groups btg ON bt.budget_type_id = btg.budget_type_id
3607                        WHERE
3608                            q.issue_date IS NOT NULL
3609                            AND q.budget_type_id != 7
3610                            AND q.budget_type_id IS NOT NULL
3611                            AND q.for_add = 0
3612                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
3613                            {$where}
3614                            {$whereYear}
3615                        GROUP BY
3616                            YEAR(q.issue_date),
3617                            MONTH(q.issue_date) WITH ROLLUP
3618                        ORDER BY
3619                            YEAR(q.issue_date) DESC,
3620                            MONTH(q.issue_date) ASC";
3621
3622            $sendBudgets = [];
3623            $sendBudgetsTotals = [];
3624
3625            $value = Cache::get(base64_encode($query));
3626
3627            if (! $value) {
3628                $result = DB::select($query);
3629
3630                Cache::put(base64_encode($query), $result, 600);
3631            } else {
3632                $result = $value;
3633            }
3634
3635            if (count($result) > 0) {
3636                for ($i = 0; $i < count($result); $i++) {
3637
3638                    if ($result[$i]->year == null && $result[$i]->month == null) {
3639                        $result[$i]->month = 'totalGeneral';
3640                        $sendBudgetsTotals['totalGeneral'] = $result[$i];
3641
3642                        continue;
3643                    } elseif ($result[$i]->month == null && $result[$i]->year != null) {
3644                        if (count($data['years']) > 0 || $whereYear == '') {
3645                            $result[$i]->month = 'totalSub';
3646                        } else {
3647                            continue;
3648                        }
3649                    } else {
3650                        $result[$i]->month = sprintf('%02d', $result[$i]->month);
3651                    }
3652
3653                    $sendBudgets[$result[$i]->year][$result[$i]->month] = $result[$i];
3654                }
3655            }
3656
3657            return response([
3658                'message' => 'OK',
3659                'data' => $sendBudgets,
3660                'totals' => $sendBudgetsTotals,
3661            ]);
3662
3663        } catch (\Exception $e) {
3664            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_EXCEPTION'));
3665            return response(['message' => 'KO', 'error' => $e->getMessage()]);
3666        }
3667    }
3668
3669    public function list_quotation_analytics_track_budgets(Request $request): ResponseFactory|HttpResponse{
3670
3671        try {
3672
3673            $data = $request->all();
3674            $companyId = addslashes((string) $data['company_id']);
3675
3676            $where = '';
3677            $whereYear = '';
3678            $isBetween = false;
3679
3680            $dateLflArray = [];
3681            $companyIds = $this->companyIds;
3682
3683            if($companyId != 0){
3684                $companyIds = [$companyId];
3685            }
3686
3687            if (isset($data['years']) && $data['years'] != null) {
3688                $years = implode(',', $data['years']);
3689                if (count($data['years']) > 0) {
3690                    $whereYear = " AND YEAR(q.issue_date) IN ({$years})";
3691                }
3692            }
3693
3694            $field = 'issue_date';
3695
3696            foreach ($companyIds as $v) {
3697
3698                $lflWhere = " AND q.company_id = {$v} ";
3699
3700                $query = "SELECT
3701                            CONCAT(
3702                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
3703                                ' - ',
3704                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
3705                            ) AS date_like,
3706                            YEAR(q.{$field}) 'year',
3707                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
3708                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
3709                            {$v} 'company_id'
3710                        FROM
3711                            tbl_quotations q
3712                        WHERE
3713                            q.{$field} IS NOT NULL
3714                            AND q.for_add = 0
3715                            {$lflWhere}
3716                            {$whereYear}
3717                        GROUP BY YEAR(q.{$field})
3718                        ORDER BY YEAR(q.{$field}) DESC";
3719
3720                $dateLike = DB::select($query);
3721
3722                $dateLflArray[$v] = $dateLike;
3723            }
3724
3725            $whereAcceptanceDate = 'q.acceptance_date IS NOT NULL ';
3726
3727            $isFy = true;
3728
3729            if (isset($data['issue_year_ytd']) && $data['issue_year_ytd'] != null && $data['issue_year_ytd'] == true) {
3730                $isFy = false;
3731                $ytdArray = [];
3732                $ytdAcceptanceArray = [];
3733                $lflCompanyIds = [];
3734                $lflCompanyIdsAcc = [];
3735                foreach ($dateLflArray as $k => $v) {
3736                    foreach ($dateLflArray[$k] as $item) {
3737                        $year = $item->year;
3738                        $now = date('m-d');
3739                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}' AND YEAR(q.acceptance_date) = YEAR(issue_date)");
3740                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
3741                    }
3742
3743                    $ytdArray = implode(' OR ', $ytdArray);
3744                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
3745                    $ytdArray = [];
3746
3747                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
3748                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
3749                    $ytdAcceptanceArray = [];
3750                }
3751
3752                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
3753                $where .= " AND ({$lflCompanyIds}";
3754
3755                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
3756                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
3757            }
3758
3759            if (isset($data['issue_year_lfl']) && $data['issue_year_lfl'] != null && $data['issue_year_lfl'] == true) {
3760                $isFy = false;
3761                $lflArray = [];
3762                $ytdAcceptanceArray = [];
3763                $lflCompanyIds = [];
3764                $lflCompanyIdsAcc = [];
3765                foreach ($dateLflArray as $k => $v) {
3766                    foreach ($dateLflArray[$k] as $item) {
3767                        $year = $item->year;
3768                        $min_date_like = $item->min_date_like;
3769                        $max_date_like = $item->max_date_like;
3770                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND YEAR(q.acceptance_date) = YEAR(issue_date)");
3771                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
3772                    }
3773
3774                    $lflArray = implode(' OR ', $lflArray);
3775                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
3776                    $lflArray = [];
3777
3778                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
3779                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
3780                    $ytdAcceptanceArray = [];
3781                }
3782
3783                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
3784                $where .= " AND ({$lflCompanyIds}";
3785
3786                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
3787                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
3788            }
3789
3790            if ($isFy) {
3791                if ($companyId != 0) {
3792                    $where .= " AND q.company_id = {$companyId} ";
3793                } else {
3794                    $where .= " AND q.company_id IN ({$this->companyId})";
3795                }
3796            }
3797
3798            if (isset($data['source']) && $data['source'] != null) {
3799                $where .= " AND s.name = '{$data['source']}'";
3800            }
3801
3802            if (isset($data['commercial']) && $data['commercial'] != null) {
3803                $where .= " AND q.commercial = '{$data['commercial']}'";
3804            }
3805
3806            if (isset($data['created_by']) && $data['created_by'] != null) {
3807                $where .= " AND q.created_by = '{$data['created_by']}'";
3808            }
3809
3810            if (isset($data['budget_type']) && $data['budget_type'] != null) {
3811                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
3812            }
3813
3814            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
3815                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
3816            }
3817
3818            if (isset($data['budget_status']) && $data['budget_status'] != null) {
3819                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
3820            }
3821
3822            if (isset($data['client_type']) && $data['client_type'] != null) {
3823                $where .= " AND ct.customer_type_id = {$data['client_type']}";
3824            }
3825
3826            if (isset($data['segment_id']) && $data['segment_id'] != null) {
3827                $where .= " AND q.segment_id = {$data['segment_id']}";
3828            }
3829
3830            $query = "SELECT
3831                            YEAR(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) AS 'year',
3832                            LPAD(MONTH(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)), 2, 0) AS 'month',
3833                            LPAD(WEEK(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)), 2, 0) AS 'week',
3834                            DATE_FORMAT(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY), '%W, %M %e') issue_date,
3835                            COUNT(
3836                                CASE WHEN q.issue_date IS NOT NULL
3837                                THEN 1 END
3838                            ) AS totalIssue,
3839                            GROUP_CONCAT(
3840                                CASE WHEN q.issue_date IS NOT NULL
3841                                THEN q.id END
3842                            ) AS groupConcatIds,
3843                            COUNT(
3844                                CASE WHEN {$whereAcceptanceDate}
3845                                AND bs.name = 'Aceptado' THEN 1 END) totalAccept,
3846                            COUNT(
3847                                CASE WHEN q.acceptance_date IS NOT NULL
3848                                AND {$whereAcceptanceDate}
3849                                AND bs.name = 'Aceptado'
3850                                AND DATEDIFF(q.acceptance_date, q.issue_date) <= 10 THEN 1 END
3851                            ) / COUNT(
3852                                CASE WHEN q.issue_date IS NOT NULL
3853                                THEN 1 END
3854                            ) * 100 AS percentageOfacceptanceLessThan10,
3855                            COUNT(
3856                                CASE WHEN q.acceptance_date IS NOT NULL
3857                                AND {$whereAcceptanceDate}
3858                                AND bs.name = 'Aceptado'
3859                                AND DATEDIFF(q.acceptance_date, q.issue_date) > 10
3860                                AND DATEDIFF(q.acceptance_date, q.issue_date) < 30 THEN 1 END
3861                            ) / COUNT(
3862                                CASE WHEN q.issue_date IS NOT NULL
3863                                THEN 1 END
3864                            ) * 100 AS percentageOfacceptanceLessThan30,
3865                            COUNT(
3866                                CASE WHEN q.acceptance_date IS NOT NULL
3867                                AND {$whereAcceptanceDate}
3868                                AND bs.name = 'Aceptado'
3869                                AND DATEDIFF(q.acceptance_date, q.issue_date) >= 30 THEN 1 END
3870                            ) / COUNT(
3871                                CASE WHEN q.issue_date IS NOT NULL
3872                                THEN 1 END
3873                            ) * 100 AS percentageOfacceptanceMoreThan30,
3874                            COUNT(CASE WHEN
3875                                {$whereAcceptanceDate} THEN 1 END
3876                            ) / COUNT(
3877                                CASE WHEN q.issue_date IS NOT NULL
3878                                THEN 1 END
3879                            ) * 100 acceptedPercentage,
3880                            COALESCE(
3881                                AVG(
3882                                    CASE WHEN q.issue_date IS NOT NULL
3883                                    AND q.acceptance_date IS NOT NULL
3884                                    AND {$whereAcceptanceDate}
3885                                    AND bs.name = 'Aceptado'
3886                                    THEN DATEDIFF(q.acceptance_date, q.issue_date) ELSE NULL END
3887                                ), 0
3888                            )
3889                            AS averageAcceptedDuration,
3890                            COALESCE(
3891                                AVG(
3892                                    CASE WHEN q.issue_date IS NOT NULL
3893                                    AND q.request_date IS NOT NULL
3894                                    THEN DATEDIFF(q.issue_date, q.request_date) ELSE NULL END
3895                                ), 0
3896                            )
3897                            AS averageRequestedDays
3898                        FROM
3899                            tbl_quotations q
3900                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
3901                            LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
3902                            LEFT JOIN tbl_sources s ON s.source_id = q.source_id
3903                            LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
3904                        WHERE
3905                            q.issue_date IS NOT NULL
3906                            AND q.budget_type_id != 7
3907                            AND q.budget_type_id IS NOT NULL
3908                            AND q.for_add = 0
3909                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
3910                            {$where}
3911                            {$whereYear}
3912                        GROUP BY
3913                            YEAR(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)),
3914                            MONTH(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)),
3915                            WEEK(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) WITH ROLLUP
3916                        ORDER BY
3917                            YEAR DESC,
3918                            MONTH ASC,
3919                            WEEK ASC,
3920                            DATE_FORMAT(q.issue_date, '%e') ASC";
3921
3922            $value = Cache::get(base64_encode($query));
3923
3924            if (! $value) {
3925                $result = DB::select($query);
3926
3927                Cache::put(base64_encode($query), $result, 600);
3928            } else {
3929                $result = $value;
3930            }
3931
3932            return response([
3933                'message' => 'OK',
3934                'data' => $result,
3935            ]);
3936
3937        } catch (\Exception $e) {
3938            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_TRACK_BUDGETS_EXCEPTION'));
3939            return response(['message' => 'KO', 'error' => $e->getMessage()]);
3940        }
3941    }
3942
3943    public function list_quotation_analytics_types_budgets(Request $request): ResponseFactory|HttpResponse{
3944
3945        try {
3946
3947            $data = $request->all();
3948            $companyId = addslashes((string) $data['company_id']);
3949
3950            $where = '';
3951            $whereYear = '';
3952            $isBetween = false;
3953
3954            $dateLflArray = [];
3955            $companyIds = $this->companyIds;
3956
3957            if($companyId != 0){
3958                $companyIds = [$companyId];
3959            }
3960
3961            if (isset($data['years']) && $data['years'] != null) {
3962                $years = implode(',', $data['years']);
3963                if (count($data['years']) > 0) {
3964                    $whereYear = " AND YEAR(q.issue_date) IN ({$years})";
3965                }
3966            }
3967
3968            $field = 'issue_date';
3969
3970            foreach ($companyIds as $v) {
3971
3972                $lflWhere = " AND q.company_id = {$v} ";
3973
3974                $query = "SELECT
3975                            CONCAT(
3976                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
3977                                ' - ',
3978                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
3979                            ) AS date_like,
3980                            YEAR(q.{$field}) 'year',
3981                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
3982                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
3983                            {$v} 'company_id'
3984                        FROM
3985                            tbl_quotations q
3986                        WHERE
3987                            q.{$field} IS NOT NULL
3988                            AND q.for_add = 0
3989                            {$lflWhere}
3990                            {$whereYear}
3991                        GROUP BY YEAR(q.{$field})
3992                        ORDER BY YEAR(q.{$field}) DESC";
3993
3994                $dateLike = DB::select($query);
3995
3996                $dateLflArray[$v] = $dateLike;
3997            }
3998
3999            $whereAcceptanceDate = 'q.acceptance_date IS NOT NULL ';
4000
4001            $isFy = true;
4002
4003            if (isset($data['issue_year_ytd']) && $data['issue_year_ytd'] != null && $data['issue_year_ytd'] == true) {
4004                $isFy = false;
4005                $ytdArray = [];
4006                $ytdAcceptanceArray = [];
4007                $lflCompanyIds = [];
4008                $lflCompanyIdsAcc = [];
4009                foreach ($dateLflArray as $k => $v) {
4010                    foreach ($dateLflArray[$k] as $item) {
4011                        $year = $item->year;
4012                        $now = date('m-d');
4013                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}' AND YEAR(q.acceptance_date) = YEAR(issue_date)");
4014                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
4015                    }
4016
4017                    $ytdArray = implode(' OR ', $ytdArray);
4018                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
4019                    $ytdArray = [];
4020
4021                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
4022                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
4023                    $ytdAcceptanceArray = [];
4024                }
4025
4026                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
4027                $where .= " AND ({$lflCompanyIds}";
4028
4029                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
4030                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
4031            }
4032
4033            if (isset($data['issue_year_lfl']) && $data['issue_year_lfl'] != null && $data['issue_year_lfl'] == true) {
4034                $isFy = false;
4035                $lflArray = [];
4036                $ytdAcceptanceArray = [];
4037                $lflCompanyIds = [];
4038                $lflCompanyIdsAcc = [];
4039                foreach ($dateLflArray as $k => $v) {
4040                    foreach ($dateLflArray[$k] as $item) {
4041                        $year = $item->year;
4042                        $min_date_like = $item->min_date_like;
4043                        $max_date_like = $item->max_date_like;
4044                        array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND YEAR(q.acceptance_date) = YEAR(issue_date)");
4045                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
4046                    }
4047
4048                    $lflArray = implode(' OR ', $lflArray);
4049                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
4050                    $lflArray = [];
4051
4052                    $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
4053                    array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
4054                    $ytdAcceptanceArray = [];
4055                }
4056
4057                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
4058                $where .= " AND ({$lflCompanyIds}";
4059
4060                $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
4061                $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
4062            }
4063
4064            if ($isFy) {
4065                if ($companyId != 0) {
4066                    $where .= " AND q.company_id = {$companyId} ";
4067                } else {
4068                    $where .= " AND q.company_id IN ({$this->companyId})";
4069                }
4070            }
4071
4072            if (isset($data['budget_status']) && $data['budget_status'] != null) {
4073                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
4074            }
4075
4076            if (isset($data['segment_id']) && $data['segment_id'] != null) {
4077                $where .= " AND q.segment_id = {$data['segment_id']}";
4078            }
4079
4080            $query = "SELECT
4081                            YEAR(q.issue_date) AS 'year',
4082                            bt.name,
4083                            COUNT(
4084                                CASE WHEN q.issue_date IS NOT NULL
4085                                THEN 1 END
4086                            ) AS totalIssue,
4087                            GROUP_CONCAT(
4088                                CASE WHEN q.issue_date IS NOT NULL
4089                                THEN q.id END
4090                            ) AS groupConcatIds,
4091                            COUNT(CASE WHEN {$whereAcceptanceDate} AND bs.name = 'Aceptado' THEN 1 END) totalAccept,
4092                            COUNT(
4093                                CASE WHEN q.acceptance_date IS NOT NULL
4094                                AND {$whereAcceptanceDate}
4095                                AND bs.name = 'Aceptado'
4096                                AND DATEDIFF(q.acceptance_date, q.issue_date) < 10 THEN 1 END
4097                            ) / COUNT(
4098                                CASE WHEN q.issue_date IS NOT NULL
4099                                THEN 1 END
4100                            ) * 100 AS percentageOfacceptanceLessThan10,
4101                            COUNT(
4102                                CASE WHEN q.acceptance_date IS NOT NULL
4103                                AND {$whereAcceptanceDate}
4104                                AND bs.name = 'Aceptado'
4105                                AND DATEDIFF(q.acceptance_date, q.issue_date) > 10
4106                                AND DATEDIFF(q.acceptance_date, q.issue_date) < 30 THEN 1 END
4107                            ) / COUNT(
4108                                CASE WHEN q.issue_date IS NOT NULL
4109                                THEN 1 END
4110                            ) * 100 AS percentageOfacceptanceLessThan30,
4111                            COUNT(
4112                                CASE WHEN q.acceptance_date IS NOT NULL
4113                                AND {$whereAcceptanceDate}
4114                                AND bs.name = 'Aceptado'
4115                                AND DATEDIFF(q.acceptance_date, q.issue_date) > 30 THEN 1 END
4116                            ) / COUNT(
4117                                CASE WHEN q.issue_date IS NOT NULL
4118                                THEN 1 END
4119                            ) * 100 AS percentageOfacceptanceMoreThan30,
4120                            COUNT(CASE WHEN {$whereAcceptanceDate} THEN 1 END) / COUNT(
4121                                CASE WHEN q.issue_date IS NOT NULL
4122                                THEN 1 END
4123                            ) * 100 acceptedPercentage,
4124                            COALESCE(
4125                                AVG(
4126                                    CASE WHEN q.issue_date IS NOT NULL
4127                                    AND q.acceptance_date IS NOT NULL
4128                                    AND {$whereAcceptanceDate}
4129                                    AND bs.name = 'Aceptado'
4130                                    THEN DATEDIFF(q.acceptance_date, q.issue_date) ELSE NULL END
4131                                ), 0
4132                            )
4133                            AS averageAcceptedDuration,
4134                            COALESCE(
4135                                AVG(
4136                                    CASE WHEN q.issue_date IS NOT NULL
4137                                    AND q.request_date IS NOT NULL
4138                                    AND q.issue_date > q.request_date
4139                                    THEN DATEDIFF(q.issue_date, q.request_date) ELSE NULL END
4140                                ), 0
4141                            )
4142                            AS averageRequestedDays
4143                        FROM
4144                            tbl_quotations q
4145                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
4146                            LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
4147                        WHERE
4148                            q.issue_date IS NOT NULL
4149                            AND q.budget_type_id != 7
4150                            AND q.budget_type_id IS NOT NULL
4151                            AND q.for_add = 0
4152                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
4153                            {$where}
4154                            {$whereYear}
4155                        GROUP BY
4156                            YEAR(q.issue_date),
4157                            bt.name WITH ROLLUP";
4158
4159            $value = Cache::get(base64_encode($query));
4160
4161            if (! $value) {
4162                $result = DB::select($query);
4163
4164                Cache::put(base64_encode($query), $result, 600);
4165            } else {
4166                $result = $value;
4167            }
4168
4169            $typesBudgets = [];
4170            $typesBudgetsTotals = [];
4171
4172            if (count($result) > 0) {
4173                for ($i = 0; $i < count($result); $i++) {
4174
4175                    if ($result[$i]->year == null && $result[$i]->name == null) {
4176                        $result[$i]->name = 'totalGeneral';
4177                        $typesBudgetsTotals['totalGeneral'] = $result[$i];
4178
4179                        continue;
4180                    } elseif ($result[$i]->name == null && $result[$i]->year != null) {
4181                        if (count($data['years']) > 0 || $whereYear == '') {
4182                            $result[$i]->name = 'totalSub';
4183                        } else {
4184                            continue;
4185                        }
4186                    }
4187
4188                    $typesBudgets[$result[$i]->year][$result[$i]->name] = $result[$i];
4189                }
4190            }
4191
4192            return response([
4193                'message' => 'OK',
4194                'data' => $typesBudgets,
4195                'totals' => $typesBudgetsTotals,
4196            ]);
4197
4198        } catch (\Exception $e) {
4199            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_TYPES_BUDGETS_EXCEPTION'));
4200            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4201        }
4202    }
4203
4204    function download_quotations(Request $request): ResponseFactory|HttpResponse{
4205        ini_set('max_execution_time', 123456);
4206        $data = $request->all();
4207        $companyId = addslashes($data['company_id']);
4208        $userId = addslashes((string) $data['user_id']);
4209
4210        $where = '';
4211
4212        $query = "SELECT
4213                b.name
4214            FROM tbl_users a
4215            LEFT JOIN tbl_roles b
4216                ON a.role_id = b.role_id
4217            WHERE a.id = {$userId}";
4218
4219        $role = DB::select($query);
4220
4221        $r = new Request([
4222            'filterModel' => $data['filterModel'],
4223            'sortModel' => $data['sortModel'],
4224            'start' => 0,
4225            'end' => 999999999,
4226            'company_id' => $data['company_id'],
4227            'user_id' => $data['user_id'],
4228            'ids' => $data['ids'],
4229            'searchText' => $data['searchText'],
4230            'ids_not_in' => $data['ids_not_in'],
4231        ]);
4232
4233        $result = $this->list_quotations($r);
4234
4235        $result = $result->original['data'];
4236
4237        return response(['data' => $result]);
4238    }
4239
4240    public function download_quotations_csv($filename, $data) {}
4241
4242    function bulk_upload(Request $request): ResponseFactory|HttpResponse{
4243
4244        try {
4245
4246            $data = $request->all();
4247            $file = $request->file('file');
4248            $exte = 'Xlsx';
4249            $companyId = $data['company_id'];
4250
4251            if ($file->getMimeType() == 'application/vnd.ms-excel') {
4252                $exte = 'Xls';
4253            }
4254
4255            $destination_path = config('app.bulk_upload_file_destination');
4256            $filename         = $file->getClientOriginalName();
4257
4258            if (file_exists($destination_path.$filename)) {
4259                $filename = pathinfo($filename, PATHINFO_FILENAME).'-'.uniqid().'.'.pathinfo($filename, PATHINFO_EXTENSION);
4260            }
4261
4262            $file->move($destination_path, $filename);
4263
4264            TblBulkUpload::create(
4265                [
4266                    'company_id' => $companyId,
4267                    'filename' => $filename,
4268                    'status' => 'Uploading...',
4269                    'is_running' => 1,
4270                    'uploaded_by' => $data['created_by']
4271                ]
4272            );
4273
4274            $command = "php BulkUploadQuotations.php '{$data['created_by']}' '{$exte}' '{$destination_path}' '{$filename}{$companyId}";
4275            exec($command.' > /dev/null &');
4276
4277            $query = '';
4278            $isRunning = 0;
4279
4280            if ($companyId == 0) {
4281                $query = 'SELECT * FROM tbl_bulk_upload ORDER BY started_at DESC';
4282                $isRunning = TblBulkUpload::where('is_running', 1)->count();
4283            } else {
4284                $query = "SELECT * FROM tbl_bulk_upload WHERE company_id = {$companyId} ORDER BY started_at DESC";
4285                $isRunning = TblBulkUpload::where('is_running', 1)->where('company_id', $companyId)->count();
4286            }
4287
4288            $result = DB::select($query);
4289
4290            return response(['message' => 'OK', 'data' => $result, 'is_running' => $isRunning]);
4291
4292        } catch (\Exception $e) {
4293            report(AppException::fromException($e, 'BULK_UPLOAD_QUOTATIONS_EXCEPTION'));
4294            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4295        }
4296    }
4297
4298    function list_bulk_upload($companyId): ResponseFactory|HttpResponse{
4299
4300        try {
4301
4302            $companyId = addslashes((string) $companyId);
4303            $query = "";
4304            $isRunning = 0;
4305
4306            if ($companyId == 0) {
4307                $query = 'SELECT * FROM tbl_bulk_upload ORDER BY started_at DESC';
4308                $isRunning = TblBulkUpload::where('is_running', 1)->count();
4309            } else {
4310                $query = "SELECT * FROM tbl_bulk_upload WHERE company_id = {$companyId} ORDER BY started_at DESC";
4311                $isRunning = TblBulkUpload::where('is_running', 1)->where('company_id', $companyId)->count();
4312            }
4313
4314            $result = DB::select($query);
4315
4316            return response(['message' => 'OK', 'data' => $result, 'is_running' => $isRunning]);
4317
4318        } catch (\Exception $e) {
4319            report(AppException::fromException($e, 'LIST_BULK_UPLOAD_EXCEPTION'));
4320            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4321        }
4322
4323    }
4324
4325    function delete_number(Request $request, $id): ResponseFactory|HttpResponse{
4326
4327        try {
4328
4329            $id = addslashes((string) $id);
4330            $data = $request->all();
4331
4332            $r = TblQuotations::where('id', $id)->first();
4333            $updatedAt = date('Y-m-d H:i:s');
4334            $query = "INSERT INTO tbl_quotations_deleted (id, quote_id, company_id, for_add, created_by, updated_by, updated_at)
4335                        SELECT id, quote_id, company_id, 1, created_by, '{$data['updated_by']}', '{$updatedAt}' FROM tbl_quotations WHERE id = {$id}";
4336
4337            DB::select($query);
4338
4339            TblQuotations::where('id', $id)->delete();
4340
4341            $latestBudget = TblQuotations::where('company_id', $r->company_id)->orderByRaw('id DESC')->value('quote_id');
4342
4343            $query = "UPDATE tbl_companies SET last_id = '{$latestBudget}' WHERE company_id = {$r->company_id}";
4344            DB::select($query);
4345
4346            return response(['message' => 'OK']);
4347
4348        } catch (\Exception $e) {
4349            report(AppException::fromException($e, 'DELETE_QUOTATION_EXCEPTION'));
4350            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4351        }
4352
4353    }
4354
4355    function get_number(Request $request, $companyId, $n = null): ResponseFactory|HttpResponse{
4356
4357        try {
4358
4359            $companyId = addslashes((string) $companyId);
4360            $data = $request->all();
4361            $latestBudget = [];
4362            $number = 0;
4363            $beforeLastId = null;
4364
4365            $x = true;
4366
4367            if ($companyId == 0) {
4368                $latestBudget = TblQuotations::orderByRaw('CAST(quote_id AS DOUBLE) DESC')->value('quote_id');
4369            } else {
4370                $latestBudget = TblCompanies::where('company_id', $companyId)->value('last_id');
4371
4372                if ($latestBudget == null) {
4373                    $latestBudget = TblQuotations::where('company_id', $companyId)->orderByRaw('id DESC')->value('quote_id');
4374                    $beforeLastId = $latestBudget;
4375                }
4376            }
4377
4378            $number = $latestBudget;
4379
4380            while ($x) {
4381
4382                if(is_numeric(substr((string) $number, -1))) {
4383                    $number++;
4384                } else {
4385                    $number .= '1';
4386                }
4387
4388                $check = 0;
4389
4390                if ($companyId == 0) {
4391                    $check = TblQuotations::where('quote_id', (string) $number)->count();
4392                } else {
4393                    $check = TblQuotations::where('company_id', $companyId)->where('quote_id', (string) $number)->count();
4394                }
4395
4396                if ($check == 0) {
4397                    $x = false;
4398                }
4399            }
4400
4401            $result = null;
4402
4403            if ($n == null) {
4404                $result = TblQuotations::create(['quote_id' => $number, 'company_id' => $companyId, 'for_add' => 1, 'created_by' => $data['created_by']]);
4405            }
4406
4407            if ($beforeLastId == null) {
4408                $beforeLastId = $number;
4409            }
4410
4411            $query = "UPDATE tbl_companies SET last_id = '{$number}', before_last_id = CASE WHEN before_last_id IS NULL THEN '{$beforeLastId}' ELSE before_last_id END WHERE company_id = {$companyId}";
4412            DB::select($query);
4413
4414            return response([
4415                'message' => 'OK',
4416                'number' => $number,
4417                'id' => ($result != null) ? $result->id : null,
4418            ]);
4419
4420        } catch (\Exception $e) {
4421            report(AppException::fromException($e, 'GET_NUMBER_EXCEPTION'));
4422            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4423        }
4424
4425    }
4426
4427    function get_years(Request $request): ResponseFactory|HttpResponse{
4428
4429        try {
4430
4431            $data = $request->all();
4432            $companyId = addslashes((string) $data['company_id']);
4433            $where = "";
4434
4435            if ($companyId != 0) {
4436                $where = " AND company_id = {$companyId} ";
4437            } else {
4438                $where = " AND company_id IN ({$this->companyId}";
4439            }
4440
4441            $query = "SELECT
4442                        CONCAT(
4443                            DATE_FORMAT((SELECT MIN(issue_date) FROM tbl_quotations WHERE issue_date IS NOT NULL {$where}), '%c/%e/'), YEAR(issue_date),
4444                            ' - ',
4445                            DATE_FORMAT((SELECT MAX(issue_date) FROM tbl_quotations WHERE issue_date IS NOT NULL {$where}), '%c/%e/'), YEAR(issue_date)
4446                        ) AS date_like,
4447                        YEAR(issue_date) 'year'
4448                        FROM tbl_quotations
4449                        WHERE issue_date IS NOT NULL {$where}
4450                        GROUP BY YEAR(issue_date)
4451                        ORDER BY YEAR(issue_date) DESC";
4452
4453            $issueDate = DB::select($query);
4454
4455            $query = "SELECT
4456                        CONCAT(
4457                            DATE_FORMAT((SELECT MIN(created_at) FROM tbl_quotations WHERE created_at IS NOT NULL {$where}), '%c/%e/'), YEAR(created_at),
4458                            ' - ',
4459                            DATE_FORMAT((SELECT MAX(created_at) FROM tbl_quotations WHERE created_at IS NOT NULL {$where}), '%c/%e/'), YEAR(created_at)
4460                        ) AS date_like,
4461                        YEAR(created_at) 'year'
4462                        FROM tbl_quotations
4463                        WHERE created_at IS NOT NULL {$where}
4464                        GROUP BY YEAR(created_at)
4465                        ORDER BY YEAR(created_at) DESC";
4466
4467            $createdAt = DB::select($query);
4468
4469            $query = "SELECT
4470                        CONCAT(
4471                            DATE_FORMAT((SELECT MIN(acceptance_date) FROM tbl_quotations WHERE acceptance_date IS NOT NULL {$where}), '%c/%e/'), YEAR(acceptance_date),
4472                            ' - ',
4473                            DATE_FORMAT((SELECT MAX(acceptance_date) FROM tbl_quotations WHERE acceptance_date IS NOT NULL {$where}), '%c/%e/'), YEAR(acceptance_date)
4474                        ) AS date_like,
4475                        YEAR(acceptance_date) 'year'
4476                        FROM tbl_quotations
4477                        WHERE acceptance_date IS NOT NULL {$where}
4478                        GROUP BY YEAR(acceptance_date)
4479                        ORDER BY YEAR(acceptance_date) DESC";
4480
4481            $acceptanceDate = DB::select($query);
4482
4483            return response([
4484                'message' => 'OK',
4485                'data' => $issueDate,
4486                'created_at' => $createdAt,
4487                'acceptance_date' => $acceptanceDate,
4488            ]);
4489
4490        } catch (\Exception $e) {
4491            report(AppException::fromException($e, 'GET_YEARS_EXCEPTION'));
4492            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4493        }
4494
4495    }
4496
4497    function human_filesize($bytes, $decimals = 2): string {
4498        $size = ['B', 'KB', 'MB'];
4499
4500        $factor = floor((strlen($bytes) - 1) / 3);
4501        return number_format($bytes / 1024 ** $factor, 2, ',', '.') . $size[$factor];
4502    }
4503
4504    function get_files($quoteId): ResponseFactory|HttpResponse{
4505
4506        try {
4507
4508            $quoteId = addslashes((string) $quoteId);
4509
4510            $quoteId = (int) $quoteId;
4511            $query = '
4512            SELECT
4513                file_id,
4514                quotation_id,
4515                quote_id,
4516                original_name,
4517                filename,
4518                uploaded_by,
4519                uploaded_at,
4520                is_internal,
4521                file_size,
4522                file_hash,
4523                mime_type
4524            FROM tbl_files
4525            WHERE quotation_id = ?
4526            AND is_internal IS NULL';
4527
4528            $result = DB::select($query, [$quoteId]);
4529
4530            foreach ($result as $file) {
4531                if ($file->file_size > 0) {
4532                    $file->filesize = $this->human_filesize($file->file_size);
4533                } else {
4534                    $path = 'uploads/'.$file->filename;
4535
4536                    if (Storage::disk('s3')->exists($path)) {
4537                        $fileSizeBytes = Storage::disk('s3')->size($path);
4538                    } else {
4539                        $fileSizeBytes = 0;
4540                    }
4541
4542                    $file->filesize = $this->human_filesize($fileSizeBytes);
4543                }
4544
4545                $file->original_name = $file->original_name." ({$file->filesize})";
4546            }
4547
4548            $query = '
4549            SELECT
4550                file_id,
4551                quotation_id,
4552                quote_id,
4553                original_name,
4554                filename,
4555                uploaded_by,
4556                uploaded_at,
4557                is_internal,
4558                file_size,
4559                file_hash,
4560                mime_type
4561            FROM tbl_files
4562            WHERE quotation_id = ?
4563            AND is_internal = 1';
4564
4565            $internal = DB::select($query, [$quoteId]);
4566
4567            foreach ($internal as $file) {
4568                if ($file->file_size > 0) {
4569                    $file->filesize = $this->human_filesize($file->file_size);
4570                } else {
4571                    $path = 'uploads/'.$file->filename;
4572
4573                    if (Storage::disk('s3')->exists($path)) {
4574                        $fileSizeBytes = Storage::disk('s3')->size($path);
4575                    } else {
4576                        $fileSizeBytes = 0;
4577                    }
4578
4579                    $file->filesize = $this->human_filesize($fileSizeBytes);
4580                }
4581
4582                $file->original_name = $file->original_name." ({$file->filesize})";
4583            }
4584
4585            $job = TblOngoingJobs::where('quotation_id', $quoteId)->first();
4586            $order = TblQuotations::where('id', $quoteId)->first();
4587
4588            $z = 0;
4589            $status = 2;
4590            $xStatus = 'processed';
4591            $uniqueEvents = [];
4592            $currentEvents = [];
4593            $followUpLogs = [];
4594            $sendgridFollowUpLogs = [];
4595            $projectTypes = [];
4596
4597            if ($order) {
4598                $emails = explode(',', str_replace(' ', '', $order->email));
4599
4600                $sendGrid = TblSendgridWebhook::where('quotation_id', $quoteId)->where('x_message_id', $order->x_message_id)->first();
4601
4602                if ($sendGrid) {
4603                    $emailErrors = ['deferred', 'bounce', 'dropped', 'spamreport', 'invalid'];
4604                    $events = json_decode($sendGrid->json_body, true);
4605                    $xMessageId = $sendGrid->x_message_id;
4606                    $isDelivered = 0;
4607                    $isProcessed = 0;
4608                    $isError = 0;
4609
4610                    foreach ($emails as $email) {
4611
4612                        $emailEvents = array_filter($events, fn(array $event): bool => strtolower((string) $event['email']) === strtolower($email));
4613
4614                        $statuses = array_unique(array_column($emailEvents, 'event'));
4615                        $eventCount = count($statuses);
4616
4617                        if ($eventCount === 1 && in_array('processed', $statuses)) {
4618                            $xStatus = 'processed';
4619                        }
4620
4621                        if ($eventCount == 2) {
4622                            if (in_array('processed', $statuses) && in_array('delivered', $statuses)) {
4623                                $xStatus = 'delivered';
4624                            }
4625
4626                            foreach ($emailErrors as $e) {
4627                                if (in_array('processed', $statuses) && in_array($e, $statuses)) {
4628                                    $xStatus = $e;
4629                                }
4630                            }
4631
4632                        } elseif ($eventCount > 2) {
4633                            if (in_array('processed', $statuses) && in_array('delivered', $statuses)) {
4634                                $xStatus = 'delivered';
4635                            }
4636
4637                            if ($xStatus != 'delivered') {
4638                                foreach ($emailErrors as $e) {
4639                                    if (in_array('processed', $statuses) && in_array($e, $statuses)) {
4640                                        $xStatus = $e;
4641                                    }
4642                                }
4643                            }
4644                        }
4645
4646                        if ($xStatus == 'processed') {
4647                            $isProcessed++;
4648                        } elseif ($xStatus == 'delivered') {
4649                            $isDelivered++;
4650                        } else {
4651                            $isError++;
4652                        }
4653
4654                        foreach ($emailEvents as $event) {
4655                            $key = strtolower($event['email']).'|'.$event['event'];
4656                            if (! isset($uniqueEvents[$key])) {
4657                                $uniqueEvents[$key] = $event;
4658                            }
4659                        }
4660                    }
4661
4662                    if ($uniqueEvents) {
4663                        foreach (array_values($uniqueEvents) as $d) {
4664                            $d['created_at'] = date('Y-m-d H:i:s', $d['timestamp']);
4665
4666                            if (isset($d['response'])) {
4667                                $d['error'] = $d['response'];
4668                            }
4669
4670                            if (isset($d['reason'])) {
4671                                $d['error'] = $d['reason'];
4672                            }
4673
4674                            array_push($currentEvents, $d);
4675                        }
4676                    }
4677
4678                    if (count($emails) == $isDelivered) {
4679                        $status = 1;
4680                        $xStatus = 'Completed';
4681                    } elseif ($isProcessed > 0) {
4682                        $status = 2;
4683                        $xStatus = 'Processing';
4684                    } elseif ($isError > 0) {
4685                        $status = 3;
4686
4687                        if ($xStatus == 'bounce') {
4688                            $xStatus = 'Error - Bounce';
4689                        } else {
4690                            $xStatus = 'Error';
4691                        }
4692                    }
4693                }
4694
4695                if ($order->x_status != $xStatus) {
4696                    TblQuotations::where('id', $order->id)->update(
4697                        [
4698                            'x_status' => $xStatus,
4699                        ]
4700                    );
4701                    $z = 1;
4702                    Cache::flush();
4703                }
4704
4705                if (empty($currentEvents)) {
4706                    $status = 0;
4707                }
4708
4709                $followUpLogs = TblFollowUpLogs::where('quotation_id', $quoteId)->orderBy('created_at', 'desc')->get();
4710
4711                $projectTypes = TblProjectTypes::where('company_id', $order->company_id)->get();
4712
4713                if ($order->y_message_id) {
4714                    $sendgridFollowUpLogs = TblSendgridWebhook::where('x_message_id', $order->y_message_id)->first();
4715
4716                    if ($sendgridFollowUpLogs) {
4717                        $sendgridFollowUpLogs = json_decode($sendgridFollowUpLogs->json_body, true);
4718                        foreach ($sendgridFollowUpLogs as &$item) {
4719                            $item['created_at'] = date('Y-m-d H:i:s', $item['timestamp']);
4720                        }
4721                    }
4722                }
4723            }
4724
4725            return response([
4726                'message' => 'OK',
4727                'data' => $result,
4728                'followUpLogs' => $followUpLogs,
4729                'sendgridFollowUpLogs' => $sendgridFollowUpLogs,
4730                'internal' => $internal,
4731                'projectTypes' => $projectTypes,
4732                'currentEvents' => [
4733                    'status' => $status,
4734                    'email' => $currentEvents,
4735                    $uniqueEvents,
4736                    $xStatus
4737                ],
4738                'job' => $job,
4739                'isUpdated' => $z,
4740            ]);
4741
4742        } catch (\Exception $e) {
4743            report(AppException::fromException($e, 'GET_FILES_EXCEPTION'));
4744            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4745        }
4746
4747    }
4748
4749    public function download_file($fileId)
4750    {
4751        try {
4752            $fileId = addslashes((string) $fileId);
4753            $file = TblFiles::where('file_id', $fileId)->first();
4754
4755            if (! $file) {
4756                return response()->json([
4757                    'message' => 'KO',
4758                    'error' => 'Archivo no encontrado',
4759                ], 404);
4760            }
4761
4762            if (! is_null($file->file_hash) && ! empty($file->file)) {
4763                $fileContent = $file->file;
4764                $mimeType = $file->mime_type ?? 'application/octet-stream';
4765                $filename = $file->original_name ?? $file->filename ?? 'download';
4766
4767                return response($fileContent)
4768                    ->header('Content-Type', $mimeType)
4769                    ->header('Content-Disposition', 'attachment; filename="' . $filename . '"')
4770                    ->header('Content-Length', strlen((string) $fileContent))
4771                    ->header('Cache-Control', 'no-cache, no-store, must-revalidate');
4772
4773            } else {
4774                $filePath = Storage::disk('s3')->get('uploads/'.$file->filename);
4775                // $filePath = storage_path('app/public/uploads/' . $file->filename);
4776
4777                if (! Storage::disk('s3')->exists('uploads/'.$file->filename)) {
4778                    return response()->json([
4779                        'message' => 'KO',
4780                        'error' => 'Archivo físico no encontrado: '.$file->filename,
4781                    ], 404);
4782                }
4783
4784                $fileSize = filesize($filePath);
4785                $mimeType = mime_content_type($filePath) ?: 'application/octet-stream';
4786                $filename = $file->original_name ?? $file->filename;
4787
4788                $fileContent = file_get_contents($filePath);
4789
4790                return response($fileContent)
4791                    ->header('Content-Type', $mimeType)
4792                    ->header('Content-Disposition', 'attachment; filename="'.$filename.'"')
4793                    ->header('Content-Length', $fileSize)
4794                    ->header('Cache-Control', 'no-cache, no-store, must-revalidate');
4795            }
4796
4797        } catch (\Exception $e) {
4798            report(AppException::fromException($e, 'DOWNLOAD_FILE_EXCEPTION'));
4799            \Log::error('Error downloading file: ' . $e->getMessage());
4800            return response()->json([
4801                'message' => 'KO',
4802                'error' => 'Error interno del servidor: '.$e->getMessage(),
4803            ], 500);
4804        }
4805    }
4806
4807    function delete_file($fileId): ResponseFactory|HttpResponse{
4808
4809        try {
4810
4811            $fileId = addslashes((string) $fileId);
4812            $file = TblFiles::where('file_id', $fileId)->first();
4813            $result = TblFiles::where('file_id', $fileId)->first();
4814
4815            if ($result) {
4816                TblFiles::where('file_id', $fileId)->delete();
4817                $path = storage_path('app/public/uploads/'.$result->filename);
4818                Storage::disk('public')->delete('uploads/'.$result->filename);
4819
4820                if(!config('services.sendgrid.staging')){
4821                    Storage::disk('s3')->delete('uploads/' . $result->filename);
4822                }
4823            }
4824
4825            $fileCount = TblFiles::where('quotation_id', $file->quotation_id)->count();
4826            $data = [];
4827            if($fileCount > 0){
4828                $data['has_attachment'] = 1;
4829            } else {
4830                $data['has_attachment'] = 0;
4831            }
4832
4833            TblQuotations::where('quote_id', $file->quote_id)->update($data);
4834
4835            $this->addUpdateLog($file->quote_id, 'user', 'delete_attachment', $file->filename, null, 4);
4836
4837            return response(['message' => 'OK']);
4838
4839        } catch (\Exception $e) {
4840            report(AppException::fromException($e, 'DELETE_FILE_EXCEPTION'));
4841            return response(['message' => 'KO', 'error' => $e->getMessage()]);
4842        }
4843    }
4844
4845    function send_email_to_client(Request $request): ResponseFactory|HttpResponse{
4846
4847        ini_set('max_execution_time', 0);
4848
4849        try {
4850
4851            $data = $request->all();
4852
4853            // $id = addslashes($data['id']);
4854            $userId = addslashes((string) $data['user_id']);
4855            $companyId = addslashes((string) $data['company_id']);
4856
4857            $isResend = null;
4858
4859            if (isset($data['is_resend'])) {
4860                $isResend = 1;
4861                unset($data['is_resend']);
4862            }
4863
4864            $where = '';
4865            $emailTemplateId = $data['email_template_id'];
4866            unset($data['email_template_id']);
4867
4868            $emailTemplates = [];
4869
4870            if (isset($data['html'])) {
4871                $emailTemplates = array_column($data['html'], null, 'id');
4872            }
4873
4874            $emailTemplate = TblEmailConfiguration::whereIn('id', $emailTemplateId)->get()->keyBy('company_id');
4875
4876            $emailCompany = [];
4877            
4878            $result = [];
4879
4880            $result = [];
4881
4882            if ($companyId == 0) {
4883                $indexes = $emailTemplate->keys();
4884
4885                $emailCompany = TblCompanies::whereIn('company_id', $indexes)->get()->keyBy('company_id');
4886                $limit = 21;
4887
4888                foreach ($emailCompany as $item) {
4889                    $r = new Request([
4890                        'filterModel' => $data['filterModel'],
4891                        'sortModel' => $data['sortModel'],
4892                        'start' => 0,
4893                        'end' => 999999999,
4894                        'company_id' => $data['company_id'],
4895                        'user_id' => $data['user_id'],
4896                        'ids' => $data['ids'],
4897                        'searchText' => $data['searchText'],
4898                        'ids_not_in' => $data['ids_not_in'],
4899                    ]);
4900
4901                    $listQuotations = $this->list_quotations($r);
4902                    $d = $listQuotations->original['data'];
4903
4904                    if ($item->limit_send != null) {
4905                        $limit = $item->limit_send;
4906                    }
4907                    $l = 0;
4908                    for ($i = 0; $i < count($d); $i++) {
4909                        if ($d[$i]->email != null
4910                            && ! $this->isBlacklistedEmail($d[$i]->email)
4911                            && $d[$i]->budget_type_id != null
4912                            && ($d[$i]->budget_status_id == 11 || $isResend == 1)
4913                            && $item->company_id == $d[$i]->company_id
4914                        ) {
4915                            if ($l == $limit) {
4916                                break;
4917                            }
4918                            array_push($result, $d[$i]);
4919                            $l++;
4920                        } elseif ($d[$i]->email != null && $this->isBlacklistedEmail($d[$i]->email)) {
4921                            TblQuotations::where('id', $d[$i]->id)->update(['budget_status_id' => 22]);
4922                        }
4923                    }
4924                }
4925            } else {
4926
4927                $emailCompany = TblCompanies::where('company_id', $companyId)->get()->keyBy('company_id');
4928
4929                $limit = 21;
4930
4931                if ($emailCompany[$companyId]->limit_send != null) {
4932                    $limit = $emailCompany[$companyId]->limit_send;
4933                }
4934
4935                $r = new Request([
4936                    'filterModel' => $data['filterModel'],
4937                    'sortModel' => $data['sortModel'],
4938                    'start' => 0,
4939                    'end' => 999999999,
4940                    'company_id' => $data['company_id'],
4941                    'user_id' => $data['user_id'],
4942                    'ids' => $data['ids'],
4943                    'searchText' => $data['searchText'],
4944                    'ids_not_in' => $data['ids_not_in'],
4945                ]);
4946
4947                $listQuotations = $this->list_quotations($r);
4948                $d = $listQuotations->original['data'];
4949                $l = 0;
4950                for ($i = 0; $i < count($d); $i++) {
4951                    if ($d[$i]->email != null
4952                        && ! $this->isBlacklistedEmail($d[$i]->email)
4953                        && ($d[$i]->budget_status_id == 11 || $isResend == 1)
4954                    ) {
4955                        if ($l == $limit) {
4956                            break;
4957                        }
4958                        array_push($result, $d[$i]);
4959                        $l++;
4960                    } elseif ($d[$i]->email != null && $this->isBlacklistedEmail($d[$i]->email)) {
4961                        TblQuotations::where('id', $d[$i]->id)->update(['budget_status_id' => 21]);
4962                    }
4963                }
4964            }
4965
4966            if (count($result) == 0) {
4967                return response(['message' => 'OK', $d ?? []]);
4968            }
4969
4970            $user = TblUsers::where('id', $userId)->first();
4971            $error = false;
4972
4973            $availableParameters = [
4974                'quote_id',
4975                'company_id',
4976                'client',
4977                'client_type',
4978                'phone_number',
4979                'email',
4980                'issue_date',
4981                'request_date',
4982                'duration',
4983                'invoice_number',
4984                'type',
4985                'acceptance_date',
4986                'status',
4987                'source',
4988                'amount',
4989                'reason_for_not_following_up',
4990                'last_follow_up_date',
4991                'last_follow_up_comment',
4992                'reason_for_rejection_id',
4993                'reason_for_rejection',
4994                'commercial',
4995                'created_at',
4996                'created_by',
4997                'updated_at',
4998                'updated_by',
4999            ];
5000
5001            $dateParameters = [
5002                'issue_date',
5003                'request_date',
5004                'acceptance_date',
5005                'last_follow_up_date',
5006                'created_at',
5007                'updated_at',
5008            ];
5009
5010            if($this->locale == 'es'){
5011                setlocale(LC_ALL, "es_ES", 'Spanish_Spain', 'Spanish');
5012                setlocale(LC_ALL, "es_ES", 'Spanish_Spain', 'Spanish');
5013            }
5014
5015            $sentItems = [];
5016            
5017            for ($i = 0; $i < count($result); $i++) {
5018
5019                $body = $emailTemplate[$result[$i]->company_id]->html;
5020                if (isset($data['html']) && ! empty($data['html'])) {
5021                    $body = $emailTemplates[$result[$i]->company_id]['html'];
5022                }
5023                $subject = $emailTemplate[$result[$i]->company_id]->subject;
5024                $commercialUser = $result[$i]->commercial;
5025
5026                if ($result[$i]->email != null) {
5027
5028                    if(config('services.sendgrid.staging')){
5029                        $toEmail = $user->email;
5030                    } else {
5031                        $toEmail = $result[$i]->email;
5032                    }
5033
5034                    preg_match_all('/{{(.*?)}}/', (string) $body, $matches);
5035
5036                    $parameters = $matches[1];
5037
5038                    foreach ($parameters as $parameter) {
5039
5040                        if(in_array($parameter, $dateParameters)){
5041                            if($result[$i]->{$parameter}){
5042                                $result[$i]->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result[$i]->{$parameter})));
5043                            }
5044                        }
5045
5046                        if (in_array($parameter, $availableParameters)) {
5047                            $body = str_replace('{{'.$parameter.'}}', $result[$i]->{$parameter}, $body);
5048                        }
5049                    }
5050
5051                    preg_match_all('/{{(.*?)}}/', (string) $subject, $matches);
5052
5053                    $parameters = $matches[1];
5054
5055                    foreach ($parameters as $parameter) {
5056
5057                        if(in_array($parameter, $dateParameters)){
5058                            if($result[$i]->{$parameter}){
5059                                $result[$i]->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result[$i]->{$parameter})));
5060                            }
5061                        }
5062
5063                        if (in_array($parameter, $availableParameters)) {
5064                            $subject = str_replace('{{'.$parameter.'}}', $result[$i]->{$parameter}, $subject);
5065                        }
5066                    }
5067
5068                    $email = new \SendGrid\Mail\Mail;
5069                    $templateFiles = TblEmailFiles::where('email_template_id', $emailTemplate[$result[$i]->company_id]->id)->orderBy('order', 'asc')->get();
5070
5071                    foreach ($templateFiles as $item) {
5072                        $f = storage_path('app/public/uploads/'.$item->filename);
5073
5074                        if (file_exists($f)) {
5075                            $imgpath = file_get_contents($f);
5076                            $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
5077                            $mimeType = mime_content_type($f);
5078
5079                            $email->addAttachment(
5080                                $imgpath,
5081                                $mimeType,
5082                                str_replace(' ', '', $item->original_name),
5083                                'inline',
5084                                str_replace(' ', '', $item->original_name),
5085                            );
5086
5087                            $body .= "<img src='cid:{$item->original_name}' style='height: 45px; padding-right: 6px' />";
5088                        } else {
5089                            Log::channel('email_failed_log')->error('File not found: '.$f);
5090                        }
5091                    }
5092
5093                    $html = '<!DOCTYPE html>';
5094                    $html .= '<html>';
5095                    $html .= '<head>';
5096                    $html .= '<meta charset="UTF-8">';
5097                    $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
5098                    $html .= '</head>';
5099                    $html .= '<body>';
5100                    $html .= $body;
5101                    $html .= '</body>';
5102                    $html .= '</html>';
5103
5104                    if ($toEmail != null) {
5105
5106                        $toEmail = explode(",", (string) $toEmail);
5107                        $toEmail = array_map(trim(...), $toEmail);
5108
5109                        $companyEmail = null;
5110
5111                        $queryUsers = "SELECT sender_email AS from_email, `name` AS from_name FROM tbl_users WHERE sender_enabled = 1 AND response_id IS NOT NULL AND verified = 1 AND `name` = '{$commercialUser}'";
5112                        $commercialEmail = DB::select($queryUsers);
5113
5114                        if (count($commercialEmail) > 0) {
5115                            $companyEmail = $commercialEmail[0];
5116                        } else {
5117                            if ($emailTemplate[$result[$i]->company_id]->from_id != null) {
5118                                $companyEmail = TblCompanyEmails::where('id', $emailTemplate[$result[$i]->company_id]->from_id)->first();
5119                            } else {
5120                                $companyEmail = TblCompanyEmails::where('is_active', 1)->where('verified', 1)->where('company_id', $result[$i]->company_id)->first();
5121                            }
5122                        }
5123
5124                        if (! $companyEmail) {
5125                            return response(['message' => 'KO', 'error' => __('language.no_active_verified_sender')]);
5126                        }
5127
5128                        $ccBcc = TblCcBcc::where('company_id', $result[$i]->company_id)->get();
5129
5130                        $email->setFrom($companyEmail->from_email, $companyEmail->from_name);
5131                        $email->setSubject($subject);
5132
5133                        foreach ($toEmail as $clientEmail) {
5134                            $isValid = $this->isEmailValid($clientEmail);
5135                            if ($isValid) {
5136                                $email->addTo($clientEmail);
5137                            }
5138                        }
5139
5140                        if (!config('services.sendgrid.staging')) {
5141                            if(!in_array($user->email, $toEmail)){
5142                                $email->addCc($user->email);
5143                            }
5144                            if(count($ccBcc) > 0){
5145                                foreach ($ccBcc as $data) {
5146                                    if (! in_array($data->email, $toEmail) && $user->email != $data->email) {
5147                                        $email->addBcc($data->email);
5148                                    }
5149                                }
5150                            }
5151                        }
5152
5153                        $email->addContent("text/html", $html);
5154                        $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
5155
5156                        $files = TblFiles::where('quotation_id', $result[$i]->id)->where('is_internal', null)->get();
5157                        $requestSize = $this->calculateEmailRequestSize($email);
5158
5159                        foreach ($files as $key => $value) {
5160                            $fileContent = null;
5161                            $fileSize = 0;
5162                            $fileName = null;
5163
5164                            if (Storage::disk('s3')->exists("uploads/" . $files[$key]->filename)) {                                
5165                                $fileContent = Storage::disk('s3')->get("uploads/" . $files[$key]->filename);                                
5166                                $fileSize = (int) ceil(strlen((string) $fileContent) / 1048576);                                
5167                                $fileName = $files[$key]->original_name;                                                            
5168                            }
5169                            else if ($files[$key]->file) {
5170                                $fileContent = $files[$key]->file;
5171
5172                                if (is_string($fileContent) && base64_decode($fileContent, true)) {
5173                                    $fileContent = base64_decode($fileContent);
5174                                }
5175
5176                                $fileSize = strlen((string) $fileContent) / 1048576;
5177                                $fileSize = (int) ceil($fileSize);
5178                                $fileName = $files[$key]->original_name ?: $files[$key]->getAttribute('file_name') ?: 'file';
5179                            }
5180
5181                            if ($fileContent && $fileSize > 0) {
5182                                if ($requestSize + $fileSize < 25) {
5183                                    $attachment = new \SendGrid\Mail\Attachment;
5184                                    $attachment->setFilename($fileName);
5185                                    $attachment->setDisposition('attachment');
5186
5187                                    $attachment->setContent(base64_encode((string) $fileContent));
5188
5189                                    $email->addAttachment($attachment);
5190                                    $requestSize = $this->calculateEmailRequestSize($email);
5191                                }
5192                            }
5193                        }
5194
5195                        $sentWithBackup = false;
5196                        $response = $sendgrid->send($email);
5197
5198                        if (config('services.sendgrid.block') === true) {
5199    
5200                            Log::warning("SendGrid failed with status {$response->statusCode()}. Initiating Failover...");
5201                            try {
5202                                \Illuminate\Support\Facades\Mail::mailer('resend')->send([], [], function ($message) use ($html, $companyEmail, $toEmail, $subject, $files, $user, $ccBcc): void {
5203                                    $message->from($companyEmail->from_email, $companyEmail->from_name)
5204                                        ->to($toEmail)
5205                                        ->subject($subject);
5206
5207                                    $message->html($html);
5208
5209                                    $message->cc($user->email);
5210
5211                                    foreach ($ccBcc as $bcc) {
5212                                        $message->bcc($bcc->email);
5213                                    }
5214
5215                                    foreach ($files as $file) {
5216                                        if (Storage::disk('s3')->exists('uploads/'.$file->filename)) {
5217                                            $content = Storage::disk('s3')->get('uploads/'.$file->filename);
5218                                            $message->attachData($content, $file->original_name);
5219                                        } elseif ($file->file) {
5220                                            $content = $file->file;
5221                                            if (is_string($content) && base64_decode($content, true)) {
5222                                                $content = base64_decode($content);
5223                                            }
5224                                            $message->attachData($content, $file->original_name ?: 'archivo.pdf');
5225                                        }
5226                                    }
5227                                });
5228
5229                                $sentWithBackup = true;
5230                            } catch (\Exception $backupException) {
5231                                $sentWithBackup = false;
5232                                Log::error('Backup server also failed: '.$backupException->getMessage());
5233                            }
5234
5235                            if (! $sentWithBackup) {
5236                                throw new \Exception("SendGrid failed with status {$response->statusCode()}. Backup server also failed: ".$backupException->getMessage());
5237                            }
5238
5239                        } 
5240                        
5241                        if (($response->statusCode() == 202 || $sentWithBackup) && !config('services.sendgrid.block')) {
5242                            $messageId = null;
5243
5244                            if ($response->headers()) {
5245                                foreach ($response->headers() as $header) {
5246                                    if (str_starts_with(strtolower((string) $header), 'x-message-id:')) {
5247                                        $messageId = trim(substr((string) $header, strpos((string) $header, ':') + 1));
5248                                        break;
5249                                    }
5250                                }
5251                            }
5252
5253                            $sentItems[$result[$i]->company_id][] = $result[$i]->id;
5254
5255                            $comment = 'Se ha enviado la orden por correo electrónico manualmente al cliente el '.date('Y-m-d H:i:s').' por el usuario '.$user->name;
5256                            $result[$i]->last_follow_up_comment = $result[$i]->last_follow_up_comment."\n".$comment;
5257
5258                            TblQuotations::where('id', $result[$i]->id)->update(
5259                                [
5260                                    'last_follow_up_comment' => $result[$i]->last_follow_up_comment,
5261                                    'budget_status_id' => 2,
5262                                    'x_message_id' => $messageId,
5263                                    'x_status' => 'Processing',
5264                                    'updated_by' => $user->name,
5265                                    'updated_at' => date('Y-m-d H:i:s')
5266                                ]
5267                            );
5268
5269                            $jsonBody = [];
5270
5271                            foreach ($toEmail as $clientEmail) {
5272                                $isValid = $this->isEmailValid($clientEmail);
5273                                $eventStatus = 'processed';
5274                                $eventResponse = '';
5275                                if (! $isValid) {
5276                                    $eventStatus = 'invalid';
5277                                    $eventResponse = 'Invalid email address';
5278                                }
5279
5280                                array_push(
5281                                    $jsonBody,
5282                                    [
5283                                        'email' =>  $clientEmail,
5284                                        'event' => $eventStatus,
5285                                        'sg_message_id' => $messageId,
5286                                        'smtp-id' => $messageId,
5287                                        'timestamp' => strtotime(date('Y-m-d H:i:s')),
5288                                        'response' => $eventResponse
5289                                    ]
5290                                );
5291                            }
5292
5293                            TblSendgridWebhook::create(
5294                                [
5295                                    'quotation_id' => $result[$i]->id,
5296                                    'type' => 'sendToClient',
5297                                    'json_body' => json_encode($jsonBody),
5298                                    'x_message_id' => $messageId
5299                                ]
5300                            );
5301
5302                            Log::channel('email_log')->info("[RS-{$requestSize}] ID:".$result[$i]->id.' - EMAIL MANUALLY SENT');
5303                            $this->addUpdateLog($result[$i]->id, $userId, 'send_email_to_client', null, null, 5);
5304                        } else {
5305                            $error = true;
5306                            Log::channel('email_failed_log')->error("[RS-{$requestSize}] ID:".$result[$i]->id.' - '.$response->body());
5307                        }
5308                    }
5309                }
5310            }
5311
5312            $this->update_commercial_numbers($companyId);
5313
5314            Cache::flush();
5315
5316            foreach ($sentItems as $k => $v) {
5317                $sentItems[$k] = [
5318                    'total' => count($sentItems[$k]),
5319                    'company' => $emailCompany[$k]->name,
5320                    'limit' => $emailCompany[$k]->limit_send,
5321                ];
5322            }
5323
5324            $totalSent = array_values($sentItems);
5325
5326            return response(['message' => 'OK', 'data' => $totalSent]);
5327
5328        } catch (\Exception $e) {
5329            report(AppException::fromException($e, 'SEND_EMAIL_TO_CLIENT_EXCEPTION'));
5330            Log::channel('email_failed_log')->error($e->getMessage());
5331
5332            return response(['message' => 'KO', 'error' => $e->getMessage()]);
5333        }
5334
5335    }
5336
5337    function send_email_follow_ups(Request $request, $automaticSendLimit = null): ResponseFactory|HttpResponse{
5338
5339        $currentQuotationId = null;
5340        ini_set('max_execution_time', 0);
5341
5342        try {
5343
5344            $data = $request->all();
5345
5346            $startedAt = date('Y-m-d H:i:s');
5347            $toEmail = "";
5348            $userId = addslashes((string) $data['user_id']);
5349            $companyId = addslashes((string) $data['company_id']);
5350            $emailTemplateId = $data['email_template_id'];
5351            unset($data['email_template_id']);
5352
5353            $emailTemplates = [];
5354
5355            if (isset($data['html']) && ! empty($data['html'])) {
5356                $emailTemplates = array_column($data['html'], null, 'id');
5357            }
5358
5359            $emailTemplate = TblEmailConfiguration::whereIn('id', $emailTemplateId)->get()->keyBy('company_id');
5360
5361            $limit = 0;
5362
5363            $blockedDomains = [];
5364            $workingDays = 10;
5365            $limitReminderEmails = 3;
5366            $emailCompany = [];
5367
5368            $result = [];
5369
5370            if ($companyId == 0) {
5371                $indexes = $emailTemplate->keys();
5372
5373                $emailCompany = TblCompanies::whereIn('company_id', $indexes)->get()->keyBy('company_id');
5374
5375                foreach ($emailCompany as $item) {
5376                    $limit = 20;
5377
5378                    $r = new Request([
5379                        'filterModel' => $data['filterModel'],
5380                        'sortModel' => $data['sortModel'],
5381                        'start' => 0,
5382                        'end' => 999999999,
5383                        'company_id' => $data['company_id'],
5384                        'user_id' => $data['user_id'],
5385                        'ids' => $data['ids'],
5386                        'searchText' => $data['searchText'],
5387                        'ids_not_in' => $data['ids_not_in'],
5388                        'last_follow_up_date' => 1,
5389                    ]);
5390
5391                    if ($item->limit_send != null) {
5392                        $limit = $item->limit_send;
5393                    }
5394
5395                    $listQuotations = $this->list_quotations($r);
5396                    $d = $listQuotations->original['data'];
5397                    if ($automaticSendLimit != null) {
5398                        $limit = $automaticSendLimit;
5399                    }
5400
5401                    $l = 0;
5402                    for ($i = 0; $i < count($d); $i++) {
5403                        if ($d[$i]->email != null
5404                            && $d[$i]->budget_type_id != null
5405                            && $d[$i]->budget_status_id == 2
5406                            && $d[$i]->reason_for_not_following_up_id == null
5407                            && $d[$i]->total_sent < $limitReminderEmails
5408                            && $d[$i]->last_follow_up_date != null
5409                            && $d[$i]->last_follow_up_date < date('Y-m-d H:i:s')
5410                            && $d[$i]->last_follow_up_date > 0
5411                            && $item->company_id == $d[$i]->company_id
5412                        ) {
5413
5414                            if ($l == $limit) {
5415                                break;
5416                            }
5417                            array_push($result, $d[$i]);
5418                            $l++;
5419                        }
5420                    }
5421
5422                }
5423            } else {
5424
5425                $emailCompany = TblCompanies::where('company_id', $companyId)->get()->keyBy('company_id');
5426                $workingDays = $emailCompany[$companyId]->last_follow_up_date ?? 10;
5427                $limitReminderEmails = $emailCompany[$companyId]->limit_reminder_emails ?? 3;
5428
5429                if ($emailCompany[$companyId]->limit_send != null) {
5430                    $limit = $emailCompany[$companyId]->limit_send;
5431                }
5432
5433                $r = new Request([
5434                    'filterModel' => $data['filterModel'],
5435                    'sortModel' => $data['sortModel'],
5436                    'start' => 0,
5437                    'end' => 999999999,
5438                    'company_id' => $data['company_id'],
5439                    'user_id' => $data['user_id'],
5440                    'ids' => $data['ids'],
5441                    'searchText' => $data['searchText'],
5442                    'ids_not_in' => $data['ids_not_in'],
5443                    'last_follow_up_date' => 1,
5444                ]);
5445
5446                $listQuotations = $this->list_quotations($r);
5447                $d = $listQuotations->original['data'];
5448
5449                if ($automaticSendLimit != null) {
5450                    $limit = $automaticSendLimit;
5451                }
5452
5453                $l = 0;
5454                for ($i = 0; $i < count($d); $i++) {
5455                    if ($d[$i]->email != null
5456                        && $d[$i]->budget_status_id == 2
5457                        && $d[$i]->reason_for_not_following_up_id == null
5458                        && $d[$i]->total_sent < $limitReminderEmails
5459                        && $d[$i]->last_follow_up_date != null
5460                        && $d[$i]->last_follow_up_date < date('Y-m-d H:i:s')
5461                        && $d[$i]->last_follow_up_date > 0
5462                    ) {
5463                        if ($l == $limit) {
5464                            break;
5465                        }
5466                        array_push($result, $d[$i]);
5467                        $l++;
5468                    }
5469                }
5470            }
5471
5472            if (count($result) == 0) {
5473                return response(['message' => 'OK']);
5474            }
5475
5476            $user = TblUsers::where('id', $userId)->first();
5477            $error = false;
5478
5479            $sentBy = $user->name;
5480
5481            $availableParameters = [
5482                'quote_id',
5483                'company_id',
5484                'client',
5485                'client_type',
5486                'phone_number',
5487                'email',
5488                'issue_date',
5489                'request_date',
5490                'duration',
5491                'invoice_number',
5492                'type',
5493                'acceptance_date',
5494                'status',
5495                'source',
5496                'amount',
5497                'reason_for_not_following_up',
5498                'last_follow_up_date',
5499                'last_follow_up_comment',
5500                'reason_for_rejection_id',
5501                'reason_for_rejection',
5502                'commercial',
5503                'created_at',
5504                'created_by',
5505                'updated_at',
5506                'updated_by',
5507            ];
5508
5509            $dateParameters = [
5510                'issue_date',
5511                'request_date',
5512                'acceptance_date',
5513                'last_follow_up_date',
5514                'created_at',
5515                'updated_at',
5516            ];
5517
5518            if ($this->locale == 'es') {
5519                setlocale(LC_ALL, 'es_ES', 'Spanish_Spain', 'Spanish');
5520            }
5521
5522            $totalSent = 0;
5523            $totalSentIds = [];
5524            $totalFailedIds = [];
5525            $totalErrorIds = [];
5526            $currentQuotationId = null;       
5527            
5528            $sentItems = [];
5529
5530            for ($i = 0; $i < count($result); $i++) {
5531                $budgetTypeId = $result[$i]->budget_type_id;
5532                $currentQuotationId = $result[$i]->quote_id;
5533                $body = $emailTemplate[$result[$i]->company_id]->html;
5534                if (isset($data['html']) && ! empty($data['html'])) {
5535                    $body = $emailTemplates[$result[$i]->company_id]['html'];
5536                }
5537                $subject = $emailTemplate[$result[$i]->company_id]->subject;
5538                $commercialUser = $result[$i]->commercial;
5539
5540                $blockedDomains = TblBlockedDomains::where('company_id', $result[$i]->company_id)->pluck('domain')->toArray();
5541
5542                if(config('services.sendgrid.staging')){
5543                    $toEmail = $user->email;
5544                } else {
5545                    $toEmail = $result[$i]->email;
5546                }
5547
5548                preg_match_all('/{{(.*?)}}/', (string) $body, $matches);
5549
5550                $parameters = $matches[1];
5551
5552                foreach ($parameters as $parameter) {
5553
5554                    if(in_array($parameter, $dateParameters)){
5555                        if($result[$i]->{$parameter}){
5556                            $result[$i]->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result[$i]->{$parameter})));
5557                        }
5558                    }
5559
5560                    if (in_array($parameter, $availableParameters)) {
5561                        $body = str_replace('{{'.$parameter.'}}', $result[$i]->{$parameter}, $body);
5562                    }
5563                }
5564
5565                preg_match_all('/{{(.*?)}}/', (string) $subject, $matches);
5566
5567                $parameters = $matches[1];
5568
5569                foreach ($parameters as $parameter) {
5570
5571                    if(in_array($parameter, $dateParameters)){
5572                        if($result[$i]->{$parameter}){
5573                            $result[$i]->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result[$i]->{$parameter})));
5574                        }
5575                    }
5576
5577                    if (in_array($parameter, $availableParameters)) {
5578                        $subject = str_replace('{{'.$parameter.'}}', $result[$i]->{$parameter}, $subject);
5579                    }
5580                }
5581
5582                $email = new \SendGrid\Mail\Mail;
5583
5584                $templateFiles = TblEmailFiles::where('email_template_id', $emailTemplate[$result[$i]->company_id]->id)->orderBy('order', 'asc')->get();
5585
5586                foreach ($templateFiles as $item) {
5587                    $f = storage_path('app/public/uploads/'.$item->filename);
5588
5589                    if (file_exists($f)) {
5590                        $imgpath = file_get_contents($f);
5591                        $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
5592                        $mimeType = mime_content_type($f);
5593
5594                        $email->addAttachment(
5595                            $imgpath,
5596                            $mimeType,
5597                            str_replace(' ', '', $item->original_name),
5598                            'inline',
5599                            str_replace(' ', '', $item->original_name),
5600                        );
5601
5602                        $body .= "<img src='cid:{$item->original_name}' style='height: 45px; padding-right: 6px' />";
5603                    } else {
5604                        Log::channel('email_failed_log')->error('File not found: '.$f);
5605                    }
5606                }
5607
5608                $html = '<!DOCTYPE html>';
5609                $html .= '<html>';
5610                $html .= '<head>';
5611                $html .= '<meta charset="UTF-8">';
5612                $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
5613                $html .= '</head>';
5614                $html .= '<body>';
5615                $html .= $body;
5616                $html .= '</body>';
5617                $html .= '</html>';
5618
5619                if ($automaticSendLimit != null) {
5620                    $sentBy = 'System';
5621                }
5622
5623                if ($toEmail != null) {
5624
5625                    $toEmail = explode(",", (string) $toEmail);
5626                    $toEmail = array_map(trim(...), $toEmail);
5627
5628                    $companyEmail = null;
5629
5630                    $queryUsers = "SELECT sender_email AS from_email, `name` AS from_name FROM tbl_users WHERE sender_enabled = 1 AND response_id IS NOT NULL AND verified = 1 AND `name` = '{$commercialUser}'";
5631                    $commercialEmail = DB::select($queryUsers);
5632
5633                    if (count($commercialEmail) > 0) {
5634                        $companyEmail = $commercialEmail[0];
5635                    } else {
5636                        if ($emailTemplate[$result[$i]->company_id]->from_id != null) {
5637                            $companyEmail = TblCompanyEmails::where('id', $emailTemplate[$result[$i]->company_id]->from_id)->first();
5638                        } else {
5639                            $companyEmail = TblCompanyEmails::where('is_active', 1)->where('verified', 1)->where('company_id', $result[$i]->company_id)->first();
5640                        }
5641                    }
5642
5643                    if (! $companyEmail) {
5644                        return response(['message' => 'KO', 'error' => __('language.no_active_verified_sender')]);
5645                    }
5646
5647                    $ccBcc = TblCcBcc::where('company_id', $result[$i]->company_id)->get();
5648
5649                    $email->setFrom($companyEmail->from_email, $companyEmail->from_name);
5650                    $email->setSubject($subject);
5651
5652                    $s = 0;
5653                    $addTo = [];
5654                    foreach ($toEmail as $clientEmail) {
5655                        $isValid = $this->isEmailValid($clientEmail);
5656                        if ($isValid) {
5657                            $domain = substr($clientEmail, strpos($clientEmail, '@') + 1);
5658
5659                            if (! in_array($domain, $blockedDomains)) {
5660                                $email->addTo($clientEmail);
5661                                array_push($addTo, $clientEmail);
5662                                $s++;
5663                            }
5664                        } else {
5665                            TblFollowUpLogs::create(
5666                                [
5667                                    'quotation_id' => $result[$i]->id,
5668                                    'email' => $clientEmail,
5669                                    'sent_by' => $sentBy,
5670                                    'status' => 'Invalid email'
5671                                ]
5672                            );
5673                        }
5674                    }
5675
5676                    if ($s == 0) {
5677                        array_push($totalFailedIds, $result[$i]->id);
5678                        Log::channel('email_failed_log')->error($s.'ID:'.$result[$i]->id.' - '.json_encode($toEmail));
5679
5680                        continue;
5681                    }
5682
5683                    if (!config('services.sendgrid.staging')) {
5684                        if(count($ccBcc) > 0){
5685                            foreach ($ccBcc as $data) {
5686                                if (! in_array($data->email, $toEmail)) {
5687                                    $email->addBcc($data->email);
5688                                }
5689                            }
5690                        }
5691                    }
5692
5693                    $email->addContent('text/html', $html);
5694
5695                    $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
5696
5697                    $files = TblFiles::where('quotation_id', $result[$i]->id)->where('is_internal', null)->get();
5698                    $requestSize = $this->calculateEmailRequestSize($email);
5699
5700                    foreach ($files as $key => $value) {
5701                        if ($files[$key]->filename && Storage::disk('s3')->exists("uploads/" . $files[$key]->filename)) {
5702                                                        
5703                            $fileContent = Storage::disk('s3')->get("uploads/" . $files[$key]->filename);
5704                            $fileSize = strlen((string) $fileContent) / 1048576;
5705                            $fileSize = (int) ceil($fileSize);
5706
5707                            $originalName = $files[$key]->original_name ?: basename((string) $files[$key]->filename);
5708                        }
5709                        else if ($files[$key]->file) {
5710                            $fileContent = $files[$key]->file;
5711
5712                            if (is_string($fileContent) && base64_decode($fileContent, true)) {
5713                                $fileContent = base64_decode($fileContent);
5714                            }
5715
5716                            $fileSize = strlen((string) $fileContent) / 1048576;
5717                            $fileSize = (int) ceil($fileSize);
5718
5719                            $originalName = $files[$key]->original_name ?: ($files[$key]->getAttribute('file_name') ?: 'file');
5720                        } else {
5721                            continue;
5722                        }
5723
5724                        if ($fileSize > 0) {
5725                            if ($requestSize + $fileSize < 25) {
5726                                $attachment = new \SendGrid\Mail\Attachment;
5727                                $attachment->setFilename($originalName);
5728                                $attachment->setDisposition('attachment');
5729
5730                                $attachment->setContent(base64_encode((string) $fileContent));
5731
5732                                if ($files[$key]->mime_type) {
5733                                    $attachment->setType($files[$key]->mime_type);
5734                                }
5735
5736                                $email->addAttachment($attachment);
5737                                $requestSize = $this->calculateEmailRequestSize($email);
5738                            } else {
5739                                Log::warning('File omitted due to size limit: '.$originalName);
5740                            }
5741                        }
5742                    }
5743
5744                    $sentWithBackup = false;
5745                    $response = $sendgrid->send($email);
5746
5747                    if (config('services.sendgrid.block') === true) {
5748    
5749                        Log::warning("SendGrid failed with status {$response->statusCode()}. Initiating Failover...");
5750                        try {
5751                            \Illuminate\Support\Facades\Mail::mailer('smtp_backup')->send([], [], function ($message) use ($html, $companyEmail, $toEmail, $subject, $files, $user, $ccBcc): void {
5752                                $message->from($companyEmail->from_email, $companyEmail->from_name)
5753                                    ->to($toEmail)
5754                                    ->subject($subject);
5755
5756                                $message->html($html);
5757
5758                                $message->cc($user->email);
5759
5760                                foreach ($ccBcc as $bcc) {
5761                                    $message->bcc($bcc->email);
5762                                }
5763
5764                                foreach ($files as $file) {
5765                                    if (Storage::disk('s3')->exists('uploads/'.$file->filename)) {
5766                                        $content = Storage::disk('s3')->get('uploads/'.$file->filename);
5767                                        $message->attachData($content, $file->original_name);
5768                                    } elseif ($file->file) {
5769                                        $content = $file->file;
5770                                        if (is_string($content) && base64_decode($content, true)) {
5771                                            $content = base64_decode($content);
5772                                        }
5773                                        $message->attachData($content, $file->original_name ?: 'archivo.pdf');
5774                                    }
5775                                }
5776                            });
5777
5778                            $sentWithBackup = true;
5779                        } catch (\Exception $backupException) {
5780                            $sentWithBackup = false;
5781                            Log::error('Backup server also failed: '.$backupException->getMessage());
5782                        }
5783
5784                        if (! $sentWithBackup) {
5785                            throw new \Exception("SendGrid failed with status {$response->statusCode()}. Backup server also failed: ".$backupException->getMessage());
5786                        }
5787
5788                    }
5789
5790                    if (($response->statusCode() == 202 || $sentWithBackup) && !config('services.sendgrid.block')) {
5791
5792                        $messageId = null;
5793
5794                        foreach ($response->headers() as $header) {
5795                            if (str_starts_with(strtolower((string) $header), 'x-message-id:')) {
5796                                $messageId = trim(substr((string) $header, strpos((string) $header, ':') + 1));
5797                                break;
5798                            }
5799                        }
5800
5801                        $lastFollowUp = TblLastFollowUpDate::where('company_id', $companyId)->where('budget_type_id', $budgetTypeId)->first();
5802                        $workingDaysN = $workingDays;
5803
5804                        if ($companyId == 0) {
5805                            $workingDaysN = $emailCompany[$result[$i]->company_id]->last_follow_up_date ?? 10;
5806                        }
5807
5808                        if ($lastFollowUp != null) {
5809                            if ($lastFollowUp->last_follow_up_date) {
5810                                $workingDaysN = $lastFollowUp->last_follow_up_date;
5811                            }
5812                        }
5813
5814                        $comment = 'Email automático enviado el '.date('Y-m-d H:i:s').' por usuario '.$sentBy;
5815                        $result[$i]->last_follow_up_comment = $result[$i]->last_follow_up_comment."\n".$comment;
5816                        $date = strtotime("{$workingDaysN} weekdays");
5817                        $result[$i]->last_follow_up_date = date('Y-m-d H:i:s', $date);
5818                        $totalSentQ = $result[$i]->total_sent + 1;
5819
5820                        array_push($totalSentIds, $result[$i]->id);
5821                        $sentItems[$result[$i]->company_id][] = $result[$i]->id;
5822
5823                        foreach ($addTo as $addToEmail) {
5824                            TblFollowUpLogs::create(
5825                                [
5826                                    'quotation_id' => $result[$i]->id,
5827                                    'email' => $addToEmail,
5828                                    'sent_by' => $sentBy,
5829                                    'status' => 'OK'
5830                                ]
5831                            );
5832                            $this->addUpdateLog($result[$i]->id, $userId, 'send_email_follow_up', null, null, 5);
5833                        }
5834
5835                        if ($totalSentQ >= $limitReminderEmails) {
5836                            $result[$i]->reason_for_not_following_up_id = 3;
5837                            $result[$i]->last_follow_up_date = null;
5838                        }
5839
5840                        TblQuotations::where('id', $result[$i]->id)->update(
5841                            [
5842                                'last_follow_up_comment' => $result[$i]->last_follow_up_comment,
5843                                'last_follow_up_date' => $result[$i]->last_follow_up_date,
5844                                'total_sent' => $result[$i]->total_sent + 1,
5845                                'y_message_id' => $messageId,
5846                                'y_status' => 'Processing',
5847                                'reason_for_not_following_up_id' => $result[$i]->reason_for_not_following_up_id,
5848                                'updated_by' => $sentBy,
5849                                'updated_at' => date('Y-m-d H:i:s')
5850                            ]
5851                        );
5852
5853                        $jsonBody = [];
5854
5855                        foreach ($toEmail as $clientEmail) {
5856                            $isValid = $this->isEmailValid($clientEmail);
5857                            $eventStatus = 'processed';
5858                            $eventResponse = '';
5859                            if (! $isValid) {
5860                                $eventStatus = 'invalid';
5861                                $eventResponse = 'Invalid email address';
5862                            }
5863
5864                            array_push(
5865                                $jsonBody,
5866                                [
5867                                    'email' =>  $clientEmail,
5868                                    'event' => $eventStatus,
5869                                    'sg_message_id' => $messageId,
5870                                    'smtp-id' => $messageId,
5871                                    'timestamp' => strtotime(date('Y-m-d H:i:s')),
5872                                    'response' => $eventResponse
5873                                ]
5874                            );
5875                        }
5876
5877                        TblSendgridWebhook::create(
5878                            [
5879                                'quotation_id' => $result[$i]->id,
5880                                'type' => 'followUps',
5881                                'json_body' => json_encode($jsonBody),
5882                                'x_message_id' => $messageId
5883                            ]
5884                        );
5885
5886                        Log::channel('email_log')->info("[RS-{$requestSize}] ID:".$result[$i]->id.' - EMAIL SENT');
5887                        $totalSent++;
5888                        $workingDays = 10;
5889                    } else {
5890                        $error = true;
5891                        array_push($totalErrorIds, $result[$i]->id);
5892                        Log::channel('email_failed_log')->error("[RS-{$requestSize}] ID:".$result[$i]->id.' - '.$response->body());
5893
5894                        foreach ($addTo as $addToEmail) {
5895                            TblFollowUpLogs::create(
5896                                [
5897                                    'quotation_id' => $result[$i]->id,
5898                                    'email' => $addToEmail,
5899                                    'sent_by' => $sentBy,
5900                                    'status' => $response->body()
5901                                ]
5902                            );
5903                        }
5904                    }
5905
5906                    $body = '';
5907                    $subject = '';
5908                }
5909            }
5910
5911            Cache::flush();
5912
5913            Log::channel('send_email_follow_ups')->info(
5914                json_encode(
5915                    [
5916                        "success" => $totalSentIds,
5917                        "failed" => $totalFailedIds,
5918                        "error" => $totalErrorIds
5919                    ]
5920                )
5921            );
5922
5923            $this->update_commercial_numbers($companyId);
5924
5925            if ($error) {
5926                return response(['message' => 'KO']);
5927            } else {
5928
5929                if ($automaticSendLimit != null) {
5930                    TblOrdersUpdateLogs::create(
5931                        [
5932                            'company_id' => $companyId,
5933                            'to_process' => 'Orders',
5934                            'status' => 'success',
5935                            'follow_ups_affected_rows' => $totalSent,
5936                            'processed_by' => $sentBy,
5937                            'started_at' => $startedAt,
5938                            'ended_at' => date('Y-m-d H:i:s')
5939                        ]
5940                    );
5941                }
5942
5943                foreach ($sentItems as $k => $v) {
5944                    $sentItems[$k] = [
5945                        'total' => count($sentItems[$k]),
5946                        'company' => $emailCompany[$k]->name,
5947                        'limit' => $emailCompany[$k]->limit_send,
5948                    ];
5949                }
5950
5951                $totalSent = array_values($sentItems);
5952
5953                return response(['message' => 'OK', 'data' => $totalSent]);
5954            }
5955
5956        } catch (\Throwable $e) {
5957            report(AppException::fromException($e, 'SEND_EMAIL_FOLLOW_UPS_EXCEPTION'));
5958            Log::channel('email_failed_log')->error($e->getMessage());
5959
5960            return response(['message' => 'KO', 'error' => $e->getMessage(), 'quotation_id' => $currentQuotationId]);
5961        }
5962    }
5963
5964    function create_sender_identity(Request $request): ResponseFactory|HttpResponse{
5965
5966        $data = $request->all();
5967        try {
5968
5969            $sData = $data;
5970            $companyId = $data['company_id'];
5971            $createdBy = $data['created_by'];
5972            unset($data['company_id']);
5973            unset($data['created_by']);
5974
5975            $sender = TblCompanyEmails::where('from_email', $data['from_email'])->where('verified', 1)->count();
5976
5977            if ($sender > 0) {
5978                TblCompanyEmails::create($sData);
5979
5980                return response(['message' => 'OK', 'data' => $data, 'is_verified' => 'yes']);
5981            }
5982
5983            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
5984            $data['reply_to'] = $data['from_email'];
5985            $data['reply_to_name'] = $data['from_name'];
5986            $requestBody = $data;
5987            $error = false;
5988
5989            // @phpstan-ignore-next-line
5990            $response = $sendgrid->client->verified_senders()->post($requestBody);
5991
5992            if ($response->statusCode() == 201) {
5993                $x = json_decode((string) $response->body());
5994
5995                $data['company_id'] = $companyId;
5996                $data['created_by'] = $createdBy;
5997                $data['response_id'] = $x->id;
5998                TblCompanyEmails::create($data);
5999                Log::channel('email_log')->info('EMAIL: '.$data['from_email'].' - VERIFICATION SENT');
6000            } else {
6001                $error = true;
6002                Log::channel('email_log')->error('REQUEST BODY: - '.$response->body());
6003            }
6004
6005            $response = json_decode((string) $response->body());
6006
6007            if ($error) {
6008                if ($response->errors[0]->message == 'already exists' && $response->errors[0]->field == 'from_email') {
6009                    TblCompanyEmails::create($sData);
6010
6011                    return response(['message' => 'OK', 'data' => $data, 'is_verified' => 'yes']);
6012                }
6013
6014                $errMessage = $response->errors[0]->field.': '.$response->errors[0]->message;
6015
6016                return response(['message' => 'KO', 'error' => $errMessage]);
6017            } else {
6018                return response(['message' => 'OK', 'data' => $response, 'is_verified' => 'no']);
6019            }
6020
6021        } catch (\Exception $e) {
6022            report(AppException::fromException($e, 'CREATE_SENDER_IDENTITY_EXCEPTION'));
6023            Log::channel('email_log')->error('EMAIL:' . $data['from_email'] . ' - ' . $e->getMessage());
6024            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6025        }
6026    }
6027
6028    function get_sender_identity($companyId): ResponseFactory|HttpResponse{
6029
6030        try {
6031
6032            $companyId = addslashes((string) $companyId);
6033
6034            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
6035
6036            // @phpstan-ignore-next-line
6037            $response = $sendgrid->client->verified_senders()->get();
6038
6039            if ($response->statusCode() == 200) {
6040                $x = json_decode((string) $response->body())->results;
6041
6042                foreach ($x as $item) {
6043                    TblCompanyEmails::where('from_email', $item->from_email)->update([
6044                        'verified' => $item->verified,
6045                        'reply_to' => $item->reply_to,
6046                        'response_id' => $item->id
6047                    ]);
6048                }
6049            }
6050
6051            $companyEmails = TblCompanyEmails::where('company_id', $companyId)->get();
6052
6053            return response(['message' => 'OK', 'data' => $companyEmails]);
6054
6055        } catch (\Exception $e) {
6056            report(AppException::fromException($e, 'GET_SENDER_IDENTITY_EXCEPTION'));
6057            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6058        }
6059    }
6060
6061    function get_all_sender_identity($companyId): ResponseFactory|HttpResponse{
6062
6063        try {
6064
6065            $companyId = addslashes((string) $companyId);
6066
6067            $query = "SELECT
6068                        id,
6069                        response_id,
6070                        nickname,
6071                        CONCAT(nickname, ' - ', from_email) from_email,
6072                        from_name,
6073                        reply_to,
6074                        reply_to_name,
6075                        address,
6076                        address2,
6077                        state,
6078                        city,
6079                        country,
6080                        zip,
6081                        verified,
6082                        locked,
6083                        is_active,
6084                        created_by,
6085                        created_at,
6086                        updated_by,
6087                        updated_at
6088                    FROM
6089                        tbl_company_emails
6090                    WHERE
6091                        company_id != {$companyId}
6092                        AND from_email NOT IN (
6093                        SELECT
6094                            from_email
6095                        FROM
6096                            tbl_company_emails
6097                        WHERE
6098                            company_id = {$companyId}
6099                        )
6100                    ";
6101
6102            $companyEmails = DB::select($query);
6103
6104            return response(['message' => 'OK', 'data' => $companyEmails]);
6105
6106        } catch (\Exception $e) {
6107            report(AppException::fromException($e, 'GET_ALL_SENDER_IDENTITY_EXCEPTION'));
6108            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6109        }
6110    }
6111
6112    function delete_sender_identity(Request $request): ResponseFactory|HttpResponse{
6113
6114        try {
6115
6116            $data = $request->all();
6117            $responseId = addslashes((string) $data['response_id']);
6118            $id = addslashes((string) $data['id']);
6119
6120            $sender = TblCompanyEmails::where('response_id', $responseId)->count();
6121
6122            if ($sender > 1) {
6123                TblCompanyEmails::where('id', $id)->delete();
6124            }else{
6125                $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
6126
6127                $response = $sendgrid->client->verified_senders()->_($responseId)->delete();
6128
6129                if ($response->statusCode() == 204) {
6130                    TblCompanyEmails::where('response_id', $responseId)->delete();
6131                }
6132            }
6133
6134            return response(['message' => 'OK']);
6135
6136        } catch (\Exception $e) {
6137            report(AppException::fromException($e, 'DELETE_SENDER_IDENTITY_EXCEPTION'));
6138            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6139        }
6140    }
6141
6142    function create_template(Request $request): ResponseFactory|HttpResponse{
6143
6144        try {
6145
6146            $data = $request->all();
6147
6148            $files = $request->file('files');
6149            unset($data['files']);
6150
6151            if ($files) {
6152                $totalFileCount = count($files);
6153                if ($totalFileCount > 2) {
6154                    return response(['message' => 'KO', 'error' => __('language.file_count_exceeded')]);
6155                }
6156            }
6157
6158            $result = TblEmailConfiguration::create($data);
6159            $id = $result->id;
6160
6161            $directory = 'public/uploads';
6162            $origFilename = 'fireservicetitan.png';
6163            $file = 'public/uploads/fireservicetitan.png';
6164
6165            $sourcePath = public_path($origFilename);
6166            $destinationPath = 'public/uploads/'.$origFilename;
6167
6168            $filename = $id.'-EI'.time().'-'.$origFilename;
6169
6170            Storage::putFileAs($directory, new \Illuminate\Http\File($sourcePath), $filename);
6171            Storage::disk('google')->put($filename, file_get_contents(storage_path().'/app/public/uploads/'.$filename));
6172
6173            TblEmailFiles::create(
6174                [
6175                    'email_template_id' => $id,
6176                    'original_name' => $origFilename,
6177                    'filename' => $filename,
6178                    'uploaded_by' => $data['created_by']
6179                ]
6180            );
6181
6182            if ($files) {
6183
6184                $uploadedFiles = [];
6185                $i = 0;
6186
6187                $combinedFilesSize = 0;
6188
6189                foreach ($files as $file) {
6190                    $i++;
6191
6192                    $origFilename = str_replace(' ', '', $file->getClientOriginalName());
6193
6194                    $filename = $id.'-EI'.time().'-'.$origFilename;
6195
6196                    $combinedFilesSize = $combinedFilesSize + $file->getSize();
6197
6198                    if ($combinedFilesSize > 25000000) {
6199                        return response(['message' => 'KO', 'error' => __('language.file_size_exceeded')]);
6200                    }
6201
6202                    Storage::putFileAs($directory, $file, $filename);
6203                    Storage::disk('google')->put($filename, file_get_contents(storage_path().'/app/public/uploads/'.$filename));
6204
6205                    if (in_array($origFilename, $uploadedFiles)) {
6206                        $origFilename = $origFilename.$i;
6207                    }
6208
6209                    TblEmailFiles::create(
6210                        [
6211                            'email_template_id' => $id,
6212                            'original_name' => $origFilename,
6213                            'filename' => $filename,
6214                            'uploaded_by' => $data['created_by']
6215                        ]
6216                    );
6217
6218                    $uploadedFiles[] = $file->getClientOriginalName();
6219                }
6220            }
6221
6222            return response(['message' => 'OK']);
6223
6224        } catch (\Exception $e) {
6225            report(AppException::fromException($e, 'CREATE_TEMPLATE_EXCEPTION'));
6226            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6227        }
6228    }
6229
6230    function get_email_files($emailTemplateId): ResponseFactory|HttpResponse{
6231
6232        try {
6233
6234            $emailTemplateId = addslashes((string) $emailTemplateId);
6235
6236            $result = TblEmailFiles::where('email_template_id', $emailTemplateId)->orderBy('order', 'asc')->get();
6237
6238            foreach ($result as $key => $value) {
6239                $path = storage_path('app/public/uploads/'.$result[$key]->filename);
6240
6241                if (File::exists($path)) {
6242                    $fileSizeBytes = File::size($path);
6243                    $result[$key]->setAttribute('filesize', $this->human_filesize($fileSizeBytes));
6244                    $result[$key]->original_name = $result[$key]->original_name." ({$result[$key]->getAttribute('filesize')})";
6245                    $result[$key]->setAttribute('img', 'data:image/png;base64,'.base64_encode(file_get_contents($path)));
6246                }
6247            }
6248
6249            return response(['message' => 'OK', 'data' => $result]);
6250
6251        } catch (\Exception $e) {
6252            report(AppException::fromException($e, 'GET_EMAIL_FILES_EXCEPTION'));
6253            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6254        }
6255    }
6256
6257    function download_email_template_file($fileId): ResponseFactory|HttpResponse{
6258
6259        try {
6260
6261            $fileId = addslashes((string) $fileId);
6262
6263            $result = TblEmailFiles::where('file_id', $fileId)->first();
6264
6265            if ($result) {
6266                $path = storage_path('app/public/uploads/'.$result->filename);
6267
6268                if (! Storage::disk('public')->exists('uploads/'.$result->filename)) {
6269                    return response(['message' => 'KO']);
6270                }
6271
6272                return response()->download($path);
6273            }
6274
6275            return response(['message' => 'KO']);
6276
6277        } catch (\Exception $e) {
6278            report(AppException::fromException($e, 'DOWNLOAD_EMAIL_TEMPLATE_FILE_EXCEPTION'));
6279            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6280        }
6281
6282    }
6283
6284    function delete_email_template_file($fileId): ResponseFactory|HttpResponse{
6285
6286        try {
6287
6288            $fileId = addslashes((string) $fileId);
6289            $file = TblEmailFiles::where('file_id', $fileId)->first();
6290            $result = TblEmailFiles::where('file_id', $fileId)->first();
6291
6292            if ($result) {
6293                TblEmailFiles::where('file_id', $fileId)->delete();
6294                $path = storage_path('app/public/uploads/'.$result->filename);
6295                Storage::disk('public')->delete('uploads/'.$result->filename);
6296            }
6297
6298            return response(['message' => 'OK']);
6299
6300        } catch (\Exception $e) {
6301            report(AppException::fromException($e, 'DELETE_EMAIL_TEMPLATE_FILE_EXCEPTION'));
6302            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6303        }
6304    }
6305
6306    public function update_email_template_order(Request $request, $id)
6307    {
6308
6309        try {
6310
6311            $id = addslashes((string) $id);
6312            $data = $request->all();
6313
6314            foreach ($data as $item) {
6315                TblEmailFiles::where('file_id', $item['file_id'])->update(['order' => $item['order']]);
6316            }
6317
6318        } catch (\Exception $e) {
6319            report(AppException::fromException($e, 'UPDATE_EMAIL_TEMPLATE_ORDER_EXCEPTION'));
6320            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6321        }
6322
6323    }
6324
6325
6326    function update_email_template(Request $request, $id): ResponseFactory|HttpResponse{
6327
6328        try {
6329
6330            $data = $request->all();
6331
6332            $id = addslashes((string) $id);
6333
6334            $files = $request->file('files');
6335            unset($data['files']);
6336
6337            $fileCount = TblEmailFiles::where('email_template_id', $id)->count();
6338
6339            if ($files) {
6340                $totalFileCount = $fileCount + count($files);
6341                if ($totalFileCount > 3) {
6342                    return response(['message' => 'KO', 'error' => __('language.file_count_exceeded')]);
6343                }
6344            }
6345
6346            if(isset($data['cron_default'])){
6347                TblEmailConfiguration::where('company_id', $data['company_id'])->where('cron_default', 1)->update(['cron_default' => 0]);
6348            }
6349
6350            $data['updated_at'] = date('Y-m-d H:i:s');
6351            TblEmailConfiguration::where('id', $id)->update($data);
6352
6353            if ($files) {
6354
6355                $directory = 'public/uploads';
6356                $uploadedFiles = [];
6357                $i = 0;
6358
6359                $combinedFilesSize = 0;
6360                foreach ($files as $file) {
6361                    $i++;
6362                    $origFilename = str_replace(' ', '', $file->getClientOriginalName());
6363                    $filename = $id.'-EI'.time().'-'.$origFilename;
6364
6365                    $combinedFilesSize = $combinedFilesSize + $file->getSize();
6366
6367                    if ($combinedFilesSize > 25000000) {
6368                        return response(['message' => 'KO', 'error' => __('language.file_size_exceeded')]);
6369                    }
6370
6371                    Storage::putFileAs($directory, $file, $filename);
6372                    Storage::disk('google')->put($filename, file_get_contents(storage_path().'/app/public/uploads/'.$filename));
6373
6374                    if (in_array($origFilename, $uploadedFiles)) {
6375                        $origFilename = $origFilename.$i;
6376                    }
6377
6378                    TblEmailFiles::create(
6379                        [
6380                            'email_template_id' => $id,
6381                            'original_name' => $origFilename,
6382                            'filename' => $filename,
6383                            'uploaded_by' => $data['updated_by']
6384                        ]
6385                    );
6386
6387                    $uploadedFiles[] = $file->getClientOriginalName();
6388                }
6389            }
6390
6391            return response(['message' => 'OK']);
6392
6393        } catch (\Exception $e) {
6394            report(AppException::fromException($e, 'UPDATE_EMAIL_TEMPLATE_EXCEPTION'));
6395            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6396        }
6397
6398    }
6399
6400    function delete_template($id): ResponseFactory|HttpResponse{
6401
6402        try {
6403
6404            $id = addslashes((string) $id);
6405
6406            TblEmailConfiguration::where('id', $id)->delete();
6407
6408            return response(['message' => 'OK']);
6409
6410        } catch (\Exception $e) {
6411            report(AppException::fromException($e, 'DELETE_TEMPLATE_EXCEPTION'));
6412            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6413        }
6414
6415    }
6416
6417    function get_email_template($companyId): ResponseFactory|HttpResponse{
6418
6419        try {
6420
6421            $companyId = addslashes((string) $companyId);
6422
6423            $where = '';
6424            if ($companyId != 0) {
6425                $where = " a.company_id = {$companyId} ";
6426            } else {
6427                $where = " a.company_id IN ({$this->companyId}";
6428            }
6429
6430            $query = "SELECT
6431                        a.id,
6432                        a.from_id,
6433                        CASE
6434                            WHEN a.type = 'followUps' THEN 'Follow-ups'
6435                            WHEN a.type = 'sendToClient' THEN 'Send to client'
6436                        END types,
6437                        (SELECT from_email FROM tbl_company_emails WHERE id = a.from_id) from_email,
6438                        a.name,
6439                        b.name company_name,
6440                        b.company_id,
6441                        a.subject,
6442                        a.html,
6443                        a.is_active,
6444                        a.type,
6445                        a.created_by,
6446                        a.created_at,
6447                        a.updated_by,
6448                        a.updated_at,
6449                        a.cron_default
6450                    FROM tbl_email_configuration a
6451                    LEFT JOIN tbl_companies b
6452                        ON b.company_id = a.company_id
6453                    WHERE {$where}
6454                    ORDER BY a.id DESC";
6455
6456            $result = DB::select($query);
6457
6458            return response(['message' => 'OK', 'data' => $result]);
6459
6460        } catch (\Exception $e) {
6461            report(AppException::fromException($e, 'GET_EMAIL_TEMPLATE_EXCEPTION'));
6462            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6463        }
6464
6465    }
6466
6467    function update_sender_identity(Request $request, $responseId): ResponseFactory|HttpResponse{
6468
6469        try {
6470
6471            $data = $request->all();
6472            $companyId = $data['company_id'];
6473            $updatedBy = $data['updated_by'];
6474            $active = $data['is_active'];
6475            $id = addslashes((string) $data['id']);
6476            unset($data['id']);
6477            unset($data['company_id']);
6478            unset($data['updated_by']);
6479            unset($data['is_active']);
6480
6481            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
6482            $data['reply_to'] = $data['from_email'];
6483            $data['reply_to_name'] = $data['from_name'];
6484            $requestBody = $data;
6485            $error = false;
6486
6487            $response = $sendgrid->client->verified_senders()->_($responseId)->patch($requestBody);
6488
6489            if ($response->statusCode() == 200) {
6490                $x = json_decode((string) $response->body());
6491
6492                $data['updated_by'] = $updatedBy;
6493                $data['updated_at'] = date('Y-m-d H:i:s');
6494                $data['is_active'] = $active;
6495
6496                TblCompanyEmails::where('company_id', $companyId)->update(['is_active' => 0]);
6497                TblCompanyEmails::where('id', $id)->update($data);
6498
6499                Log::channel('email_log')->info('EMAIL: '.$data['from_email'].' - UPDATED');
6500            } else {
6501                $error = true;
6502                Log::channel('email_log')->error('REQUEST BODY: - '.$response->body());
6503            }
6504
6505            $response = json_decode((string) $response->body());
6506
6507            if ($error) {
6508                $errMessage = @$response->errors[0]->field.': '.@$response->errors[0]->message;
6509
6510                return response(['message' => 'KO', 'error' => $errMessage]);
6511            } else {
6512                return response(['message' => 'OK', 'data' => $response]);
6513            }
6514
6515        } catch (\Exception $e) {
6516            report(AppException::fromException($e, 'UPDATE_SENDER_IDENTITY_EXCEPTION'));
6517            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6518        }
6519
6520    }
6521
6522    function resend_verification($id): ResponseFactory|HttpResponse{
6523
6524        try {
6525
6526            $id = addslashes((string) $id);
6527
6528            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
6529
6530            $response = $sendgrid->client->verified_senders()->resend()->_($id)->post();
6531
6532            if ($response->statusCode() == 204) {
6533                return response(['message' => 'OK']);
6534            }else{
6535                $response = json_decode((string) $response->body());
6536                $errMessage = $response->errors[0]->error_id . ': ' . $response->errors[0]->message;
6537                return response(['message' => 'KO', 'error' => $errMessage]);
6538            }
6539
6540        } catch (\Exception $e) {
6541            report(AppException::fromException($e, 'RESEND_VERIFICATION_EXCEPTION'));
6542            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6543        }
6544    }
6545
6546    function list_quotation_analytics_by_performance(Request $request): ResponseFactory|HttpResponse{
6547
6548        try {
6549
6550            $data = $request->all();
6551            $companyId = addslashes((string) $data['company_id']);
6552            $where  = "";
6553
6554            if ($companyId != 0) {
6555                $where .= " AND company_id = {$companyId} AND for_add = 0 ";
6556            } else {
6557                $where .= " AND company_id IN ({$this->companyId}) AND for_add = 0 ";
6558            }
6559
6560            if (isset($data['budget_status']) && $data['budget_status'] != null) {
6561                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
6562            }
6563
6564            $latestYear = TblQuotations::max(DB::raw('YEAR(issue_date)'));
6565            $md = date('m-d');
6566
6567            $query = "SELECT
6568                        q.years,
6569                        q.totalIssue,
6570                        (q.total_acceptance_percentage - q.totalAcceptancePercentage) / q.totalAcceptancePercentage * 100 totalAcceptancePercentage,
6571                        q.totalAccept
6572                    FROM
6573                        (
6574                        SELECT
6575                            YEAR(a.issue_date) years,
6576                            (b.total_issued - COUNT(a.issue_date)) / COUNT(a.issue_date) * 100 totalIssue,
6577                            COUNT(
6578                                CASE WHEN a.acceptance_date IS NOT NULL
6579                                AND DATE_FORMAT(a.acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(a.acceptance_date), '-01-01')
6580                                AND CONCAT(YEAR(a.acceptance_date), '-{$md}')
6581                                AND YEAR(a.acceptance_date) = YEAR(a.issue_date)
6582                                AND a.budget_status_id = 3 THEN 1 END
6583                            ) / COUNT(a.issue_date) * 100 totalAcceptancePercentage,
6584                            b.total_acceptance_percentage,
6585                            (b.total_acceptance -
6586                                COUNT(
6587                                    CASE WHEN a.acceptance_date IS NOT NULL
6588                                    AND DATE_FORMAT(a.acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(a.acceptance_date), '-01-01')
6589                                    AND CONCAT(YEAR(a.acceptance_date), '-{$md}')
6590                                    AND YEAR(a.acceptance_date) = YEAR(a.issue_date)
6591                                    AND a.budget_status_id = 3 THEN 1 END)
6592                                    ) / COUNT(
6593                                            CASE WHEN a.acceptance_date IS NOT NULL
6594                                            AND DATE_FORMAT(a.acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(a.acceptance_date), '-01-01')
6595                                            AND CONCAT(YEAR(a.acceptance_date), '-{$md}')
6596                                            AND YEAR(a.acceptance_date) = YEAR(a.issue_date)
6597                                            AND a.budget_status_id = 3 THEN 1 END
6598                            ) * 100 totalAccept
6599                        FROM
6600                            tbl_quotations a
6601                            JOIN (
6602                            SELECT
6603                                YEAR(issue_date) current_year,
6604                                COUNT(issue_date) total_issued,
6605                                COUNT(
6606                                    CASE WHEN acceptance_date IS NOT NULL
6607                                    AND DATE_FORMAT(acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(acceptance_date), '-01-01')
6608                                    AND CONCAT(YEAR(acceptance_date), '-{$md}')
6609                                    AND YEAR(acceptance_date) = YEAR(issue_date)
6610                                    AND budget_status_id = 3 THEN 1 END
6611                                ) total_acceptance,
6612                                COUNT(
6613                                    CASE WHEN acceptance_date IS NOT NULL
6614                                    AND DATE_FORMAT(acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(acceptance_date), '-01-01')
6615                                    AND CONCAT(YEAR(acceptance_date), '-{$md}')
6616                                    AND YEAR(acceptance_date) = YEAR(issue_date)
6617                                    AND budget_status_id = 3 THEN 1 END
6618                                ) / COUNT(issue_date) * 100 total_acceptance_percentage
6619                            FROM
6620                                tbl_quotations
6621                            WHERE
6622                                issue_date IS NOT NULL
6623                                AND YEAR(issue_date) = {$latestYear}
6624                                AND DATE_FORMAT(issue_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(issue_date), '-01-01')
6625                                AND CONCAT(YEAR(issue_date), '-{$md}')
6626                                {$where}
6627                            GROUP by
6628                                1
6629                            ) b
6630                        WHERE
6631                            a.issue_date IS NOT NULL
6632                            AND a.budget_type_id != 7
6633                            AND a.budget_type_id IS NOT NULL
6634                            AND {$latestYear} > YEAR(a.issue_date)
6635                            AND DATE_FORMAT(a.issue_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(a.issue_date), '-01-01')
6636                            AND CONCAT(YEAR(a.issue_date), '-{$md}')
6637                        GROUP BY
6638                            1
6639                        ORDER BY
6640                            YEAR(issue_date) DESC
6641                        ) q
6642                    ";
6643
6644            $resultYtd = DB::select($query);
6645
6646            $query = "SELECT
6647                        YEAR(issue_date) years,
6648                        COUNT(issue_date) totalIssue,
6649                        COUNT(
6650                            CASE WHEN acceptance_date IS NOT NULL
6651                            AND DATE_FORMAT(acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(acceptance_date), '-01-01')
6652                            AND CONCAT(YEAR(acceptance_date), '-{$md}')
6653                            AND YEAR(acceptance_date) = YEAR(issue_date)
6654                            AND budget_status_id = 3 THEN 1 END
6655                        ) totalAccept,
6656                        COUNT(
6657                            CASE WHEN acceptance_date IS NOT NULL
6658                            AND DATE_FORMAT(acceptance_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(acceptance_date), '-01-01')
6659                            AND CONCAT(YEAR(acceptance_date), '-{$md}')
6660                            AND YEAR(acceptance_date) = YEAR(issue_date)
6661                            AND budget_status_id = 3 THEN 1 END
6662                        ) / COUNT(issue_date) * 100 totalAcceptancePercentage
6663                    FROM
6664                        tbl_quotations
6665                    WHERE
6666                        issue_date IS NOT NULL
6667                        AND budget_type_id != 7
6668                        AND budget_type_id IS NOT NULL
6669                        AND DATE_FORMAT(issue_date, '%Y-%m-%d') BETWEEN CONCAT(YEAR(issue_date), '-01-01')
6670                        AND CONCAT(YEAR(issue_date), '-{$md}')
6671                        {$where}
6672                    GROUP BY
6673                        1
6674                    ORDER BY
6675                        YEAR(issue_date) DESC
6676                    ";
6677
6678            $resultYears = DB::select($query);
6679
6680            return response(['message' => 'OK', 'ytdData' => $resultYtd, 'yearsData' => $resultYears]);
6681
6682        } catch (\Exception $e) {
6683            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_BY_PERFORMANCE_EXCEPTION'));
6684            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6685        }
6686    }
6687
6688    function list_orders_update_logs($companyId): ResponseFactory|HttpResponse{
6689
6690        try {
6691
6692            $where = '';
6693
6694            if ($companyId != 0) {
6695                $where .= " a.company_id = {$companyId}";
6696            } else {
6697                $where .= " a.company_id IN ({$this->companyId})";
6698            }
6699
6700            $query = "SELECT
6701                        a.id,
6702                        b.name company,
6703                        a.to_process,
6704                        a.rejected_affected_rows,
6705                        a.for_add_deleted_affected_rows,
6706                        a.month_change_affected_rows,
6707                        a.follow_ups_affected_rows,
6708                        a.sync_succesfull,
6709                        a.sync_error,
6710                        a.sync_error_message,
6711                        a.sync_success_ids,
6712                        a.cleared_email_errors,
6713                        a.remaining_email_errors,
6714                        CASE
6715                            WHEN a.rejected_affected_rows IS NOT NULL THEN a.rejected_affected_rows
6716                            WHEN a.for_add_deleted_affected_rows IS NOT NULL THEN a.for_add_deleted_affected_rows
6717                            WHEN a.month_change_affected_rows IS NOT NULL THEN a.month_change_affected_rows
6718                            WHEN a.follow_ups_affected_rows IS NOT NULL THEN a.follow_ups_affected_rows
6719                        END totals,
6720                        a.status,
6721                        a.processed_by,
6722                        a.started_at,
6723                        a.ended_at,
6724                        a.rejected_automatically_ids
6725                    FROM `tbl_orders_update_logs` a
6726                    LEFT JOIN tbl_companies b
6727                        ON a.company_id = b.company_id
6728                    WHERE {$where}
6729                    ORDER BY started_at DESC";
6730
6731            $result = DB::select($query);
6732
6733            return response(['message' => 'OK', 'data' => $result]);
6734
6735        } catch (\Exception $e) {
6736            report(AppException::fromException($e, 'LIST_ORDERS_UPDATE_LOGS_EXCEPTION'));
6737            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6738        }
6739
6740    }
6741
6742    function list_g3w_orders_update_logs($companyId): ResponseFactory|HttpResponse{
6743
6744        try {
6745            $result = TblG3WOrdersUpdateLogs::where('company_id', $companyId)
6746                ->where('processed_by', 'System')
6747                ->get();
6748
6749            return response(['message' => 'OK', 'data' => $result]);
6750
6751        } catch (\Exception $e) {
6752            report(AppException::fromException($e, 'LIST_G3W_ORDERS_UPDATE_LOGS_EXCEPTION'));
6753            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6754        }
6755
6756    }
6757
6758    function list_g3w_orders_failed($companyId): ResponseFactory|HttpResponse {
6759        try {
6760            $logs = TblG3WOrdersUpdateLogs::where('company_id', $companyId)
6761                ->where('sync_error', '>', 0)
6762                ->get();
6763
6764            $individualErrors = [];
6765
6766            foreach ($logs as $log) {
6767                $errorIds = json_decode((string) $log->sync_error_ids);
6768                $errorMessages = preg_split('/(?=Error (sincronizando|actualizando) el presupuesto)/', (string) $log->sync_error_message, -1, PREG_SPLIT_NO_EMPTY);
6769
6770                if (is_array($errorIds)) {
6771                    foreach ($errorIds as $index => $id) {
6772                        $rawMsg = $errorMessages[$index] ?? 'Error desconocido';
6773                        $cleanMsg = trim((string) preg_replace('/.*?el presupuesto \d+:/i', '', $rawMsg));
6774                        $cleanMsg = rtrim($cleanMsg, ',');
6775
6776                        $individualErrors[] = [
6777                            'quote_id' => $id,
6778                            'status' => 'Failed',
6779                            'started_at' => $log->started_at,
6780                            'ended_at' => $log->ended_at,
6781                            'reason' => $cleanMsg,
6782                            'log_id' => $log->id,
6783                        ];
6784                    }
6785                }
6786            }
6787
6788            return response(['message' => 'OK', 'data' => $individualErrors]);
6789
6790        } catch (\Exception $e) {
6791            report($e);
6792
6793            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6794        }
6795    }
6796
6797    function update_budget_status_rejected_manual(Request $request): ResponseFactory|HttpResponse{
6798
6799        try {
6800
6801            $data = $request->all();
6802
6803            $update = $this->update_budget_status_rejected($data['company_id'], $data['processed_by']);
6804
6805            if ($update) {
6806                return response(['message' => 'OK']);
6807            } else {
6808                return response(['message' => 'KO']);
6809            }
6810
6811        } catch (\Exception $e) {
6812            report(AppException::fromException($e, 'UPDATE_BUDGET_STATUS_REJECTED_MANUAL_EXCEPTION'));
6813            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6814        }
6815    }
6816
6817    function update_budget_status_rejected($companyId = null, $processedBy = "System"): ResponseFactory|HttpResponse{
6818
6819        $startedAt = date('Y-m-d H:i:s');
6820        try {
6821
6822            $startedAt = date('Y-m-d H:i:s');
6823            $companyId = addslashes((string) $companyId);
6824            $where = "";
6825
6826            $budgetTypes = TblBudgetTypes::get();
6827
6828            if (count($budgetTypes) > 0) {
6829
6830                $companies = [];
6831
6832                if ($companyId != 0) {
6833                    $companies = TblCompanies::where('company_id', $companyId)->get();
6834                    $where = "AND company_id = {$companyId}";
6835                } else {
6836                    $companies = TblCompanies::get();
6837                }
6838
6839                for ($i = 0; $i < count($budgetTypes); $i++) {
6840
6841                    $days = $budgetTypes[$i]->duration;
6842                    $id = $budgetTypes[$i]->budget_type_id;
6843
6844                    if (! $days) {
6845                        continue;
6846                    }
6847
6848                    for ($c = 0; $c < count($companies); $c++) {
6849                        $companyId = $companies[$c]->company_id;
6850
6851                        $query = "SELECT
6852                                    GROUP_CONCAT(id) ids
6853                                FROM
6854                                    tbl_quotations
6855                                WHERE
6856                                    budget_type_id = {$id}
6857                                AND budget_status_id IN (1, 2, 11)
6858                                AND DATEDIFF(NOW(), issue_date) >= {$days}
6859                                AND for_add = 0
6860                                {$where}";
6861
6862                        $result = DB::select($query);
6863
6864                        if (count($result) > 0) {
6865
6866                            $ids = $result[0]->ids;
6867
6868                            if ($ids != null || $ids != '') {
6869                                $query = "UPDATE
6870                                            tbl_quotations
6871                                        SET
6872                                            budget_status_id = 7
6873                                        WHERE
6874                                            id IN ({$ids})";
6875
6876                                DB::select($query);
6877
6878                                TblOrdersUpdateLogs::create(
6879                                    [
6880                                        'company_id' => $companyId,
6881                                        'to_process' => 'Orders',
6882                                        'status' => 'success',
6883                                        'rejected_automatically_ids' => $ids,
6884                                        'processed_by' => $processedBy,
6885                                        'started_at' => $startedAt,
6886                                        'ended_at' => date('Y-m-d H:i:s')
6887                                    ]
6888                                );
6889                            }
6890                        }
6891                    }
6892                }
6893            }
6894
6895            Cache::flush();
6896
6897            return response(['message' => 'OK']);
6898
6899        } catch (\Exception $e) {
6900            report(AppException::fromException($e, 'UPDATE_BUDGET_STATUS_REJECTED_MANUAL_EXCEPTION'));
6901
6902            TblOrdersUpdateLogs::create(
6903                [
6904                    'company_id' => $companyId,
6905                    'to_process' => 'Orders',
6906                    'status' => $e->getMessage(),
6907                    'processed_by' => $processedBy,
6908                    'started_at' => $startedAt,
6909                    'ended_at' => date('Y-m-d H:i:s')
6910                ]
6911            );
6912
6913            return response(['message' => 'KO', 'error' => $e->getMessage()]);
6914        }
6915
6916    }
6917
6918    function bulk_update_quotation(Request $request): ResponseFactory|HttpResponse{
6919
6920        // try {
6921
6922        $data = $request->all();
6923
6924        $r = new Request([
6925            'filterModel' => $data['filterModel'],
6926            'sortModel' => $data['sortModel'],
6927            'start' => 0,
6928            'end' => 999999999,
6929            'company_id' => $data['company_id'],
6930            'user_id' => $data['user_id'],
6931            'ids' => $data['ids'],
6932            'searchText' => $data['searchText'],
6933            'ids_not_in' => $data['ids_not_in'],
6934        ]);
6935
6936        $listQuotations = $this->list_quotations($r);
6937        $d = $listQuotations->original['data'];
6938
6939        if (count($d) > 0) {
6940            if (isset($data['last_follow_up_date']) && $data['last_follow_up_date'] == 1) {
6941                unset($data['last_follow_up_date']);
6942            }
6943            // unset($data['last_follow_up_date']);
6944            unset($data['filterModel']);
6945            unset($data['sortModel']);
6946            unset($data['start']);
6947            unset($data['end']);
6948            unset($data['company_id']);
6949            unset($data['user_id']);
6950            unset($data['ids']);
6951            unset($data['searchText']);
6952            unset($data['ids_not_in']);
6953
6954            $result = [];
6955            for ($i = 0; $i < count($d); $i++) {
6956                array_push($result, $d[$i]->id);
6957            }
6958
6959            TblQuotations::whereIn('id', $result)->update($data);
6960
6961            Cache::flush();
6962        }
6963
6964        return response(['message' => 'OK', $data]);
6965
6966        // } catch (\Exception $e) {
6967        //     return response(['message' => 'KO', 'error' => $e->getMessage()]);
6968        // }
6969
6970    }
6971
6972    function move_budget_and_job(Request $request): ResponseFactory|HttpResponse{
6973
6974        try {
6975
6976            $data = $request->all();
6977            $id = addslashes((string) $data['id']);
6978            $companyId = addslashes((string) $data['company_id']);
6979
6980            unset($data['id']);
6981            unset($data['company_id']);
6982
6983            $budget = TblQuotations::where('id', $id)->first();
6984
6985            $quotationId = $budget->id;
6986            $companyIdJob = $budget->company_id;
6987
6988            $query = "SELECT
6989                        COUNT(1) total
6990                    FROM tbl_company_users a
6991                    LEFT JOIN tbl_users b
6992                        ON a.user_id = b.id
6993                    WHERE a.company_id = {$companyId}
6994                    AND b.name = '{$budget->commercial}'
6995                    ORDER BY b.name ASC";
6996
6997            $result = DB::select($query);
6998
6999            $commercial = $budget->commercial;
7000
7001            if ($result[0]->total == 0) {
7002                $commercial = $data['created_by'];
7003            }
7004
7005            $r = new Request([
7006                'created_by' => $commercial,
7007            ]);
7008
7009            $result = $this->get_number($r, $companyId, 1);
7010            $newNumber = $result->original['number'];
7011
7012            TblQuotations::where('id', $id)->update(
7013                [
7014                    'quote_id' => $newNumber,
7015                    'company_id' => $companyId,
7016                    'from_company_id' => $budget->company_id,
7017                    'commercial' => $commercial,
7018                ]
7019            );
7020
7021            $job = TblOngoingJobs::where('quotation_id', $quotationId)->first();
7022
7023            $jobId = null;
7024
7025            if ($job) {
7026                $jobId = $job->id;
7027
7028                $query = "SELECT
7029                            COUNT(1) total
7030                        FROM tbl_company_users a
7031                        LEFT JOIN tbl_users b
7032                            ON a.user_id = b.id
7033                        WHERE a.company_id = {$companyId}
7034                        AND b.name = '{$job->responsible_for_work}'
7035                        ORDER BY b.name ASC";
7036
7037                $result = DB::select($query);
7038
7039                $responsibleForWork = $job->responsible_for_work;
7040
7041                if ($result[0]->total == 0) {
7042                    $responsibleForWork = $data['created_by'];
7043                }
7044
7045                TblOngoingJobs::where('quotation_id', $id)->update(
7046                    [
7047                        'quote_id' => $newNumber,
7048                        'company_id' => $companyId,
7049                        'responsible_for_work' => $responsibleForWork,
7050                    ]
7051                );
7052            }
7053
7054            Cache::flush();
7055
7056            return response([
7057                'message' => 'OK',
7058                'quotation_id' => $quotationId,
7059                'job_id' => $jobId,
7060            ]);
7061
7062        } catch (\Exception $e) {
7063            report(AppException::fromException($e, 'MOVE_BUDGET_AND_JOB_EXCEPTION'));
7064            return response(['message' => 'KO', 'error' => $e->getMessage()]);
7065        }
7066    }
7067
7068    function list_quotation_analytics_by_types_of_budgets_created_per_week(Request $request): ResponseFactory|HttpResponse{
7069
7070        try {
7071
7072            $data = $request->all();
7073            $companyId = addslashes((string) $data['company_id']);
7074            $field = $data['field'];
7075
7076            $where = "";
7077            $whereYear = "";
7078            $dateLflArray = [];
7079            $companyIds = $this->companyIds;
7080
7081            if($companyId != 0){
7082                $companyIds = [$companyId];
7083            }
7084
7085            $acc = '';
7086            if ($field == 'acceptance_date') {
7087                $acc = ' AND q.acceptance_date IS NOT NULL ';
7088                // $field = 'created_at';
7089
7090                if (@$data['data_to_display'] == 4) {
7091                    $field = 'created_at';
7092                    $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7093                }
7094            } else {
7095                $field = 'created_at';
7096                $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7097            }
7098
7099            if (isset($data['years']) && $data['years'] != null) {
7100
7101                if (count($data['years']) > 0) {
7102                    foreach ($data['years'] as $year) {
7103                        if (isset($data['week']) && $data['week'] != null) {
7104                            $w = sprintf('%02d', $data['week']);
7105                            $whereYear .= " AND YEARWEEK(q.{$field}, 1) = '{$year}{$w}'";
7106                        } else {
7107                            $whereYear .= " AND YEAR(q.{$field}) = {$year}";
7108                        }
7109                    }
7110                }
7111            }
7112
7113            foreach ($companyIds as $v) {
7114
7115                $lflWhere = " AND q.company_id = {$v} ";
7116
7117                $query = "SELECT
7118                            CONCAT(
7119                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
7120                                ' - ',
7121                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
7122                            ) AS date_like,
7123                            YEAR(q.{$field}) 'year',
7124                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
7125                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
7126                            {$v} 'company_id'
7127                        FROM
7128                            tbl_quotations q
7129                        WHERE
7130                            q.{$field} IS NOT NULL
7131                            AND q.for_add = 0
7132                            {$lflWhere}
7133                            {$whereYear}
7134                        GROUP BY YEAR(q.{$field})
7135                        ORDER BY YEAR(q.{$field}) DESC";
7136
7137                $dateLike = DB::select($query);
7138
7139                $dateLflArray[$v] = $dateLike;
7140            }
7141
7142            $isFy = true;
7143
7144            if (isset($data['ytd']) && $data['ytd'] != null && $data['ytd'] == true) {
7145                $isFy = false;
7146                $ytdArray = [];
7147                $ytdAcceptanceArray = [];
7148                $lflCompanyIds = [];
7149                foreach ($dateLflArray as $k => $v) {
7150                    foreach ($dateLflArray[$k] as $item) {
7151                        $year = $item->year;
7152                        $now = date('m-d');
7153                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
7154                    }
7155
7156                    $ytdArray = implode(' OR ', $ytdArray);
7157                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
7158                    $ytdArray = [];
7159                }
7160
7161                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
7162                $where .= " AND ({$lflCompanyIds}";
7163            }
7164
7165            if (isset($data['lfl']) && $data['lfl'] != null && $data['lfl'] == true) {
7166                $isFy = false;
7167                $lflArray = [];
7168                $ytdAcceptanceArray = [];
7169                $lflCompanyIds = [];
7170                foreach ($dateLflArray as $k => $v) {
7171                    foreach ($dateLflArray[$k] as $item) {
7172                        $year = $item->year;
7173                        $min_date_like = $item->min_date_like;
7174                        $max_date_like = $item->max_date_like;
7175                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
7176                    }
7177
7178                    $lflArray = implode(' OR ', $lflArray);
7179                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
7180                    $lflArray = [];
7181                }
7182
7183                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
7184                $where .= " AND ({$lflCompanyIds}";
7185            }
7186
7187            if ($isFy) {
7188                if ($companyId != 0) {
7189                    $where .= " AND q.company_id = {$companyId} ";
7190                } else {
7191                    $where .= " AND q.company_id IN ({$this->companyId})";
7192                }
7193            }
7194
7195            if (isset($data['source']) && $data['source'] != null) {
7196                $where .= " AND s.name = '{$data['source']}'";
7197            }
7198
7199            if (isset($data['month']) && $data['month'] != null) {
7200                $where .= " AND MONTH(q.{$field}) = '{$data['month']}'";
7201            }
7202
7203            if (isset($data['commercial']) && $data['commercial'] != null) {
7204                $where .= " AND q.commercial = '{$data['commercial']}'";
7205            }
7206
7207            if (isset($data['created_by']) && $data['created_by'] != null) {
7208                $where .= " AND q.created_by = '{$data['created_by']}'";
7209            }
7210
7211            if (isset($data['budget_type']) && $data['budget_type'] != null) {
7212                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
7213            }
7214
7215            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
7216                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
7217            }
7218
7219            if (isset($data['budget_status']) && $data['budget_status'] != null) {
7220                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
7221            }
7222
7223            if (isset($data['client_type']) && $data['client_type'] != null) {
7224                $where .= " AND ct.customer_type_id = {$data['client_type']}";
7225            }
7226
7227            if (isset($data['segment_id']) && $data['segment_id'] != null) {
7228                $where .= " AND q.segment_id = {$data['segment_id']}";
7229            }
7230
7231            $col = '1';
7232
7233            if (isset($data['data_to_display']) && $data['data_to_display'] != null) {
7234                if ($data['data_to_display'] == 1) {
7235                    $col = '1';
7236                }
7237
7238                if ($data['data_to_display'] == 2) {
7239                    $col = 'q.amount';
7240                }
7241            }
7242
7243            $budgetTypes = TblBudgetTypes::orderByRaw('ISNULL(priority), priority ASC')->get();
7244            $cols = '';
7245            foreach ($budgetTypes as $item) {
7246                if ($item->name == '' || $item->name == null) {
7247                    $cols .= ",COALESCE(SUM(CASE WHEN bt.name IS NULL {$acc} THEN {$col} ELSE 0 END), 0) AS 'Otros'";
7248                } else {
7249                    $cols .= ",COALESCE(SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN {$col} ELSE 0 END), 0) AS '{$item->name}'";
7250                }
7251            }
7252
7253            $budgetTypeGroups = TblBudgetTypeGroups::orderByRaw('ISNULL(priority), priority ASC')->get();
7254
7255            $colsGroups = ",COALESCE(SUM(CASE WHEN bt.name IS NULL {$acc} THEN {$col} END), 0) AS Otros";
7256
7257            foreach ($budgetTypeGroups as $item) {
7258                $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
7259                $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
7260                $colsGroups .= ",COALESCE(SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN {$col} END), 0) AS '{$budgetTypeGroupName}'";
7261            }
7262
7263            $colsGroups .= ",COALESCE(SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN {$col} END), 0) AS total";
7264
7265            $col = $colsGroups.$cols;
7266
7267            if (@$data['data_to_display'] == 3) {
7268
7269                $cols = '';
7270                foreach ($budgetTypes as $item) {
7271                    if ($item->name == '' || $item->name == null) {
7272                        $cols .= ",COALESCE(
7273                                        SUM(CASE WHEN bt.name IS NULL {$acc} THEN q.amount ELSE 0 END) /
7274                                        SUM(CASE WHEN bt.name IS NULL {$acc} THEN 1 ELSE 0 END) * 100 , 0
7275                                    ) AS 'Otros'";
7276                    } else {
7277                        $cols .= ",COALESCE(
7278                                        SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN q.amount ELSE 0 END) /
7279                                        SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN 1 ELSE 0 END), 0
7280                                    ) AS '{$item->name}'";
7281                    }
7282                }
7283
7284                $colsGroups = ",COALESCE(
7285                                (SUM(CASE WHEN bt.name IS NULL {$acc} THEN q.amount END)) /
7286                                (SUM(CASE WHEN bt.name IS NULL {$acc} THEN 1 END))
7287                            , 0) Otros";
7288
7289                foreach ($budgetTypeGroups as $item) {
7290                    $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
7291                    $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
7292                    $colsGroups .= ",COALESCE(
7293                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.amount END)) /
7294                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN 1 END))
7295                                    , 0) '{$budgetTypeGroupName}'";
7296                }
7297
7298                $colsGroups .= ",COALESCE(
7299                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN q.amount END)) /
7300                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN 1 END))
7301                                , 0) total";
7302
7303                $col = $colsGroups.$cols;
7304            }
7305
7306            if (@$data['data_to_display'] == 4) {
7307
7308                $cols = '';
7309
7310                foreach ($budgetTypes as $item) {
7311
7312                    if ($item->name == '' || $item->name == null) {
7313                        $cols .= ",COALESCE(
7314                                        SUM(CASE WHEN bt.name IS NULL AND q.acceptance_date IS NOT NULL THEN 1 ELSE 0 END) /
7315                                        SUM(CASE WHEN bt.name IS NULL AND q.created_at IS NOT NULL THEN 1 ELSE 0 END) * 100 , 0
7316                                ) AS 'Otros'";
7317                    } else {
7318                        $cols .= ", COALESCE(
7319                                        SUM(CASE WHEN bt.name = '{$item->name}' AND q.acceptance_date IS NOT NULL THEN 1 END) /
7320                                        SUM(CASE WHEN bt.name = '{$item->name}' AND q.created_at IS NOT NULL THEN 1 END) * 100, 0
7321                                ) AS '{$item->name}'";
7322                    }
7323                }
7324
7325                $colsGroups = ',COALESCE(
7326                                    (SUM(CASE WHEN bt.name IS NULL AND q.acceptance_date IS NOT NULL THEN 1 END)) /
7327                                    (SUM(CASE WHEN bt.name IS NULL AND q.created_at IS NOT NULL THEN 1 END)) * 100
7328                                    , 0) Otros';
7329
7330                foreach ($budgetTypeGroups as $item) {
7331                    $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
7332                    $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
7333                    $colsGroups .= ",COALESCE(
7334                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) AND q.acceptance_date IS NOT NULL THEN 1 END)) /
7335                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) AND q.created_at IS NOT NULL THEN 1 END)) * 100
7336                                    , 0) '{$budgetTypeGroupName}'";
7337                }
7338
7339                $colsGroups .= ',COALESCE(
7340                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) AND q.acceptance_date IS NOT NULL THEN 1 END)) /
7341                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) AND q.created_at IS NOT NULL THEN 1 END)) * 100
7342                                    , 0) total';
7343
7344                $col = $colsGroups.$cols;
7345            }
7346
7347            $query = "SELECT
7348                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)) AS 'year',
7349                            LPAD(MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)), 2, 0) AS 'month',
7350                            LPAD(WEEK(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)), 2, 0) AS 'week',
7351                            DATE_FORMAT(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY), '%W, %M %e') 'namedate',
7352                            GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN q.id END) groupConcatIds
7353                            {$col}
7354                        FROM
7355                            tbl_quotations q
7356                            LEFT JOIN tbl_sources s ON s.source_id = q.source_id
7357                            LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
7358                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
7359                            LEFT JOIN tbl_budget_type_groups btg ON bt.budget_type_group_id = btg.budget_type_group_id
7360                            LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
7361                        WHERE
7362                            q.{$field} IS NOT NULL
7363                            AND q.for_add = 0
7364                            AND q.budget_type_id IS NOT NULL
7365                            AND q.budget_type_id != 7
7366                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1                            
7367                            {$where}
7368                            {$whereYear}
7369                        GROUP BY
7370                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
7371                            MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
7372                            WEEK(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)) WITH ROLLUP
7373                        ORDER BY
7374                            YEAR DESC,
7375                            MONTH ASC,
7376                            WEEK ASC,
7377                            DATE_FORMAT(q.{$field}, '%e') ASC";
7378            // return $query;
7379            $result = DB::select($query);
7380
7381            $query = "SELECT
7382                        btg.budget_type_group_id,
7383                        btg.name,
7384                        (
7385                            SELECT
7386                                GROUP_CONCAT(COALESCE(bt.name, '') ORDER BY ISNULL(bt.priority), bt.priority ASC SEPARATOR '|')
7387                            FROM
7388                                tbl_budget_types bt
7389                            WHERE
7390                                bt.budget_type_group_id = btg.budget_type_group_id
7391                        ) budget_types
7392                        FROM
7393                            tbl_budget_type_groups btg
7394                        ORDER BY
7395                            ISNULL(btg.priority),
7396                            btg.priority ASC";
7397
7398            $budgetTypeGroups = DB::select($query);
7399
7400            foreach ($budgetTypeGroups as $item) {
7401                $item->group_key_name = str_replace(" ", "", $item->name) . $item->budget_type_group_id;
7402                $item->budget_types = explode("|", (string) $item->budget_types);
7403            }
7404
7405            return response([
7406                'message' => 'OK',
7407                'data' => $result,
7408                'budgetTypeGroups' => $budgetTypeGroups,
7409            ]);
7410
7411        } catch (\Exception $e) {
7412            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_BY_TYPES_OF_BUDGETS_CREATED_PER_WEEK_EXCEPTION'));
7413            return response(['message' => 'KO', 'error' => $e->getMessage()]);
7414        }
7415    }
7416
7417    public function preview_file($id)
7418    {
7419
7420        try {
7421
7422            $file = TblFiles::where('file_id', $id)->first();
7423
7424            if (! $file) {
7425                return response()->json([
7426                    'message' => 'KO',
7427                    'error' => __('language.file_not_found'),
7428                ], 404);
7429            }
7430
7431            if (! Storage::disk('s3')->exists('uploads/'.$file->filename)) {
7432                return response()->json(['message' => 'File not found'], 404);
7433            }
7434
7435            $url = Storage::disk('s3')->temporaryUrl(
7436                'uploads/'.$file->filename,
7437                now()->addMinutes(5)
7438            );
7439
7440            return response()->json([
7441                'filename' => $file->filename,
7442                'url' => $url,
7443                'uploaded_by' => $file->uploaded_by,
7444                'uploaded_at' => $file->uploaded_at,
7445            ]);
7446
7447        } catch (\Exception $e) {
7448            report(AppException::fromException($e, 'PREVIEW_FILE_EXCEPTION'));
7449            return response()->json([
7450                'message' => 'KO',
7451                'error' => $e->getMessage(),
7452            ], 500);
7453        }
7454    }
7455
7456    function get_past_added_quotation(Request $request): ResponseFactory|HttpResponse{
7457
7458        try {
7459
7460            $data = $request->all();
7461            $keyword = addslashes((string) $data['keyword']);
7462            $result = [];
7463
7464            if(!empty($keyword)){
7465                $array = explode(' ', $keyword);
7466
7467                $where = '';
7468
7469                $availableParameters = ['client', 'email'];
7470
7471                $searchTextArray = explode(' ', $keyword);
7472
7473                $searchArray = [];
7474                $matchScoreArray = [];
7475                foreach ($availableParameters as $field) {
7476                    foreach ($searchTextArray as $word) {
7477                        array_push($searchArray, "({$field} LIKE '%{$word}%')");
7478                        array_push($matchScoreArray, "CASE WHEN {$field} LIKE '%{$word}%' THEN 1 ELSE 0 END");
7479                    }
7480                }
7481
7482                $searchArray = implode(' OR ', $searchArray);
7483                $matchScoreArray = implode(' + ', $matchScoreArray);
7484                $matchScoreCol = "({$matchScoreArray})";
7485                $where .= " AND ({$searchArray}";
7486
7487                $query = "SELECT
7488                            id,
7489                            client,
7490                            segment_id,
7491                            CONCAT(`client`, ' - ', email) `client_email`,
7492                            email,
7493                            phone_number,
7494                            customer_type_id,
7495                            {$matchScoreCol} match_score
7496                        FROM tbl_quotations
7497                        WHERE for_add = 0
7498                        AND email IS NOT NULL AND phone_number IS NOT NULL
7499                        {$where}
7500                        GROUP BY client, email
7501                        ORDER BY match_score DESC, client ASC
7502                        ";
7503
7504                $result = DB::select($query);
7505            }
7506
7507            return response(['message' => 'OK', 'data' => $result]);
7508
7509        } catch (\Exception $e) {
7510            report(AppException::fromException($e, 'GET_PAST_ADDED_QUOTATION_EXCEPTION'));
7511            return response(['message' => 'KO', 'error' => $e->getMessage()]);
7512        }
7513    }
7514
7515    function send_acceptance_notification($quotationId, $companyId, $userId, $updatedBy): void{
7516
7517        $budget = TblQuotations::where('id', $quotationId)->first();
7518
7519        if ($budget != null) {
7520            $to = TblToAcceptanceNotifications::where('company_id', $companyId)->get();
7521            $cc = TblCcAcceptanceNotifications::where('company_id', $companyId)->get();
7522
7523            if (count($to) > 0 && count($cc) > 0) {
7524
7525                $company = TblCompanies::where('company_id', $companyId)->first();
7526
7527                $quoteId = $budget->quote_id;
7528                $amount = $this->currency($budget->amount, 1);
7529
7530                $url = config('app.frontend_url') . "orders/{$quotationId}?company_id={$companyId}";
7531                $href = "<a href='{$url}'>{$quoteId}</a>";
7532
7533                $imgpath = File::get('fireservicetitan.png');
7534                $base64 = 'data:image/png;base64,'.base64_encode($imgpath);
7535
7536                $body = __('language.send_acceptance_notification.body_hello');
7537                $body .= __('language.send_acceptance_notification.body_message');
7538
7539                $body = str_replace('{{client}}', $budget->client, $body);
7540                $body = str_replace('{{username}}', $updatedBy, $body);
7541                $body = str_replace('{{company}}', $company->name, $body);
7542                $body = str_replace('{{amount}}', $amount, $body);
7543                $body = str_replace('{{quote_id}}', $href, $body);
7544
7545                $body .= '<p>Fire Service Titan</p>';
7546                $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
7547
7548                $html = '<!DOCTYPE html>';
7549                $html .= '<html>';
7550                $html .= '<head>';
7551                $html .= '<meta charset="UTF-8">';
7552                $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
7553                $html .= '</head>';
7554                $html .= '<body>';
7555                $html .= $body;
7556                $html .= '</body>';
7557                $html .= '</html>';
7558
7559                $subject = __('language.send_acceptance_notification.subject');
7560                $subject = str_replace('{{quote_id}}', $quoteId, $subject);
7561
7562                $email = new \SendGrid\Mail\Mail;
7563
7564                $user = TblUsers::where('id', $userId)->first();
7565
7566                if(config('services.sendgrid.staging')){
7567                    $email->addTo($user->email);
7568                } else {
7569
7570                    $emails = [];
7571
7572                    foreach ($to as $item) {
7573                        if (! in_array($item->email, $emails)) {
7574                            array_push($emails, $item->email);
7575                            $email->addTo($item->email);
7576                        }
7577                    }
7578
7579                    foreach ($cc as $item) {
7580                        if (! in_array($item->email, $emails)) {
7581                            array_push($emails, $item->email);
7582                            $email->addCc($item->email);
7583                        }
7584                    }
7585
7586                    $email->addCc($user->email);
7587                    array_push($emails, $user->email);
7588
7589                    $ccUser = TblUsers::where('name', $budget->commercial)->first();
7590
7591                    if ($ccUser) {
7592                        if (! in_array($ccUser->email, $emails)) {
7593                            $email->addCc($ccUser->email);
7594                        }
7595                    }
7596                }
7597
7598                $email->setFrom('fire@fire.es', 'Fire Service Titan');
7599                $email->setSubject($subject);
7600                $email->addContent('text/html', $html);
7601
7602                $email->addAttachment(
7603                    $imgpath,
7604                    'image/png',
7605                    'fireservicetitan.png',
7606                    'inline',
7607                    'fireservicetitan'
7608                );
7609
7610                $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
7611
7612                $response = $sendgrid->send($email);
7613                if ($response->statusCode() == 202) {
7614                    $this->addUpdateLog($quotationId, $userId, 'send_acceptance_notification', null, null, 5);
7615                    $comment = 'Email de aprobación automática enviada al equipo de opearciones el '.date('Y-m-d H:i:s');
7616                    $budget->last_follow_up_comment = $budget->last_follow_up_comment."\n".$comment;
7617
7618                    TblQuotations::where('id', $quotationId)->update(
7619                        [
7620                            'last_follow_up_comment' => $budget->last_follow_up_comment
7621                        ]
7622                    );
7623
7624                    Log::channel('email_log')->info('ID:'.$quoteId.' - ACCEPTANCE EMAIL NOTIFICATION SENT');
7625                } else {
7626                    $error = true;
7627                    Log::channel('email_log')->error('ID:'.$quoteId.' - '.$response->body());
7628                }
7629
7630            }
7631        }
7632    }
7633
7634    function get_total_quotations_by_budget_status(Request $request): ResponseFactory|HttpResponse{
7635
7636        try {
7637
7638            $data = $request->all();
7639
7640            $companyId = addslashes((string) $data['company_id']);
7641
7642            $where = '';
7643
7644            if ($companyId != 0) {
7645                $where = " AND a.company_id = {$companyId} ";
7646            } else {
7647                $where = " AND a.company_id IN ({$this->companyId}";
7648            }
7649
7650            $user = null;
7651
7652            if (isset($data['commercial'])) {
7653                if ($data['commercial'] != 'All') {
7654                    $user = TblUsers::where('name', $data['commercial'])->first();
7655                }
7656            } else {
7657                $user = TblUsers::where('id', $this->userId)->first();
7658            }
7659
7660            $totalPendingFollowUps = 0;
7661            $totalRequestAndVisit = 0;
7662            $totalError = 0;
7663            $totalG3WError = 0;
7664            $totalSendToClient = 0;
7665
7666            $d = false;
7667
7668            if ($user != null) {
7669                $where .= " AND a.commercial = '{$user->name}";
7670                $d = true;
7671            }
7672
7673            if ($data['commercial'] == 'All') {
7674                $d = true;
7675            }
7676
7677            if ($d) {
7678                $blacklist = implode('|', $this->getBlacklistEmails());
7679                $query = "SELECT
7680                            COUNT(DISTINCT a.id) total
7681                        FROM
7682                            tbl_quotations a
7683                            LEFT JOIN (
7684                                SELECT
7685                                a.id,
7686                                SUBSTRING_INDEX(
7687                                    SUBSTRING_INDEX(a.email, ',', n.digit + 1),
7688                                    ',',
7689                                    -1
7690                                ) AS email_domain
7691                                FROM
7692                                tbl_quotations a
7693                                INNER JOIN (
7694                                    SELECT
7695                                    0 AS digit
7696                                    UNION ALL
7697                                    SELECT
7698                                    1
7699                                    UNION ALL
7700                                    SELECT
7701                                    2
7702                                    UNION ALL
7703                                    SELECT
7704                                    3
7705                                    UNION ALL
7706                                    SELECT
7707                                    4
7708                                    UNION ALL
7709                                    SELECT
7710                                    5
7711                                    UNION ALL
7712                                    SELECT
7713                                    6
7714                                    UNION ALL
7715                                    SELECT
7716                                    7
7717                                    UNION ALL
7718                                    SELECT
7719                                    8
7720                                    UNION ALL
7721                                    SELECT
7722                                    9
7723                                ) n ON LENGTH(
7724                                    REPLACE(a.email, ',', '')
7725                                ) <= LENGTH(a.email)- n.digit
7726                                GROUP BY a.id
7727                            ) temp ON a.id = temp.id
7728                            LEFT JOIN tbl_companies b
7729                                ON a.company_id = b.company_id
7730                        WHERE
7731                            a.last_follow_up_date < NOW()
7732                            AND a.budget_status_id IN (2)
7733                            AND a.email IS NOT NULL
7734                            AND a.email <> ''
7735                            AND NOT EXISTS (
7736                                SELECT
7737                                1
7738                                FROM
7739                                tbl_blocked_domains bd
7740                                WHERE
7741                                temp.email_domain LIKE CONCAT('%', bd.domain, '%')
7742                                AND bd.company_id = a.company_id
7743                            )
7744                            AND a.last_follow_up_date IS NOT NULL
7745                            AND a.reason_for_not_following_up_id IS NULL
7746                            AND a.last_follow_up_date > 0
7747                            AND a.total_sent < b.limit_reminder_emails
7748                            AND (
7749                                a.email IS NOT NULL 
7750                                AND TRIM(a.email) != '' 
7751                                AND a.email REGEXP '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}'
7752                                AND a.email NOT REGEXP '($blacklist)'
7753                            )
7754                            AND a.for_add = 0 {$where}";
7755
7756                $result = DB::select($query);
7757
7758                $totalPendingFollowUps = $result[0]->total;
7759
7760                $query = "SELECT
7761                                COUNT(1) total
7762                            FROM
7763                                tbl_quotations a
7764                            WHERE
7765                                a.budget_status_id IN (6, 8, 12)
7766                                AND a.for_add = 0
7767                                {$where}
7768                            ";
7769
7770                $result = DB::select($query);
7771
7772                $totalRequestAndVisit = $result[0]->total;
7773
7774                $blacklist = implode('|', $this->getBlacklistEmails());
7775
7776                $query = "SELECT
7777                                COUNT(1) total
7778                            FROM
7779                                tbl_quotations a
7780                            WHERE
7781                                a.for_add = 0 AND
7782                                (
7783                                    a.x_status IN ('Error','Error - Bounce','Error - Spam')
7784                                    OR (
7785                                        a.email IS NULL 
7786                                        OR TRIM(a.email) = '' 
7787                                        OR a.email NOT REGEXP '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}'
7788                                        OR a.email REGEXP '($blacklist)'
7789                                    )
7790                                ) 
7791                                AND a.budget_status_id IN(1, 2, 11, 17, 21, 22)
7792                                    {$where}
7793                            ";
7794
7795                $result = DB::select($query);
7796
7797                $totalError = $result[0]->total;
7798
7799                $query = "SELECT
7800                                COUNT(1) total
7801                                FROM
7802                                    tbl_quotations a
7803                                WHERE
7804                                    a.g3w_warning = 1
7805                                    AND a.for_add = 0
7806                                    {$where}
7807                                ";
7808
7809                $result = DB::select($query);
7810
7811                $totalG3WError = $result[0]->total;
7812
7813                $query = "SELECT
7814                                COUNT(1) total
7815                                FROM
7816                                    tbl_quotations a
7817                                WHERE
7818                                    a.budget_status_id = (SELECT budget_status_id FROM tbl_budget_status WHERE name = 'Validado')
7819                                    AND a.total_sent = 0
7820                                    AND a.for_add = 0
7821                                    {$where}
7822                                ";
7823
7824                $result = DB::select($query);
7825
7826                $totalSendToClient = $result[0]->total;
7827            }
7828
7829            return response([
7830                'message' => 'OK',
7831                'userId' => ($user) ? $user->id : null,
7832                'totalPendingFollowUps' => $totalPendingFollowUps,
7833                'totalRequestAndVisit' => $totalRequestAndVisit,
7834                'totalError' => $totalError,
7835                'totalG3WError' => $totalG3WError,
7836                'totalSendToClient' => $totalSendToClient,
7837            ]);
7838
7839        } catch (\Exception $e) {
7840            report(AppException::fromException($e, 'GET_TOTAL_QUPTATIONS_BY_BUDGET_STATUS_EXCEPTION'));
7841            return response(['message' => 'KO', 'error' => $e->getMessage()]);
7842        }
7843
7844    }
7845
7846    public function sendgrid_webhook_receiver(Request $request)
7847    {
7848
7849        try {
7850
7851            $data = $request->all();
7852
7853            $jsonBody = [];
7854            $order = [];
7855            $orderEmails = [];
7856
7857            Log::channel('email_log')->info('WEBHOOK: '.json_encode($data));
7858
7859            $quoteId = null;
7860
7861            foreach ($data as $item) {
7862                $matches = explode(".", (string) $item['sg_message_id']);
7863                $messageId = $matches[0];
7864
7865                Log::channel('email_log')->info('MESSAGE-ID: '.$messageId);
7866
7867                $result = TblSendgridWebhook::where('x_message_id', $messageId)->first();
7868                $quoteId = $result->quotation_id;
7869                Log::channel('email_log')->info('SENDGRID-BODY: '.json_encode($result));
7870
7871                if (empty($order)) {
7872                    $order = TblQuotations::where('x_message_id', $messageId)->first();
7873                    $quoteId = $order->id;
7874                    // if(config('services.sendgrid.staging')){
7875                    //     $user = TblUsers::where('name', $order->updated_by)->first();
7876
7877                    //     $orderEmails = array($user->email);
7878                    // }else{
7879                        $orderEmails = explode(",", (string) $order->email);
7880                    // }
7881                }
7882
7883                // if(in_array($item['email'], $orderEmails)){
7884                    if($result->json_body == null){
7885                        array_push($jsonBody, $item);
7886                    }else{
7887                        $jsonBody = json_decode((string) $result->json_body);
7888                        array_push($jsonBody, $item);
7889                    }
7890                // }
7891
7892                Log::channel('email_log')->info('JSON-BODY: '.json_encode($jsonBody));
7893
7894                TblSendgridWebhook::where('x_message_id', $messageId)->update(
7895                    [
7896                        'json_body' => json_encode($jsonBody),
7897                        'updated_at' => date('Y-m-d H:i:s')
7898                    ]
7899                );
7900
7901                if ($quoteId != null) {
7902                    $this->get_files($quoteId);
7903                }
7904
7905                Cache::flush();
7906            }
7907
7908        } catch (\Exception $e) {
7909            report(AppException::fromException($e, 'SENDGRID_WEBHOOK_RECEIVER_EXCEPTION'));
7910            return response(['message' => 'KO', 'error' => $e->getMessage()]);
7911        }
7912
7913    }
7914
7915    function isEmailValid($email): bool {
7916        // Regular expression pattern for email validation
7917        $pattern = '/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/';
7918
7919        // Check if the email matches the pattern
7920        if (preg_match($pattern, (string) $email)) {
7921            return true; // Valid email
7922        } else {
7923            return false; // Invalid email
7924        }
7925    }
7926
7927    public function list_email_status($companyId): ResponseFactory|HttpResponse {
7928        try {
7929            $params = [];
7930
7931            if ($companyId != 0) {
7932                $where = ' company_id = ? ';
7933                $params[] = $companyId;
7934            } else {
7935                $ids = is_string($this->companyId) ? explode(',', $this->companyId) : $this->companyId;
7936                $ids = array_filter($ids); 
7937
7938                if (empty($ids)) {
7939                    return response(['message' => 'OK', 'data' => []]);
7940                }
7941
7942                $placeholders = implode(',', array_fill(0, count($ids), '?'));
7943                $where = " company_id IN ($placeholders";
7944                $params = array_values($ids);
7945            }
7946
7947            $query = "SELECT DISTINCT x_status FROM tbl_quotations WHERE {$where} AND x_status IS NOT NULL";
7948            $result = DB::select($query, $params);
7949
7950            return response([
7951                'message' => 'OK',
7952                'data' => $result,
7953            ]);
7954
7955        } catch (\Exception $e) {
7956            report($e);
7957
7958            return response(['message' => 'KO', 'error' => $e->getMessage()], 500);
7959        }
7960    }
7961
7962    function list_quotation_analytics_commercial(Request $request): ResponseFactory|HttpResponse{
7963
7964        try {
7965
7966            $data = $request->all();
7967            $companyId = addslashes((string) $data['company_id']);
7968            $field = $data['field'];
7969
7970            $where = "";
7971            $whereYear = "";
7972            $dateLflArray = [];
7973            $companyIds = $this->companyIds;
7974            $whereQ = '';
7975
7976            $acc = '';
7977            if ($field == 'acceptance_date') {
7978                $acc = ' AND q.acceptance_date IS NOT NULL ';
7979                // $field = 'created_at';
7980
7981                if (@$data['data_to_display'] == 4) {
7982                    $field = 'created_at';
7983                    $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7984                    $whereQ .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7985                }
7986            } else {
7987                $field = 'created_at';
7988                $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7989                $whereQ .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
7990            }
7991
7992            if (isset($data['years']) && $data['years'] != null) {
7993                $years = implode(',', $data['years']);
7994                if (count($data['years']) > 0) {
7995                    $whereYear = " AND YEAR(q.{$field}) IN ({$years})";
7996                }
7997            }
7998
7999            if ($companyId != 0) {
8000                $companyIds = [$companyId];
8001            }
8002
8003            foreach ($companyIds as $v) {
8004
8005                $lflWhere = " AND q.company_id = {$v} ";
8006
8007                $query = "SELECT
8008                            CONCAT(
8009                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
8010                                ' - ',
8011                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
8012                            ) AS date_like,
8013                            YEAR(q.{$field}) 'year',
8014                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
8015                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
8016                            {$v} 'company_id'
8017                        FROM
8018                            tbl_quotations q
8019                        WHERE
8020                            q.{$field} IS NOT NULL
8021                            AND q.for_add = 0
8022                            {$lflWhere}
8023                            {$whereYear}
8024                        GROUP BY YEAR(q.{$field})
8025                        ORDER BY YEAR(q.{$field}) DESC";
8026
8027                $dateLike = DB::select($query);
8028
8029                $dateLflArray[$v] = $dateLike;
8030            }
8031
8032            $isFy = true;
8033
8034            if (isset($data['ytd']) && $data['ytd'] != null && $data['ytd'] == true) {
8035                $isFy = false;
8036                $ytdArray = [];
8037                $ytdAcceptanceArray = [];
8038                $lflCompanyIds = [];
8039                foreach ($dateLflArray as $k => $v) {
8040                    foreach ($dateLflArray[$k] as $item) {
8041                        $year = $item->year;
8042                        $now = date('m-d');
8043                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
8044                    }
8045
8046                    $ytdArray = implode(' OR ', $ytdArray);
8047                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
8048                    $ytdArray = [];
8049                }
8050
8051                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
8052                $where .= " AND ({$lflCompanyIds}";
8053                $whereQ .= " AND ({$lflCompanyIds}";
8054            }
8055
8056            if (isset($data['lfl']) && $data['lfl'] != null && $data['lfl'] == true) {
8057                $isFy = false;
8058                $lflArray = [];
8059                $ytdAcceptanceArray = [];
8060                $lflCompanyIds = [];
8061                foreach ($dateLflArray as $k => $v) {
8062                    foreach ($dateLflArray[$k] as $item) {
8063                        $year = $item->year;
8064                        $min_date_like = $item->min_date_like;
8065                        $max_date_like = $item->max_date_like;
8066                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
8067                    }
8068
8069                    $lflArray = implode(' OR ', $lflArray);
8070                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
8071                    $lflArray = [];
8072                }
8073
8074                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
8075                $where .= " AND ({$lflCompanyIds}";
8076                $whereQ .= " AND ({$lflCompanyIds}";
8077            }
8078
8079            if ($isFy) {
8080                if ($companyId != 0) {
8081                    $where .= " AND q.company_id = {$companyId} ";
8082                    $whereQ .= " AND q.company_id = {$companyId} ";
8083                } else {
8084                    $where .= " AND q.company_id IN ({$this->companyId})";
8085                    $whereQ .= " AND q.company_id IN ({$this->companyId})";
8086                }
8087            }
8088
8089            if (isset($data['source']) && $data['source'] != null) {
8090                $where .= " AND s.name = '{$data['source']}'";
8091            }
8092
8093            if (isset($data['month']) && $data['month'] != null) {
8094                $where .= " AND MONTH(q.{$field}) = '{$data['month']}'";
8095            }
8096
8097            if (isset($data['week']) && $data['week'] != null) {
8098                $where .= " AND WEEK(q.{$field}) = '{$data['week']}'";
8099            }
8100
8101            if (isset($data['commercial']) && $data['commercial'] != null) {
8102                $where .= " AND q.commercial = '{$data['commercial']}'";
8103            }
8104
8105            if (isset($data['created_by']) && $data['created_by'] != null) {
8106                $where .= " AND q.created_by = '{$data['created_by']}'";
8107            }
8108
8109            if (isset($data['budget_type']) && $data['budget_type'] != null) {
8110                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
8111            }
8112
8113            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
8114                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
8115            }
8116
8117            if (isset($data['budget_status']) && $data['budget_status'] != null) {
8118                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
8119            }
8120
8121            if (isset($data['client_type']) && $data['client_type'] != null) {
8122                $where .= " AND ct.customer_type_id = {$data['client_type']}";
8123            }
8124
8125            if (isset($data['segment_id']) && $data['segment_id'] != null) {
8126                $where .= " AND q.segment_id = {$data['segment_id']}";
8127            }
8128
8129            $col = '1';
8130
8131            if (isset($data['data_to_display']) && $data['data_to_display'] != null) {
8132                if ($data['data_to_display'] == 1) {
8133                    $col = '1';
8134                }
8135
8136                if ($data['data_to_display'] == 2) {
8137                    $col = 'q.amount';
8138                }
8139            }
8140
8141            $query = "SELECT
8142                        q.commercial 'name',
8143                        COUNT(q.commercial) totalCommercial
8144                    FROM tbl_quotations q
8145                    WHERE
8146                        q.for_add = 0
8147                        AND q.{$field} IS NOT NULL
8148                        {$whereQ}
8149                    GROUP BY q.commercial
8150                    HAVING COUNT(q.commercial) > 0
8151                    ORDER BY totalCommercial DESC";
8152
8153            $resultCommercials = DB::select($query);
8154
8155            $now = TblQuotations::whereIn('company_id', $this->companyIds)->orderBy($field, 'DESC')->pluck($field)->first();
8156            $weekNumber = date('W', strtotime((string) $now));
8157            $thisWeek = date('Y-m-d', strtotime($now . " - " . (date('N', strtotime((string) $now)) - 1) . " days"));
8158
8159            $query = "SELECT
8160                        q.commercial 'name',
8161                        COUNT(q.commercial) totalCommercial
8162                    FROM tbl_quotations q
8163                    WHERE
8164                        q.for_add = 0
8165                        AND q.{$field} IS NOT NULL
8166                        AND YEARWEEK(q.{$field}, 1) = YEARWEEK(NOW(), 1)
8167                        {$whereQ}
8168                    GROUP BY q.commercial
8169                    HAVING COUNT(q.commercial) > 0
8170                    ORDER BY totalCommercial DESC";
8171
8172            $resultCommercialsOrder = DB::select($query);
8173
8174            $namesToRemove = [];
8175
8176            foreach ($resultCommercialsOrder as $item) {
8177                $namesToRemove[$item->name] = true;
8178            }
8179
8180            $resultArray = [];
8181            foreach ($resultCommercials as $item) {
8182                if (! isset($namesToRemove[$item->name])) {
8183                    $resultArray[] = $item;
8184                }
8185            }
8186
8187            $resultCommercials = array_merge($resultCommercialsOrder, $resultArray);
8188
8189            $cols = '';
8190
8191            $colsGroupConcatIds = '';
8192
8193            foreach ($resultCommercials as $item) {
8194                $cols .= ",COALESCE(
8195                    SUM(
8196                        CASE WHEN q.commercial = '{$item->name}{$acc} THEN {$col} ELSE 0 END
8197                    ), 0
8198                ) '{$item->name}'";
8199
8200                $colsGroupConcatIds .= ",GROUP_CONCAT(CASE WHEN q.commercial = '{$item->name}{$acc} THEN q.id END) 'groupConcatIds-{$item->name}'";
8201            }
8202
8203            $cols .= ",COALESCE(
8204                SUM(
8205                    CASE WHEN q.commercial IS NOT NULL {$acc} THEN {$col} ELSE 0 END
8206                ), 0
8207            ) total";
8208
8209            if (@$data['data_to_display'] == 3) {
8210
8211                $cols = '';
8212
8213                foreach ($resultCommercials as $item) {
8214                    $cols .= ",COALESCE(
8215                        (
8216                            SUM(CASE WHEN q.commercial = '{$item->name}{$acc} THEN q.amount END)
8217                        ) /
8218                        (
8219                            SUM(CASE WHEN q.commercial = '{$item->name}{$acc} THEN 1 END)
8220                        )
8221                    , 0) '{$item->name}'";
8222                }
8223
8224                $cols .= ",COALESCE(
8225                    (
8226                        SUM(CASE WHEN q.commercial IS NOT NULL {$acc} THEN q.amount END)
8227                    ) /
8228                    (
8229                        SUM(CASE WHEN q.commercial IS NOT NULL {$acc} THEN 1 END)
8230                    )
8231                , 0) total";
8232            }
8233
8234            if (@$data['data_to_display'] == 4) {
8235
8236                $cols = '';
8237
8238                foreach ($resultCommercials as $item) {
8239                    $cols .= ",COALESCE(
8240                        (
8241                            SUM(CASE WHEN q.commercial = '{$item->name}' AND q.acceptance_date IS NOT NULL THEN 1 END)
8242                        ) /
8243                        (
8244                            SUM(CASE WHEN q.commercial = '{$item->name}' AND q.created_at IS NOT NULL THEN 1 END)
8245                        ) * 100
8246                    , 0) '{$item->name}'";
8247                }
8248
8249                $cols .= ',COALESCE(
8250                    (
8251                        SUM(CASE WHEN q.commercial IS NOT NULL AND q.acceptance_date IS NOT NULL THEN 1 END)
8252                    ) /
8253                    (
8254                        SUM(CASE WHEN q.commercial IS NOT NULL AND q.created_at IS NOT NULL THEN 1 END)
8255                    ) * 100
8256                , 0) total';
8257            }
8258
8259            $query = "SELECT
8260                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)) AS 'year',
8261                            LPAD(MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)), 2, 0) AS 'month',
8262                            LPAD(WEEK(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)), 2, 0) AS 'week',
8263                            DATE_FORMAT(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY), '%W, %M %e') 'namedate',
8264                            GROUP_CONCAT(
8265                                CASE WHEN q.{$field} IS NOT NULL
8266                                THEN q.id END
8267                            ) AS groupConcatIds
8268                            {$colsGroupConcatIds}
8269                            {$cols}
8270                        FROM
8271                            tbl_quotations q
8272                            LEFT JOIN tbl_sources s ON s.source_id = q.source_id
8273                            LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
8274                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
8275                            LEFT JOIN tbl_budget_type_groups btg ON bt.budget_type_group_id = btg.budget_type_group_id
8276                            LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
8277                        WHERE
8278                            q.{$field} IS NOT NULL
8279                            AND q.for_add = 0
8280                            AND q.budget_type_id != 7
8281                            AND q.budget_type_id IS NOT NULL
8282                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
8283                            {$where}
8284                            {$whereYear}
8285                        GROUP BY
8286                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
8287                            MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
8288                            WEEK(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)) WITH ROLLUP
8289                        ORDER BY
8290                            YEAR DESC,
8291                            MONTH ASC,
8292                            WEEK ASC,
8293                            DATE_FORMAT(q.{$field}, '%e') ASC";
8294
8295            $value = Cache::get(base64_encode($query));
8296
8297            if (! $value) {
8298                $result = DB::select($query);
8299
8300                Cache::put(base64_encode($query), $result, 600);
8301            } else {
8302                $result = $value;
8303            }
8304
8305            return response([
8306                'message' => 'OK',
8307                'data' => $result,
8308                'commercials' => $resultCommercials,
8309            ]);
8310
8311        } catch (\Exception $e) {
8312            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_COMMERCIAL_EXCEPTION'));
8313            return response(['message' => 'KO', 'error' => $e->getMessage()]);
8314        }
8315    }
8316
8317    function clear_open_data($companyId): ResponseFactory|HttpResponse{
8318
8319        try {
8320
8321            $companyIds = [$companyId];
8322            if ($companyId == 0) {
8323                $companyIds = $this->companyIds;
8324            }
8325
8326            $user = TblUsers::where('id', $this->userId)->first();
8327
8328            if (count($companyIds) > 0) {
8329
8330                foreach ($companyIds as $id) {
8331                    $startedAt = date('Y-m-d H:i:s');
8332                    $affectedRows = DB::delete("DELETE FROM tbl_quotations WHERE company_id = {$id} AND for_add = 1 AND DATE_FORMAT(created_at, '%Y-%m-%d') != DATE_FORMAT(NOW(), '%Y-%m-%d')");
8333
8334                    TblOrdersUpdateLogs::create(
8335                        [
8336                            'company_id' => $id,
8337                            'to_process' => 'Orders',
8338                            'status' => 'success',
8339                            'for_add_deleted_affected_rows' => $affectedRows,
8340                            'processed_by' => $user->name,
8341                            'started_at' => $startedAt,
8342                            'ended_at' => date('Y-m-d H:i:s')
8343                        ]
8344                    );
8345                }
8346            }
8347
8348            return response([
8349                'message' => 'OK',
8350            ]);
8351
8352        } catch (\Exception $e) {
8353            report(AppException::fromException($e, 'CLEAR_OPEN_DATA_EXCEPTION'));
8354            return response(['message' => 'KO', 'error' => $e->getMessage()]);
8355        }
8356
8357    }
8358
8359    function list_quotation_analytics_order_size(Request $request): ResponseFactory|HttpResponse{
8360
8361        try {
8362
8363            $data = $request->all();
8364            $companyId = addslashes((string) $data['company_id']);
8365            $field = $data['field'];
8366
8367            $where = "";
8368            $dateLflArray = [];
8369            $whereYear = "";
8370            $companyIds = $this->companyIds;
8371
8372            if ($field == 'acceptance_date') {
8373                if (@$data['data_to_display'] == 4) {
8374                    $field = 'created_at';
8375                    $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
8376                }
8377            } else {
8378                $field = 'created_at';
8379                $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
8380            }
8381
8382            if (isset($data['years']) && $data['years'] != null) {
8383                $years = implode(',', $data['years']);
8384                if (count($data['years']) > 0) {
8385                    $whereYear = " AND YEAR(q.{$field}) IN ({$years})";
8386                }
8387            }
8388
8389            if ($companyId != 0) {
8390                $companyIds = [$companyId];
8391            }
8392
8393            foreach ($companyIds as $v) {
8394
8395                $lflWhere = " AND q.company_id = {$v} ";
8396
8397                $query = "SELECT
8398                            CONCAT(
8399                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
8400                                ' - ',
8401                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
8402                            ) AS date_like,
8403                            YEAR(q.{$field}) 'year',
8404                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
8405                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
8406                            {$v} 'company_id'
8407                        FROM
8408                            tbl_quotations q
8409                        WHERE
8410                            q.{$field} IS NOT NULL
8411                            AND q.for_add = 0
8412                            {$lflWhere}
8413                            {$whereYear}
8414                        GROUP BY YEAR(q.{$field})
8415                        ORDER BY YEAR(q.{$field}) DESC";
8416
8417                $dateLike = DB::select($query);
8418
8419                $dateLflArray[$v] = $dateLike;
8420            }
8421
8422            $isFy = true;
8423
8424            if (isset($data['ytd']) && $data['ytd'] != null && $data['ytd'] == true) {
8425                $isFy = false;
8426                $ytdArray = [];
8427                $ytdAcceptanceArray = [];
8428                $lflCompanyIds = [];
8429                foreach ($dateLflArray as $k => $v) {
8430                    foreach ($dateLflArray[$k] as $item) {
8431                        $year = $item->year;
8432                        $now = date('m-d');
8433                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
8434                    }
8435
8436                    $ytdArray = implode(' OR ', $ytdArray);
8437                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
8438                    $ytdArray = [];
8439                }
8440
8441                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
8442                $where .= " AND ({$lflCompanyIds}";
8443            }
8444
8445            if (isset($data['lfl']) && $data['lfl'] != null && $data['lfl'] == true) {
8446                $isFy = false;
8447                $lflArray = [];
8448                $ytdAcceptanceArray = [];
8449                $lflCompanyIds = [];
8450                foreach ($dateLflArray as $k => $v) {
8451                    foreach ($dateLflArray[$k] as $item) {
8452                        $year = $item->year;
8453                        $min_date_like = $item->min_date_like;
8454                        $max_date_like = $item->max_date_like;
8455                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
8456                    }
8457
8458                    $lflArray = implode(' OR ', $lflArray);
8459                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
8460                    $lflArray = [];
8461                }
8462
8463                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
8464                $where .= " AND ({$lflCompanyIds}";
8465            }
8466
8467            if ($isFy) {
8468                if ($companyId != 0) {
8469                    $where .= " AND q.company_id = {$companyId} ";
8470                } else {
8471                    $where .= " AND q.company_id IN ({$this->companyId})";
8472                }
8473            }
8474
8475            if (isset($data['source']) && $data['source'] != null) {
8476                $where .= " AND s.name = '{$data['source']}'";
8477            }
8478
8479            if (isset($data['source']) && $data['source'] != null) {
8480                $where .= " AND s.name = '{$data['source']}'";
8481            }
8482
8483            if (isset($data['commercial']) && $data['commercial'] != null) {
8484                $where .= " AND q.commercial = '{$data['commercial']}'";
8485            }
8486
8487            if (isset($data['created_by']) && $data['created_by'] != null) {
8488                $where .= " AND q.created_by = '{$data['created_by']}'";
8489            }
8490
8491            if (isset($data['budget_type']) && $data['budget_type'] != null) {
8492                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
8493            }
8494
8495            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
8496                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
8497            }
8498
8499            if (isset($data['budget_status']) && $data['budget_status'] != null) {
8500                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
8501            }
8502
8503            if (isset($data['client_type']) && $data['client_type'] != null) {
8504                $where .= " AND ct.customer_type_id = {$data['client_type']}";
8505            }
8506
8507            if (isset($data['segment_id']) && $data['segment_id'] != null) {
8508                $where .= " AND q.segment_id = {$data['segment_id']}";
8509            }
8510
8511            $col = 'q.one';
8512
8513            if (isset($data['data_to_display']) && $data['data_to_display'] != null) {
8514                if ($data['data_to_display'] == 1) {
8515                    $col = 'q.one';
8516                }
8517
8518                if ($data['data_to_display'] == 2) {
8519                    $col = 'q.amount';
8520                }
8521            }
8522
8523            if ((isset($data['start_date']) && $data['start_date'] != null) && isset($data['end_date']) && $data['end_date'] != null) {
8524                $where .= " AND q.{$field} BETWEEN '{$data['start_date']}' AND '{$data['end_date']}";
8525            } elseif ((isset($data['start_date']) && $data['start_date'] == null || $data['start_date'] == '') && isset($data['end_date']) && $data['end_date'] != null) {
8526                $where .= " AND q.{$field} = '{$data['end_date']}";
8527            }
8528
8529            $whereQ = $where;
8530
8531            $sortBy = [
8532                0 => 'q.amount < 100',
8533                1 => 'q.amount BETWEEN 100 AND 500',
8534                2 => 'q.amount BETWEEN 500 AND 2000',
8535                3 => 'q.amount BETWEEN 2000 AND 10000',
8536                4 => 'q.amount BETWEEN 10000 AND 30000',
8537                5 => 'q.amount BETWEEN 30000 AND 100000',
8538                6 => 'q.amount BETWEEN 100000 AND 999999999999'
8539            ];
8540
8541            $query = "SELECT
8542                        q.commercial 'name',
8543                        COUNT(q.commercial) totalCommercial
8544                    FROM tbl_quotations q
8545                    LEFT JOIN tbl_sources s ON s.source_id = q.source_id
8546                    LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
8547                    LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
8548                    LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
8549                    WHERE
8550                        q.for_add = 0
8551                        AND q.{$field} IS NOT NULL                        
8552                        AND q.budget_type_id != 7
8553                        AND q.budget_type_id IS NOT NULL
8554                        {$whereQ}
8555                        {$whereYear}
8556                    GROUP BY q.commercial
8557                    HAVING COUNT(q.commercial) > 0
8558                    ORDER BY totalCommercial DESC";
8559
8560            $resultCommercials = DB::select($query);
8561
8562            if (isset($data['sort_by'])) {
8563                if ($data['sort_by'] != 7) {
8564                    $s = $sortBy[$data['sort_by']];
8565                    $whereQ .= " AND {$s} ";
8566                }
8567            }
8568
8569            $num = 'COUNT(1)';
8570
8571            if (@$data['data_to_display'] == 2) {
8572                $num = 'SUM(q.amount)';
8573            }
8574
8575            if (@$data['data_to_display'] == 3) {
8576                $num = 'SUM(q.amount) / COUNT(1)';
8577            }
8578
8579            if (@$data['data_to_display'] == 4) {
8580                $num = '(SUM(CASE WHEN q.acceptance_date IS NOT NULL THEN 1 ELSE 0 END) / COUNT(q.created_at) * 100)';
8581            }
8582
8583            $query = "SELECT
8584                    q.commercial 'name',
8585                    COUNT(q.commercial) totalCommercial,
8586                    {$num} num
8587                FROM tbl_quotations q
8588                LEFT JOIN tbl_sources s ON s.source_id = q.source_id
8589                LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
8590                LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
8591                LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
8592                WHERE
8593                    q.for_add = 0
8594                    AND q.{$field} IS NOT NULL                    
8595                    AND q.budget_type_id != 7
8596                    AND q.budget_type_id IS NOT NULL
8597                    {$whereQ}
8598                    {$whereYear}
8599                GROUP BY q.commercial
8600                HAVING COUNT(q.commercial) > 0
8601                ORDER BY num DESC";
8602
8603            $resultCommercialsOrder = DB::select($query);
8604
8605            foreach ($resultCommercialsOrder as $item) {
8606                $namesToRemove[$item->name] = true;
8607            }
8608
8609            $resultArray = [];
8610            foreach ($resultCommercials as $item) {
8611                if (! isset($namesToRemove[$item->name])) {
8612                    $resultArray[] = $item;
8613                }
8614            }
8615
8616            $resultCommercials = array_merge($resultCommercialsOrder, $resultArray);
8617
8618            $cols = '';
8619
8620            $colsGroupConcatIds = '';
8621
8622            foreach ($resultCommercials as $item) {
8623                $cols .= ",COALESCE(
8624                    SUM(
8625                        CASE WHEN q.commercial = '{$item->name}' THEN {$col} ELSE 0 END
8626                    ), 0
8627                ) '{$item->name}'";
8628
8629                $colsGroupConcatIds .= ",GROUP_CONCAT(CASE WHEN q.commercial = '{$item->name}' THEN q.id END) 'groupConcatIds-{$item->name}'";
8630            }
8631
8632            $cols .= ",COALESCE(
8633                SUM(
8634                    CASE WHEN q.commercial IS NOT NULL THEN {$col} ELSE 0 END
8635                ), 0
8636            ) total";
8637
8638            $range = "CASE
8639                        WHEN amount < 100 THEN '< 100€'
8640                        WHEN amount BETWEEN 100 AND 500 THEN '100€ - 500€'
8641                        WHEN amount BETWEEN 500 AND 2000 THEN '500€ - 2k€'
8642                        WHEN amount BETWEEN 2000 AND 10000 THEN '2k€ - 10k€'
8643                        WHEN amount BETWEEN 10000 AND 30000 THEN '10k€ - 30k€'
8644                        WHEN amount BETWEEN 30000 AND 100000 THEN '30k€ - 100k€'
8645                        WHEN amount BETWEEN 100000 AND 999999999999 THEN '> 100k€'
8646                    END AS amount_range";
8647
8648            if (@$data['data_to_display'] == 3) {
8649
8650                $range = "CASE
8651                            WHEN SUM(amount) / COUNT(1) < 100 THEN '< 100€'
8652                            WHEN SUM(amount) / COUNT(1) BETWEEN 100 AND 500 THEN '100€ - 500€'
8653                            WHEN SUM(amount) / COUNT(1) BETWEEN 500 AND 2000 THEN '500€ - 2k€'
8654                            WHEN SUM(amount) / COUNT(1) BETWEEN 2000 AND 10000 THEN '2k€ - 10k€'
8655                            WHEN SUM(amount) / COUNT(1) BETWEEN 10000 AND 30000 THEN '10k€ - 30k€'
8656                            WHEN SUM(amount) / COUNT(1) BETWEEN 30000 AND 100000 THEN '30k€ - 100k€'
8657                            WHEN SUM(amount) / COUNT(1) BETWEEN 100000 AND 999999999999 THEN '> 100k€'
8658                        END AS amount_range";
8659
8660                $cols = '';
8661
8662                foreach ($resultCommercials as $item) {
8663                    $cols .= ",COALESCE(
8664                        (
8665                            SUM(CASE WHEN q.commercial = '{$item->name}' THEN q.amount END)
8666                        ) /
8667                        (
8668                            SUM(CASE WHEN q.commercial = '{$item->name}' THEN q.one END)
8669                        )
8670                    , 0) '{$item->name}'";
8671                }
8672
8673                $cols .= ',COALESCE(
8674                    (
8675                        SUM(CASE WHEN q.commercial IS NOT NULL THEN q.amount END)
8676                    ) /
8677                    (
8678                        SUM(CASE WHEN q.commercial IS NOT NULL THEN q.one END)
8679                    )
8680                , 0) total';
8681            }
8682
8683            if (@$data['data_to_display'] == 4) {
8684
8685                $cols = '';
8686
8687                foreach ($resultCommercials as $item) {
8688                    $cols .= ",COALESCE(
8689                        (
8690                            SUM(CASE WHEN q.commercial = '{$item->name}' THEN q.acceptanceDate END)
8691                        ) /
8692                        (
8693                            SUM(CASE WHEN q.commercial = '{$item->name}' THEN q.createdAt END)
8694                        ) * 100
8695                    , 0) '{$item->name}'";
8696                }
8697
8698                $cols .= ',COALESCE(
8699                    (
8700                        SUM(CASE WHEN q.commercial IS NOT NULL THEN q.acceptanceDate END)
8701                    ) /
8702                    (
8703                        SUM(CASE WHEN q.commercial IS NOT NULL THEN q.createdAt END)
8704                    ) * 100
8705                , 0) total';
8706            }
8707
8708            $query = "WITH amount_ranges AS (
8709                            SELECT '< 100€' AS amount_range
8710                            UNION ALL SELECT '100€ - 500€'
8711                            UNION ALL SELECT '500€ - 2k€'
8712                            UNION ALL SELECT '2k€ - 10k€'
8713                            UNION ALL SELECT '10k€ - 30k€'
8714                            UNION ALL SELECT '30k€ - 100k€'
8715                            UNION ALL SELECT '> 100k€'
8716                        )
8717
8718                        SELECT
8719                            ar.amount_range,
8720                             GROUP_CONCAT(
8721                                q.id
8722                            ) AS groupConcatIds
8723                            {$colsGroupConcatIds}
8724                            {$cols}
8725                        FROM
8726                            amount_ranges ar
8727                        LEFT JOIN (
8728                            SELECT
8729                                commercial,
8730                                GROUP_CONCAT(
8731                                    CASE WHEN q.{$field} IS NOT NULL THEN id END
8732                                ) id,
8733                                amount AS amount,
8734                                COUNT(CASE WHEN q.created_at IS NOT NULL THEN 1 END) createdAt,
8735                                COUNT(CASE WHEN q.acceptance_date IS NOT NULL THEN 1 END) acceptanceDate,
8736                                COUNT(1) AS one,
8737                                {$range}
8738                            FROM
8739                                tbl_quotations q
8740                                LEFT JOIN tbl_sources s ON s.source_id = q.source_id
8741                                LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
8742                                LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
8743                                LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
8744                            WHERE
8745                                q.{$field} IS NOT NULL
8746                                AND q.for_add = 0                                
8747                                AND q.budget_type_id != 7
8748                                AND q.budget_type_id IS NOT NULL
8749                                AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
8750                                {$where}
8751                                {$whereYear}
8752                            GROUP BY
8753                                commercial,
8754                                id
8755                        ) AS q ON ar.amount_range = q.amount_range
8756                        GROUP BY
8757                            ar.amount_range WITH ROLLUP
8758                        ORDER BY
8759                            CASE
8760                                WHEN ar.amount_range = '< 100€' THEN 1
8761                                WHEN ar.amount_range = '100€ - 500€' THEN 2
8762                                WHEN ar.amount_range = '500€ - 2k€' THEN 3
8763                                WHEN ar.amount_range = '2k€ - 10k€' THEN 4
8764                                WHEN ar.amount_range = '10k€ - 30k€' THEN 5
8765                                WHEN ar.amount_range = '30k€ - 100k€' THEN 6
8766                                ELSE 7
8767                            END";
8768
8769            $value = Cache::get(base64_encode($query));
8770
8771            if (! $value) {
8772                $result = DB::select($query);
8773
8774                Cache::put(base64_encode($query), $result, 600);
8775            } else {
8776                $result = $value;
8777            }
8778
8779            return response([
8780                'message' => 'OK',
8781                'data' => $result,
8782                'commercials' => $resultCommercials,
8783            ]);
8784
8785        } catch (\Exception $e) {
8786            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_COMMERCIAL_EXCEPTION'));
8787            return response(['message' => 'KO', 'error' => $e->getMessage()]);
8788        }
8789
8790    }
8791
8792    function send_email_template_preview($emailTemplateId): ResponseFactory|HttpResponse{
8793
8794        try {
8795
8796            $emailTemplateId = addslashes((string) $emailTemplateId);
8797
8798            $emailTemplate = TblEmailConfiguration::where('id', $emailTemplateId)->first();
8799
8800            $user = TblUsers::where('id', $this->userId)->first();
8801            $error = false;
8802
8803            $toEmail = $user->email;
8804
8805            $availableParameters = [
8806                'quote_id',
8807                'company_id',
8808                'client',
8809                'client_type',
8810                'phone_number',
8811                'email',
8812                'issue_date',
8813                'request_date',
8814                'duration',
8815                'invoice_number',
8816                'type',
8817                'acceptance_date',
8818                'status',
8819                'source',
8820                'amount',
8821                'reason_for_not_following_up',
8822                'last_follow_up_date',
8823                'last_follow_up_comment',
8824                'reason_for_rejection_id',
8825                'reason_for_rejection',
8826                'commercial',
8827                'created_at',
8828                'created_by',
8829                'updated_at',
8830                'updated_by',
8831            ];
8832
8833            $dateParameters = [
8834                'issue_date',
8835                'request_date',
8836                'acceptance_date',
8837                'last_follow_up_date',
8838                'created_at',
8839                'updated_at',
8840            ];
8841
8842            if($this->locale == 'es'){
8843                setlocale(LC_ALL, "es_ES", 'Spanish_Spain', 'Spanish');
8844                setlocale(LC_ALL, "es_ES", 'Spanish_Spain', 'Spanish');
8845            }
8846
8847            $body = $emailTemplate->html;
8848            $subject = $emailTemplate->subject;
8849
8850            preg_match_all('/{{(.*?)}}/', (string) $body, $matches);
8851
8852            $parameters = $matches[1];
8853
8854            $result = TblQuotations::where('for_add', 0)->whereIn('company_id', $this->companyIds)->first();
8855
8856            foreach ($parameters as $parameter) {
8857
8858                if(in_array($parameter, $dateParameters)){
8859                    if($result->{$parameter}){
8860                        $result->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result->{$parameter})));
8861                    }
8862                }
8863
8864                if (in_array($parameter, $availableParameters)) {
8865                    $body = str_replace('{{'.$parameter.'}}', $result->{$parameter}, $body);
8866                }
8867            }
8868
8869            preg_match_all('/{{(.*?)}}/', (string) $subject, $matches);
8870
8871            $parameters = $matches[1];
8872
8873            foreach ($parameters as $parameter) {
8874
8875                if(in_array($parameter, $dateParameters)){
8876                    if($result->{$parameter}){
8877                        $result->{$parameter} = iconv('ISO-8859-2', 'UTF-8', strftime("%A, %B %d, %Y", strtotime((string) $result->{$parameter})));
8878                    }
8879                }
8880
8881                if (in_array($parameter, $availableParameters)) {
8882                    $subject = str_replace('{{'.$parameter.'}}', $result->{$parameter}, $subject);
8883                }
8884            }
8885
8886            $email = new \SendGrid\Mail\Mail;
8887
8888            $templateFiles = TblEmailFiles::where('email_template_id', $emailTemplateId)->orderBy('order', 'asc')->get();
8889
8890            foreach ($templateFiles as $item) {
8891                $f = storage_path('app/public/uploads/'.$item->filename);
8892                $imgpath = file_get_contents($f);
8893                $mimeType = mime_content_type($f);
8894
8895                $email->addAttachment(
8896                    $imgpath,
8897                    $mimeType,
8898                    str_replace(' ', '', $item->original_name),
8899                    'inline',
8900                    str_replace(' ', '', $item->original_name),
8901                );
8902
8903                $body .= "<img src='cid:{$item->original_name}' style='height: 45px; padding-right: 6px' />";
8904            }
8905
8906            $html = '<!DOCTYPE html>';
8907            $html .= '<html>';
8908            $html .= '<head>';
8909            $html .= '<meta charset="UTF-8">';
8910            $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
8911            $html .= '</head>';
8912            $html .= '<body>';
8913            $html .= $body;
8914            $html .= '</body>';
8915            $html .= '</html>';
8916
8917            if ($toEmail != null) {
8918
8919                $companyEmail = null;
8920
8921                if ($emailTemplate->from_id != null) {
8922                    $companyEmail = TblCompanyEmails::where('id', $emailTemplate->from_id)->first();
8923                } else {
8924                    $companyEmail = TblCompanyEmails::where('is_active', 1)->where('verified', 1)->where('company_id', $result->company_id)->first();
8925                }
8926
8927                if (! $companyEmail) {
8928                    return response(['message' => 'KO', 'error' => __('language.no_active_verified_sender')]);
8929                }
8930
8931                $email->setFrom($companyEmail->from_email, $companyEmail->from_name);
8932                $email->setSubject($subject);
8933                $email->addTo($toEmail);
8934                $email->addContent('text/html', $html);
8935
8936                $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
8937
8938                $response = $sendgrid->send($email);
8939                if ($response->statusCode() == 202) {
8940                    return response(['message' => 'OK']);
8941                }
8942            }
8943
8944            return response(['message' => 'KO']);
8945
8946        } catch (\Exception $e) {
8947            report(AppException::fromException($e, 'SEND_EMAIL_TEMPLATE_EXCEPTION'));
8948            return response(['message' => 'KO', 'error' => $e->getMessage()]);
8949        }
8950
8951    }
8952
8953    function list_quotation_analytics_by_types_of_budgets_company_per_week(Request $request): ResponseFactory|HttpResponse{
8954
8955        try {
8956
8957            $data = $request->all();
8958            $companyId = addslashes((string) $data['company_id']);
8959            $field = $data['field'];
8960
8961            $where = "";
8962            $whereYear = "";
8963            $dateLflArray = [];
8964            $companyIds = $this->companyIds;
8965
8966            $acc = '';
8967            if ($field == 'acceptance_date') {
8968                $acc = ' AND q.acceptance_date IS NOT NULL ';
8969                // $field = 'created_at';
8970
8971                if (@$data['data_to_display'] == 4) {
8972                    $field = 'created_at';
8973                    $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
8974                }
8975            } else {
8976                $field = 'created_at';
8977                $where .= ' AND YEAR(q.created_at) = YEAR(q.issue_date)';
8978            }
8979
8980            if ($companyId != 0) {
8981                $companyIds = [$companyId];
8982            }
8983
8984            if (isset($data['years']) && $data['years'] != null) {
8985
8986                if (count($data['years']) > 0) {
8987                    foreach ($data['years'] as $year) {
8988                        if (isset($data['week']) && $data['week'] != null) {
8989                            $w = sprintf('%02d', $data['week']);
8990                            $whereYear .= " AND YEARWEEK(q.{$field}, 1) = '{$year}{$w}'";
8991                        } else {
8992                            $whereYear .= " AND YEAR(q.{$field}) = {$year}";
8993                        }
8994                    }
8995                }
8996            }
8997
8998            foreach ($companyIds as $v) {
8999
9000                $lflWhere = " AND q.company_id = {$v} ";
9001
9002                $query = "SELECT
9003                            CONCAT(
9004                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
9005                                ' - ',
9006                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
9007                            ) AS date_like,
9008                            YEAR(q.{$field}) 'year',
9009                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
9010                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
9011                            {$v} 'company_id'
9012                        FROM
9013                            tbl_quotations q
9014                        WHERE
9015                            q.{$field} IS NOT NULL
9016                            AND q.for_add = 0
9017                            {$lflWhere}
9018                            {$whereYear}
9019                        GROUP BY YEAR(q.{$field})
9020                        ORDER BY YEAR(q.{$field}) DESC";
9021
9022                $dateLike = DB::select($query);
9023
9024                $dateLflArray[$v] = $dateLike;
9025            }
9026
9027            $isFy = true;
9028
9029            if (isset($data['ytd']) && $data['ytd'] != null && $data['ytd'] == true) {
9030                $isFy = false;
9031                $ytdArray = [];
9032                $ytdAcceptanceArray = [];
9033                $lflCompanyIds = [];
9034                foreach ($dateLflArray as $k => $v) {
9035                    foreach ($dateLflArray[$k] as $item) {
9036                        $year = $item->year;
9037                        $now = date('m-d');
9038                        array_push($ytdArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN '{$year}-01-01' AND '{$year}-{$now}'");
9039                    }
9040
9041                    $ytdArray = implode(' OR ', $ytdArray);
9042                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$ytdArray})");
9043                    $ytdArray = [];
9044                }
9045
9046                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
9047                $where .= " AND ({$lflCompanyIds}";
9048            }
9049
9050            if (isset($data['lfl']) && $data['lfl'] != null && $data['lfl'] == true) {
9051                $isFy = false;
9052                $lflArray = [];
9053                $ytdAcceptanceArray = [];
9054                $lflCompanyIds = [];
9055                foreach ($dateLflArray as $k => $v) {
9056                    foreach ($dateLflArray[$k] as $item) {
9057                        $year = $item->year;
9058                        $min_date_like = $item->min_date_like;
9059                        $max_date_like = $item->max_date_like;
9060                        array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
9061                    }
9062
9063                    $lflArray = implode(' OR ', $lflArray);
9064                    array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
9065                    $lflArray = [];
9066                }
9067
9068                $lflCompanyIds = implode(' OR ', $lflCompanyIds);
9069                $where .= " AND ({$lflCompanyIds}";
9070            }
9071
9072            if ($isFy) {
9073                if ($companyId != 0) {
9074                    $where .= " AND q.company_id = {$companyId} ";
9075                } else {
9076                    $where .= " AND q.company_id IN ({$this->companyId})";
9077                }
9078            }
9079
9080            if (isset($data['source']) && $data['source'] != null) {
9081                $where .= " AND s.name = '{$data['source']}'";
9082            }
9083
9084            if (isset($data['month']) && $data['month'] != null) {
9085                $where .= " AND MONTH(q.{$field}) = '{$data['month']}'";
9086            }
9087
9088            if (isset($data['commercial']) && $data['commercial'] != null) {
9089                $where .= " AND q.commercial = '{$data['commercial']}'";
9090            }
9091
9092            if (isset($data['created_by']) && $data['created_by'] != null) {
9093                $where .= " AND q.created_by = '{$data['created_by']}'";
9094            }
9095
9096            if (isset($data['budget_type']) && $data['budget_type'] != null) {
9097                $where .= " AND bt.budget_type_id = {$data['budget_type']}";
9098            }
9099
9100            if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
9101                $where .= " AND bt.budget_type_group_id = {$data['budget_type_group']}";
9102            }
9103
9104            if (isset($data['budget_status']) && $data['budget_status'] != null) {
9105                $where .= " AND bs.budget_status_id = {$data['budget_status']}";
9106            }
9107
9108            if (isset($data['client_type']) && $data['client_type'] != null) {
9109                $where .= " AND ct.customer_type_id = {$data['client_type']}";
9110            }
9111
9112            if (isset($data['segment_id']) && $data['segment_id'] != null) {
9113                $where .= " AND q.segment_id = {$data['segment_id']}";
9114            }
9115
9116            $col = '1';
9117
9118            if (isset($data['data_to_display']) && $data['data_to_display'] != null) {
9119                if ($data['data_to_display'] == 1) {
9120                    $col = '1';
9121                }
9122
9123                if ($data['data_to_display'] == 2) {
9124                    $col = 'q.amount';
9125                }
9126            }
9127
9128            $budgetTypes = TblBudgetTypes::orderByRaw('ISNULL(priority), priority ASC')->get();
9129            $cols = '';
9130            foreach ($budgetTypes as $item) {
9131                if ($item->name == '' || $item->name == null) {
9132                    $cols .= ",COALESCE(SUM(CASE WHEN bt.name IS NULL {$acc} THEN {$col} ELSE 0 END), 0) AS 'Otros'";
9133                } else {
9134                    $cols .= ",COALESCE(SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN {$col} ELSE 0 END), 0) AS '{$item->name}'";
9135                }
9136            }
9137
9138            $budgetTypeGroups = TblBudgetTypeGroups::orderByRaw('ISNULL(priority), priority ASC')->get();
9139
9140            $colsGroups = ",COALESCE(SUM(CASE WHEN bt.name IS NULL {$acc} THEN {$col} END), 0) AS Otros";
9141
9142            foreach ($budgetTypeGroups as $item) {
9143                $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
9144                $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
9145                $colsGroups .= ",COALESCE(SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN {$col} END), 0) AS '{$budgetTypeGroupName}'";
9146            }
9147
9148            $colsGroups .= ",COALESCE(SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN {$col} END), 0) AS total";
9149
9150            $col = $colsGroups.$cols;
9151
9152            if (@$data['data_to_display'] == 3) {
9153
9154                $cols = '';
9155                foreach ($budgetTypes as $item) {
9156                    if ($item->name == '' || $item->name == null) {
9157                        $cols .= ",COALESCE(
9158                                        SUM(CASE WHEN bt.name IS NULL {$acc} THEN q.amount ELSE 0 END) /
9159                                        SUM(CASE WHEN bt.name IS NULL {$acc} THEN 1 ELSE 0 END) * 100
9160                                    , 0) AS 'Otros'";
9161                    } else {
9162                        $cols .= ", COALESCE(
9163                                        SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN q.amount ELSE 0 END) /
9164                                        SUM(CASE WHEN bt.name = '{$item->name}{$acc} THEN 1 ELSE 0 END)
9165                                    , 0) AS '{$item->name}'";
9166                    }
9167                }
9168
9169                $colsGroups = ",COALESCE(
9170                                (SUM(CASE WHEN bt.name IS NULL {$acc} THEN q.amount END)) /
9171                                (SUM(CASE WHEN bt.name IS NULL {$acc} THEN 1 END))
9172                            , 0) Otros";
9173
9174                foreach ($budgetTypeGroups as $item) {
9175                    $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
9176                    $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
9177                    $colsGroups .= ",COALESCE(
9178                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.amount END)) /
9179                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN 1 END))
9180                                    , 0) '{$budgetTypeGroupName}'";
9181                }
9182
9183                $colsGroups .= ",COALESCE(
9184                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN q.amount END)) /
9185                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN 1 END))
9186                                , 0) total";
9187
9188                $col = $colsGroups.$cols;
9189            }
9190
9191            if (@$data['data_to_display'] == 4) {
9192
9193                $cols = '';
9194
9195                foreach ($budgetTypes as $item) {
9196
9197                    if ($item->name == '' || $item->name == null) {
9198                        $cols .= ",COALESCE(
9199                                        SUM(CASE WHEN bt.name IS NULL AND q.acceptance_date IS NOT NULL THEN 1 ELSE 0 END) /
9200                                        SUM(CASE WHEN bt.name IS NULL AND q.created_at IS NOT NULL THEN 1 ELSE 0 END) * 100
9201                                    , 0) AS 'Otros'";
9202                    } else {
9203                        $cols .= ", COALESCE(
9204                                        SUM(CASE WHEN bt.name = '{$item->name}' AND q.acceptance_date IS NOT NULL THEN 1 END) /
9205                                        SUM(CASE WHEN bt.name = '{$item->name}' AND q.created_at IS NOT NULL THEN 1 END) * 100
9206                                    , 0) AS '{$item->name}'";
9207                    }
9208                }
9209
9210                $colsGroups = ',COALESCE(
9211                                    (SUM(CASE WHEN bt.name IS NULL AND q.acceptance_date IS NOT NULL THEN 1 END)) /
9212                                    (SUM(CASE WHEN bt.name IS NULL AND q.created_at IS NOT NULL THEN 1 END)) * 100
9213                                , 0) Otros';
9214
9215                foreach ($budgetTypeGroups as $item) {
9216                    $budgetTypeGroupName = str_replace(' ', '', $item->name).$item->budget_type_group_id;
9217                    $colsGroups .= ",GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) {$acc} THEN q.id END) AS 'groupConcatIds{$budgetTypeGroupName}'";
9218                    $colsGroups .= ",COALESCE(
9219                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) AND q.acceptance_date IS NOT NULL THEN 1 END)) /
9220                                        (SUM(CASE WHEN (bt.budget_type_group_id = {$item->budget_type_group_id} OR bt.name IS NULL) AND q.created_at IS NOT NULL THEN 1 END)) * 100
9221                                    , 0) '{$budgetTypeGroupName}'";
9222                }
9223
9224                $colsGroups .= ',COALESCE(
9225                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) AND q.acceptance_date IS NOT NULL THEN 1 END)) /
9226                                    (SUM(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) AND q.created_at IS NOT NULL THEN 1 END)) * 100
9227                                    , 0) total';
9228
9229                $col = $colsGroups.$cols;
9230            }
9231
9232            $query = "SELECT
9233                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)) AS 'year',
9234                            LPAD(MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)), 2, 0) AS 'month',
9235                            q.company_id,
9236                            c.name 'companyName',
9237                            GROUP_CONCAT(CASE WHEN (bt.budget_type_group_id IS NOT NULL OR bt.name IS NULL) {$acc} THEN q.id END) groupConcatIds
9238                            {$col}
9239                        FROM
9240                            tbl_quotations q
9241                            LEFT JOIN tbl_sources s ON s.source_id = q.source_id
9242                            LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
9243                            LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
9244                            LEFT JOIN tbl_budget_type_groups btg ON bt.budget_type_group_id = btg.budget_type_group_id
9245                            LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
9246                            LEFT JOIN tbl_companies c ON q.company_id = c.company_id
9247                        WHERE
9248                            q.{$field} IS NOT NULL
9249                            AND q.for_add = 0
9250                            AND q.budget_type_id != 7
9251                            AND q.budget_type_id IS NOT NULL
9252                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1                            
9253                            {$where}
9254                            {$whereYear}
9255                        GROUP BY
9256                            YEAR(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
9257                            MONTH(DATE_ADD(q.{$field}, INTERVAL - WEEKDAY(q.{$field}) DAY)),
9258                            q.company_id WITH ROLLUP
9259                        ORDER BY
9260                            YEAR DESC,
9261                            MONTH ASC,
9262                            q.company_id ASC,
9263                            DATE_FORMAT(q.{$field}, '%e') ASC";
9264
9265            $result = DB::select($query);
9266
9267            $query = "SELECT
9268                        btg.budget_type_group_id,
9269                        btg.name,
9270                        (
9271                            SELECT
9272                                GROUP_CONCAT(COALESCE(bt.name, '') ORDER BY ISNULL(bt.priority), bt.priority ASC SEPARATOR '|')
9273                            FROM
9274                                tbl_budget_types bt
9275                            WHERE
9276                                bt.budget_type_group_id = btg.budget_type_group_id
9277                        ) budget_types
9278                        FROM
9279                            tbl_budget_type_groups btg
9280                        ORDER BY
9281                            ISNULL(btg.priority),
9282                            btg.priority ASC";
9283
9284            $budgetTypeGroups = DB::select($query);
9285
9286            foreach ($budgetTypeGroups as $item) {
9287                $item->group_key_name = str_replace(" ", "", $item->name) . $item->budget_type_group_id;
9288                $item->budget_types = explode("|", (string) $item->budget_types);
9289            }
9290
9291            return response([
9292                'message' => 'OK',
9293                'data' => $result,
9294                'budgetTypeGroups' => $budgetTypeGroups,
9295            ]);
9296
9297        } catch (\Exception $e) {
9298            report(AppException::fromException($e, 'LIST_QUOTATION_ANALYTICS_BY_TYPES_OF_BUDGETS_COMPANT_PER_WEEK_EXCEPTION'));
9299            return response(['message' => 'KO', 'error' => $e->getMessage()]);
9300        }
9301    }
9302
9303    function request_permission_commercial(Request $request): ResponseFactory|HttpResponse{
9304
9305        try {
9306
9307            $data = $request->all();
9308
9309            $id = addslashes((string) $data['id']);
9310            $requestedBy = $data['requested_by'];
9311            $body = '';
9312
9313            $result = TblQuotations::where('id', $id)->first();
9314
9315            $subject = __('language.request_permission_commercial.subject');
9316            $subject = str_replace('{{quote_id}}', $result->quote_id, $subject);
9317            $subject = str_replace('{{username}}', $requestedBy, $subject);
9318
9319            $email = new \SendGrid\Mail\Mail;
9320
9321            $imgpath = File::get('fireservicetitan.png');
9322
9323            $email->addAttachment(
9324                $imgpath,
9325                'image/png',
9326                'fireservicetitan.png',
9327                'inline',
9328                'fireservicetitan'
9329            );
9330
9331            $url = config('app.frontend_url') . "orders/{$id}?company_id={$result->company_id}";
9332            $href = "<a href='{$url}'>{$result->quote_id}</a>";
9333
9334            $user = TblUsers::where('name', $requestedBy)->first();
9335
9336            $urlClick = config('app.frontend_url') . "update?confirm_request={$id}&requested_by={$user->id}&quote_id={$result->quote_id}";
9337            $company = TblCompanies::where('company_id', $result->company_id)->first();
9338
9339            $body .= __('language.request_permission_commercial.body_hello');
9340            $body .= __('language.request_permission_commercial.body_message');
9341
9342            $amount = $this->currency($result->amount, 1);
9343
9344            $body = str_replace('{{commercial}}', $result->commercial, $body);
9345            $body = str_replace('{{company}}', $company->name, $body);
9346            $body = str_replace('{{client}}', $result->client, $body);
9347            $body = str_replace('{{amount}}', $amount, $body);
9348            $body = str_replace('{{quote_id}}', $href, $body);
9349            $body = str_replace('{{click}}', $urlClick, $body);
9350            $body = str_replace('{{username}}', $requestedBy, $body);
9351
9352            $body .= '<p>Fire Service Titan</p>';
9353            $body .= "<img src='cid:fireservicetitan' style='height: 45px;' />";
9354
9355            $html = '<!DOCTYPE html>';
9356            $html .= '<html>';
9357            $html .= '<head>';
9358            $html .= '<meta charset="UTF-8">';
9359            $html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
9360            $html .= '</head>';
9361            $html .= '<body>';
9362            $html .= $body;
9363            $html .= '</body>';
9364            $html .= '</html>';
9365
9366            $user = TblUsers::where('id', $this->userId)->first();
9367
9368            if(config('services.sendgrid.staging')){
9369                $email->addTo($user->email);
9370            } else {
9371                $email->addTo('luis.collar@fire.es');
9372
9373                $user = TblUsers::where('name', $result->created_by)->first();
9374                if ($user && $user->email != 'luis.collar@fire.es') {
9375                    $email->addTo($user->email);
9376                }
9377
9378                if ($result->created_by != $result->commercial) {
9379                    $user = TblUsers::where('name', $result->commercial)->first();
9380                    if ($user->email != 'luis.collar@fire.es') {
9381                        $email->addTo($user->email);
9382                    }
9383                }
9384            }
9385
9386            $email->setFrom('titan@fire.es');
9387            $email->setSubject($subject);
9388
9389            $email->addContent('text/html', $html);
9390
9391            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
9392
9393            $response = $sendgrid->send($email);
9394            if ($response->statusCode() == 202) {
9395                return response(['message' => 'OK']);
9396            }
9397
9398            $this->addUpdateLog($id, $requestedBy, 'request_permission_commercial', null, null, 4);
9399
9400            return response(['message' => 'KO']);
9401
9402        } catch (\Exception $e) {
9403            report(AppException::fromException($e, 'REQUEST_PERMISSION_EXCEPTION'));
9404            return response(['message' => 'KO', 'error' => $e->getMessage()]);
9405        }
9406
9407    }
9408
9409    function confirm_update_commercial(Request $request): ResponseFactory|HttpResponse{
9410
9411        try {
9412            sleep(3);
9413            $data = $request->all();
9414            $id = addslashes((string) $data['id']);
9415            $userId = addslashes((string) $data['requested_by']);
9416
9417            $user = TblUsers::where('id', $userId)->first();
9418
9419            if ($user) {
9420
9421                TblQuotations::where('id', $data['id'])->update(
9422                    [
9423                        'commercial' => $user->name,
9424                        'updated_at' => date('Y-m-d H:i:s')
9425                    ]
9426                );
9427
9428            } else {
9429                return response(['message' => 'KO', 'error' => 'invalid_user']);
9430            }
9431
9432            return response(['message' => 'OK']);
9433
9434        } catch (\Exception $e) {
9435            report(AppException::fromException($e, 'CONFIRM_UPDATE_COMMERCIAL_EXCEPTION'));
9436            return response(['message' => 'KO', 'error' => $e->getMessage()]);
9437        }
9438    }
9439
9440    function calculateEmailRequestSize(Mail $email): int{
9441
9442        $size = 0;
9443
9444        // Add size of 'from', 'to', 'subject', 'content'
9445        $from = $email->getFrom();
9446        $size += strlen(json_encode([
9447            'from' => $from->getEmail().' '.$from->getName(),
9448            'subject' => $email->getSubject(),
9449        ]));
9450
9451        // Add size of 'to' (recipients)
9452        $personalizations = $email->getPersonalizations() ?? [];
9453        foreach ($personalizations as $personalization) {
9454            foreach ($personalization->getTos() as $to) {
9455                $size += strlen($to->getEmail().' '.$to->getName());
9456            }
9457        }
9458
9459        // Add size of content
9460        foreach ($email->getContents() as $content) {
9461            $size += strlen((string) $content->getValue());
9462        }
9463
9464        // Add size of attachments (if any)
9465
9466        if ($email->getAttachments() != null && $email->getAttachments() != '') {
9467            foreach ($email->getAttachments() as $attachment) {
9468                $size += strlen($attachment->getContent()); // Base64 encoded size
9469                $size += strlen($attachment->getFilename());
9470                $size += strlen($attachment->getType());
9471            }
9472        }
9473
9474        $sizeInMegabytes = $size / 1048576; // 1 MB = 1,048,576 bytes
9475
9476        return (int) ceil($sizeInMegabytes);
9477    }
9478
9479    public function list_quotation_analytics_commercial_productivity(Request $request): ResponseFactory|HttpResponse{
9480
9481        // try {
9482
9483        $data = $request->all();
9484        $companyId = addslashes((string) $data['company_id']);
9485
9486        $where = '';
9487        $whereYear = '';
9488        $whereVisit = '';
9489
9490        $dateLflArray = [];
9491        $companyIds = $this->companyIds;
9492
9493        if ($companyId != 0) {
9494            $companyIds = [$companyId];
9495        }
9496
9497        $field = 'issue_date';
9498
9499        if (isset($data['years']) && $data['years'] != null) {
9500            $years = implode(',', $data['years']);
9501            if (count($data['years']) > 0) {
9502                $whereYear = " AND YEAR(q.{$field}) IN ({$years})";
9503            }
9504        }
9505
9506        foreach ($companyIds as $v) {
9507
9508            $lflWhere = " AND q.company_id = {$v} ";
9509
9510            $query = "SELECT
9511                            CONCAT(
9512                                DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field}),
9513                                ' - ',
9514                                DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%c/%e/'), YEAR({$field})
9515                            ) AS date_like,
9516                            YEAR(q.{$field}) 'year',
9517                            DATE_FORMAT((SELECT MIN(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS min_date_like,
9518                            DATE_FORMAT((SELECT MAX(q.{$field}) FROM tbl_quotations q WHERE q.{$field} IS NOT NULL {$lflWhere}), '%m-%d') AS max_date_like,
9519                            {$v} 'company_id'
9520                        FROM
9521                            tbl_quotations q
9522                        WHERE
9523                            q.{$field} IS NOT NULL
9524                            AND q.for_add = 0
9525                            {$lflWhere}
9526                            {$whereYear}
9527                        GROUP BY YEAR(q.{$field})
9528                        ORDER BY YEAR(q.{$field}) DESC";
9529
9530            $dateLike = DB::select($query);
9531
9532            if (count($dateLike) > 0) {
9533                $dateLflArray[$v] = $dateLike;
9534            }
9535        }
9536
9537        $whereAcceptanceDate = '';
9538
9539        if (isset($data['source']) && $data['source'] != null) {
9540            $where .= " AND s.name = '{$data['source']}'";
9541        }
9542
9543        if (isset($data['month']) && $data['month'] != null) {
9544            $where .= " AND MONTH(q.created_at) = '{$data['month']}'";
9545        }
9546
9547        if (isset($data['week']) && $data['week'] != null) {
9548            $where .= " AND WEEK(q.created_at) = '{$data['week']}'";
9549        }
9550
9551        if (isset($data['commercial']) && $data['commercial'] != null) {
9552            $commercial = implode("','", $data['commercial']);
9553            if (count($data['commercial']) > 0) {
9554                $where .= " AND q.commercial IN ('{$commercial}') ";
9555                $whereVisit .= " AND q.commercial IN ('{$commercial}') ";
9556            }
9557        }
9558
9559        if (isset($data['created_by']) && $data['created_by'] != null) {
9560            $created_by = implode("','", $data['created_by']);
9561            if (count($data['created_by']) > 0) {
9562                $where .= " AND q.created_by IN ('{$created_by}')";
9563            }
9564        }
9565
9566        if (isset($data['budget_type']) && $data['budget_type'] != null) {
9567            $where .= " AND bt.budget_type_id = {$data['budget_type']}";
9568        }
9569
9570        if (isset($data['budget_type_group']) && $data['budget_type_group'] != null) {
9571            $budgetTypeGroupIds = implode(',', $data['budget_type_group']);
9572            if (count($data['budget_type_group']) > 0) {
9573                $where .= " AND bt.budget_type_group_id IN ({$budgetTypeGroupIds})";
9574            }
9575        }
9576
9577        if (isset($data['budget_status']) && $data['budget_status'] != null) {
9578            $where .= " AND bs.budget_status_id = {$data['budget_status']}";
9579        }
9580
9581        if (isset($data['client_type']) && $data['client_type'] != null) {
9582            $where .= " AND ct.customer_type_id = {$data['client_type']}";
9583        }
9584
9585        if (isset($data['segment_id']) && $data['segment_id'] != null) {
9586            $where .= " AND q.segment_id = {$data['segment_id']}";
9587        }
9588
9589        if (isset($data['role_id']) && $data['role_id'] != null) {
9590            $roleId = implode(',', $data['role_id']);
9591            if (count($data['role_id']) > 0) {
9592                $where .= " AND r.role_id IN ({$roleId})";
9593                $whereVisit .= " AND r.role_id IN ({$roleId}";
9594            }
9595        }
9596
9597        $groupByFilter = 2;
9598        if (isset($data['group_by']) && $data['group_by'] != null) {
9599            $groupByFilter = $data['group_by'];
9600        }
9601
9602        $groupBy = '1, 2, 3, q.commercial, budget_type';
9603
9604        if ($groupByFilter == 1) {
9605            $groupBy = '1, q.commercial, 2, 3, budget_type';
9606        }
9607
9608        if ($groupByFilter == 3) {
9609            $groupBy = '1, budget_type, q.commercial, 2, 3';
9610        }
9611
9612        $aggregatedBy = 1;
9613        $aggregatedCol = "LPAD(q.month, 2, 0) AS 'month', LPAD(q.week, 2, 0) AS 'week', ";
9614        $aggregatedByCalc = ' / 4';
9615        if (isset($data['aggregated_by']) && $data['aggregated_by'] != null) {
9616            $aggregatedBy = $data['aggregated_by'];
9617            if ($data['aggregated_by'] == 1) {
9618
9619                $groupBy = '1, 2, 3, q.commercial, budget_type';
9620
9621                if ($groupByFilter == 1) {
9622                    $groupBy = '1, q.commercial, 2, 3, budget_type';
9623                }
9624
9625                if ($groupByFilter == 3) {
9626                    $groupBy = '1, budget_type, q.commercial, 2, 3';
9627                }
9628
9629                $aggregatedCol = "LPAD(q.month, 2, 0) AS 'month', LPAD(q.week, 2, 0) AS 'week', ";
9630            } elseif ($data['aggregated_by'] == 2) {
9631                $groupBy = '1, 2, q.commercial, budget_type';
9632
9633                if ($groupByFilter == 1) {
9634                    $groupBy = '1, q.commercial, 2, budget_type';
9635                }
9636
9637                if ($groupByFilter == 3) {
9638                    $groupBy = '1, budget_type, q.commercial, 2';
9639                }
9640
9641                $aggregatedCol = "LPAD(q.month, 2, 0) AS 'month', NULL AS 'week',";
9642                $aggregatedByCalc = '';
9643            } elseif ($data['aggregated_by'] == 3) {
9644                $groupBy = '1, q.commercial, budget_type';
9645
9646                if ($groupByFilter == 3) {
9647                    $groupBy = '1, budget_type, q.commercial';
9648                }
9649
9650                $aggregatedCol = "NULL AS 'month', NULL AS 'week',";
9651                $aggregatedByCalc = ' * 12';
9652            }
9653        }
9654
9655        $whereAcceptanceDate = $where;
9656        $whereCreatedAt = $where;
9657
9658        $isFy = true;
9659
9660        if ($companyId != 0) {
9661            $where .= " AND q.company_id = {$companyId} ";
9662            $whereCreatedAt .= " AND q.company_id = {$companyId} ";
9663            $whereAcceptanceDate .= " AND q.company_id = {$companyId} ";
9664            $whereVisit .= " AND q.company_id = {$companyId} ";
9665        } else {
9666            $where .= " AND q.company_id IN ({$this->companyId}";
9667            $whereCreatedAt .= " AND q.company_id IN ({$this->companyId}";
9668            $whereAcceptanceDate .= " AND q.company_id IN ({$this->companyId}";
9669            $whereVisit .= " AND q.company_id IN ({$this->companyId}";
9670        }
9671
9672        if (isset($data['campaign']) && $data['campaign'] != null) {
9673            $campaign = implode("','", $data['campaign']);
9674            if (count($data['campaign']) > 0) {
9675                $whereVisit .= " AND q.campaign IN ('{$campaign}')";
9676            }
9677        }
9678
9679        if (isset($data['ytd']) && $data['ytd'] != null && $data['ytd'] == true) {
9680            $isFy = false;
9681            $now = date('m-d');
9682
9683            $where .= " AND q.{$field} BETWEEN DATE_FORMAT(q.{$field}, '%Y-01-01') AND DATE_FORMAT(q.{$field}, '%Y-{$now}') ";
9684            $whereCreatedAt .= " AND q.created_at BETWEEN DATE_FORMAT(q.created_at, '%Y-01-01') AND DATE_FORMAT(q.created_at, '%Y-{$now}') ";
9685            $whereAcceptanceDate .= " AND q.acceptance_date BETWEEN DATE_FORMAT(q.acceptance_date, '%Y-01-01') AND DATE_FORMAT(q.acceptance_date, '%Y-{$now}') ";
9686            $whereVisit .= " AND q.visit_date BETWEEN DATE_FORMAT(q.visit_date, '%Y-01-01') AND DATE_FORMAT(q.visit_date, '%Y-{$now}') ";
9687        }
9688
9689        if (isset($data['lfl']) && $data['lfl'] != null && $data['lfl'] == true) {
9690            $isFy = false;
9691            $lflArray = [];
9692            $ytdAcceptanceArray = [];
9693            $lflCompanyIds = [];
9694            $lflCompanyIdsAcc = [];
9695            foreach ($dateLflArray as $k => $v) {
9696                foreach ($dateLflArray[$k] as $item) {
9697                    $year = $item->year;
9698                    $min_date_like = $item->min_date_like;
9699                    $max_date_like = $item->max_date_like;
9700                    array_push($lflArray, "DATE_FORMAT(q.{$field}, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
9701                    array_push($ytdAcceptanceArray, "DATE_FORMAT(q.acceptance_date, '%Y-%m-%d') BETWEEN LEAST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}') AND GREATEST('{$year}-{$min_date_like}', '{$year}-{$max_date_like}')");
9702                }
9703
9704                $lflArray = implode(' OR ', $lflArray);
9705                array_push($lflCompanyIds, "q.company_id = {$k} AND ({$lflArray})");
9706                $lflArray = [];
9707
9708                $ytdAcceptanceArray = implode(' OR ', $ytdAcceptanceArray);
9709                array_push($lflCompanyIdsAcc, "q.company_id = {$k} AND ({$ytdAcceptanceArray})");
9710                $ytdAcceptanceArray = [];
9711            }
9712
9713            $lflCompanyIds = implode(' OR ', $lflCompanyIds);
9714            $where .= " AND ({$lflCompanyIds}";
9715
9716            $lflCompanyIdsAcc = implode(' OR ', $lflCompanyIdsAcc);
9717            $whereAcceptanceDate .= " AND ({$lflCompanyIdsAcc}";
9718        }
9719
9720        $orderBy = 'CASE WHEN q.commercial IS NULL OR q.budget_type IS NULL THEN q.priority END DESC, q.priority ASC,';
9721
9722        if (isset($data['order_by'])) {
9723            $col = $data['order_by']['column'];
9724            $sort = $data['order_by']['sort'];
9725
9726            if (! empty($sort) || $sort != null) {
9727                $orderBy = "CASE WHEN q.commercial IS NULL OR q.budget_type IS NULL THEN {$col} END DESC, {$col} {$sort},";
9728            }
9729        }
9730
9731        $visitTypes = TblVisitTypeGroups::orderByRaw('ISNULL(priority), priority ASC')->get();
9732
9733        $visitCols = '';
9734        $visitMainTableCols = '';
9735        $visitSubMainTableCols = '';
9736        $visitSubTableCols = '';
9737        $visitMainCols = '';
9738
9739        $visitCall = ['Visita', 'Llamada'];
9740
9741        foreach ($visitCall as $value) {
9742            foreach ($visitTypes as $item) {
9743                $visitTypeNames = $value.$item->visit_type_group_id;
9744                $visitCols .= ",COUNT(CASE WHEN q.visit_date IS NOT NULL AND v.visit_type_group_id = {$item->visit_type_group_id} AND q.visit_call = '{$value}' THEN 1 END) AS 'total{$visitTypeNames}'";
9745                $visitCols .= ",GROUP_CONCAT(CASE WHEN q.visit_date IS NOT NULL AND v.visit_type_group_id = {$item->visit_type_group_id} AND q.visit_call = '{$value}' THEN q.id END) AS 'groupConcatIds{$visitTypeNames}'";
9746                $visitMainTableCols .= ",COALESCE(SUM(q.total{$visitTypeNames}), 0) AS 'total{$visitTypeNames}'";
9747                $visitMainTableCols .= ",GROUP_CONCAT(q.groupConcatIds{$visitTypeNames})  AS 'groupConcatIds{$visitTypeNames}'";
9748                $visitSubMainTableCols .= ",q.total{$visitTypeNames}";
9749                $visitSubMainTableCols .= ",q.groupConcatIds{$visitTypeNames}";
9750                $visitSubTableCols .= ",0 AS total{$visitTypeNames}";
9751                $visitSubTableCols .= ",NULL AS groupConcatIds{$visitTypeNames}";
9752                $visitMainCols .= ",COALESCE(SUM(q.total{$visitTypeNames}), 0) total{$visitTypeNames}";
9753                $visitMainCols .= ",GROUP_CONCAT(q.groupConcatIds{$visitTypeNames}) groupConcatIds{$visitTypeNames}";
9754            }
9755        }
9756
9757        $businessGoalsDefault = TblBusinessGoals::where('is_default', 1)->where('budget_type_group_id', 999999999)->first();
9758
9759        $businessGoalsDefault->issue_objective ??= 1;
9760        $businessGoalsDefault->acceptance_objective ??= 1;
9761        $businessGoalsDefault->new_objective ??= 1;
9762        $businessGoalsDefault->is_amount ??= 1;
9763
9764        $gO = '';
9765
9766        if ($groupByFilter != 3) {
9767            $gO = "ORDER BY
9768                        1 DESC,
9769                        2 ASC,
9770                        3 ASC,
9771                        q.commercial ASC,
9772                        {$orderBy}
9773                        DATE_FORMAT(q.namedate, '%e') ASC";
9774        } else {
9775            $gO = 'ORDER BY
9776                        1 DESC,
9777                        2 ASC,
9778                        3 ASC,
9779                        budget_type ASC,
9780                        q.commercial ASC';
9781        }
9782
9783        $query = "WITH business_goal_objective_users AS (
9784                        SELECT
9785                            bg.user_id,
9786                            bg.issue_objective,
9787                            bg.acceptance_objective,
9788                            bg.new_objective,
9789                            bg.is_amount
9790                        FROM tbl_business_goals bg
9791                        WHERE bg.budget_type_group_id = 999999999
9792                    ), business_goal_objective_roles AS (
9793                        SELECT
9794                            bg.role_id,
9795                            bg.issue_objective,
9796                            bg.acceptance_objective,
9797                            bg.new_objective,
9798                            bg.is_amount
9799                        FROM tbl_business_goals bg
9800                        WHERE bg.budget_type_group_id = 999999999
9801                    )
9802
9803                    SELECT
9804                        q.year,
9805                        {$aggregatedCol}
9806                        q.namedate created_at,
9807                        q.commercial,
9808                        q.budget_type,
9809                        COALESCE(SUM(q.issue_date), 0) totalIssue,
9810                        COALESCE(SUM(q.created_at), 0) totalCreatedAt,
9811                        GROUP_CONCAT(q.groupConcatIds) groupConcatIds,
9812                        CASE
9813                            WHEN SUM(q.is_amountIssue) > 0 THEN
9814                                (SUM(q.revenueIssue) / NULLIF(SUM(q.issueObjective), 0)) * 100
9815                            ELSE
9816                                (SUM(q.issue_date) / NULLIF(SUM(q.issueObjective), 0)) * 100
9817                        END AS totalIssueObjective,
9818                        CASE
9819                            WHEN (CASE
9820                                WHEN SUM(q.is_amountIssue) > 0 THEN
9821                                    (SUM(q.revenueIssue) / NULLIF(SUM(q.issueObjective), 0)) * 100
9822                                ELSE
9823                                    (SUM(q.issue_date) / NULLIF(SUM(q.issueObjective), 0)) * 100
9824                            END) BETWEEN 1 AND 70 THEN 'text-danger'
9825                            WHEN (CASE
9826                                WHEN SUM(q.is_amountIssue) > 0 THEN
9827                                    (SUM(q.revenueIssue) / NULLIF(SUM(q.issueObjective), 0)) * 100
9828                                ELSE
9829                                    (SUM(q.issue_date) / NULLIF(SUM(q.issueObjective), 0)) * 100
9830                            END) BETWEEN 70 AND 90 THEN 'text-warning'
9831                        END textIssueColor,
9832                        SUM(q.revenueIssue) revenueIssue,
9833                        CASE
9834                            WHEN SUM(q.is_amountIssue) > 0 THEN
9835                                (SUM(q.revenueIssue) / NULLIF(SUM(q.issueObjectiveMonthly), 0)) * 100
9836                            ELSE
9837                                (SUM(q.issue_date) / NULLIF(SUM(q.issueObjectiveMonthly), 0)) * 100
9838                        END AS totalIssueObjectiveMonthly,
9839                        CASE
9840                            WHEN SUM(q.is_amountIssue) > 0 THEN
9841                                (SUM(q.revenueIssue) / NULLIF(SUM(q.issueObjectiveYearly), 0)) * 100
9842                            ELSE
9843                                (SUM(q.issue_date) / NULLIF(SUM(q.issueObjectiveYearly), 0)) * 100
9844                        END AS totalIssueObjectiveYearly,
9845                        COALESCE(SUM(q.acceptance_date), 0) totalAcceptance,
9846                        GROUP_CONCAT(q.groupConcatCreatedAtIds) groupConcatCreatedAtIds,
9847                        SUM(q.totalIssueLessThan5) AS totalIssueLessThan5,
9848                        GROUP_CONCAT(q.groupConcatIdsIssueLessThan5) AS groupConcatIdsIssueLessThan5,
9849                        GROUP_CONCAT(q.groupConcatAcceptanceIds) groupConcatAcceptanceIds,
9850                        CASE
9851                            WHEN SUM(q.is_amountAcceptance) > 0 THEN
9852                                (SUM(q.revenueAcceptance) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9853                            ELSE
9854                                (SUM(q.acceptance_date) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9855                        END AS totalAcceptanceObjective,
9856                        CASE
9857                            WHEN SUM(q.is_amountAcceptance) > 0 THEN
9858                                (SUM(q.revenueAcceptance) / NULLIF(SUM(q.acceptanceObjectiveMonthly), 0)) * 100
9859                            ELSE
9860                                (SUM(q.acceptance_date) / NULLIF(SUM(q.acceptanceObjectiveMonthly), 0)) * 100
9861                        END AS totalAcceptanceObjectiveMonthly,
9862                        CASE
9863                            WHEN SUM(q.is_amountAcceptance) > 0 THEN
9864                                (SUM(q.revenueAcceptance) / NULLIF(SUM(q.acceptanceObjectiveYearly), 0)) * 100
9865                            ELSE
9866                                (SUM(q.acceptance_date) / NULLIF(SUM(q.acceptanceObjectiveYearly), 0)) * 100
9867                        END AS totalAcceptanceObjectiveYearly,
9868                        CASE
9869                            WHEN (CASE
9870                                WHEN SUM(q.is_amountAcceptance) > 0 THEN
9871                                    (SUM(q.revenueAcceptance) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9872                                ELSE
9873                                    (SUM(q.acceptance_date) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9874                            END) BETWEEN 1 AND 70 THEN 'text-danger'
9875                            WHEN (CASE
9876                                WHEN SUM(q.is_amountAcceptance) > 0 THEN
9877                                    (SUM(q.revenueAcceptance) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9878                                ELSE
9879                                    (SUM(q.acceptance_date) / NULLIF(SUM(q.acceptanceObjective), 0)) * 100
9880                            END) BETWEEN 70 AND 90 THEN 'text-warning'
9881                        END textAcceptanceColor,
9882                        SUM(q.revenueAcceptance) revenueAcceptance,
9883                        COALESCE(SUM(q.totalRejected), 0) totalRejected,
9884                        GROUP_CONCAT(q.groupConcatRejectedIds) groupConcatRejectedIds,
9885                        SUM(q.revenueRejected) revenueRejected,
9886                        COALESCE(SUM(q.totalNew)) totalNew,
9887                        GROUP_CONCAT(q.groupConcatNewIds) groupConcatNewIds,
9888                        CASE
9889                            WHEN SUM(q.is_amountNew) > 0 THEN
9890                                (SUM(q.revenueNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9891                            ELSE
9892                                (SUM(q.totalNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9893                        END AS totalNewObjective,
9894                        CASE
9895                            WHEN SUM(q.is_amountNew) > 0 THEN
9896                                (SUM(q.revenueNew) / NULLIF(SUM(q.newObjectiveMonthly), 0)) * 100
9897                            ELSE
9898                                (SUM(q.totalNew) / NULLIF(SUM(q.newObjectiveMonthly), 0)) * 100
9899                        END AS totalNewObjectiveMonthly,
9900                        CASE
9901                            WHEN SUM(q.is_amountNew) > 0 THEN
9902                                (SUM(q.revenueNew) / NULLIF(SUM(q.newObjectiveYearly), 0)) * 100
9903                            ELSE
9904                                (SUM(q.totalNew) / NULLIF(SUM(q.newObjectiveYearly), 0)) * 100
9905                        END AS totalNewObjectiveYearly,
9906                        CASE
9907                            WHEN (CASE
9908                                WHEN SUM(q.is_amountNew) > 0 THEN
9909                                    (SUM(q.revenueNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9910                                ELSE
9911                                    (SUM(q.totalNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9912                            END) BETWEEN 1 AND 70 THEN 'text-danger'
9913                            WHEN (CASE
9914                                WHEN SUM(q.is_amountNew) > 0 THEN
9915                                    (SUM(q.revenueNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9916                                ELSE
9917                                    (SUM(q.totalNew) / NULLIF(SUM(q.newObjective), 0)) * 100
9918                            END) BETWEEN 70 AND 90 THEN 'text-warning'
9919                        END textNewColor,
9920                        SUM(q.revenueNew) revenueNew,
9921                        COALESCE(SUM(q.totalVisit), 0) totalVisit,
9922                        GROUP_CONCAT(q.groupConcatVisitIds) groupConcatVisitIds,
9923                        COALESCE(SUM(q.totalCall), 0) totalCall,
9924                        GROUP_CONCAT(q.groupConcatCallIds) groupConcatCallIds,
9925                        SUM(q.is_amountIssue) AS is_amountIssue,
9926                        SUM(q.is_amountNew) AS is_amountNew,
9927                        SUM(q.is_amountAcceptance) AS is_amountAcceptance,
9928                        SUM(q.issueObjective) AS issueObjective,
9929                        SUM(q.issueObjectiveMonthly) AS issueObjectiveMonthly,
9930                        SUM(q.issueObjectiveYearly) AS issueObjectiveYearly,
9931                        SUM(q.newObjective) AS newObjective,
9932                        SUM(q.newObjectiveMonthly) AS newObjectiveMonthly,
9933                        SUM(q.newObjectiveYearly) AS newObjectiveYearly,
9934                        SUM(q.acceptanceObjective) AS acceptanceObjective,
9935                        SUM(q.acceptanceObjectiveMonthly) AS acceptanceObjectiveMonthly,
9936                        SUM(q.acceptanceObjectiveYearly) AS acceptanceObjectiveYearly
9937                        {$visitMainCols}
9938                    FROM
9939                    (
9940                        SELECT
9941                            q.year,
9942                            q.month,
9943                            q.week,
9944                            q.namedate,
9945                            SUM(q.issue_date) AS issue_date,
9946                            q.acceptance_date,
9947                            q.created_at,
9948                            q.commercial,
9949                            q.budget_type,
9950                            GROUP_CONCAT(q.groupConcatIds) AS groupConcatIds,
9951                            CASE
9952                                WHEN q.is_amountIssue > 0 THEN
9953                                    SUM(q.revenueIssue / q.issueObjective) * 100
9954                                ELSE
9955                                    SUM(q.issue_date / q.issueObjective) * 100
9956                                END
9957                            AS totalIssueObjective,
9958                            CASE
9959                                WHEN q.is_amountIssue > 0 THEN
9960                                    SUM(q.revenueIssue / q.issueObjectiveMonthly) * 100
9961                                ELSE
9962                                    SUM(q.issue_date / q.issueObjectiveMonthly) * 100
9963                                END
9964                            AS totalIssueObjectiveMonthly,
9965                            CASE
9966                                WHEN q.is_amountIssue > 0 THEN
9967                                    SUM(q.revenueIssue / q.issueObjectiveYearly) * 100
9968                                ELSE
9969                                    SUM(q.issue_date / q.issueObjectiveYearly) * 100
9970                                END
9971                            AS totalIssueObjectiveYearly,
9972                            SUM(q.revenueIssue) revenueIssue,
9973                            GROUP_CONCAT(q.groupConcatCreatedAtIds) AS groupConcatCreatedAtIds,
9974                            SUM(q.totalIssueLessThan5) totalIssueLessThan5,
9975                            GROUP_CONCAT(q.groupConcatIdsIssueLessThan5) groupConcatIdsIssueLessThan5,
9976                            GROUP_CONCAT(q.groupConcatAcceptanceIds) AS groupConcatAcceptanceIds,
9977                            SUM(q.acceptanceObjective) totalAcceptanceObjective,
9978                            SUM(q.acceptanceObjectiveMonthly) totalAcceptanceObjectiveMonthly,
9979                            SUM(q.acceptanceObjectiveYearly) totalAcceptanceObjectiveYearly,
9980                            q.revenueAcceptance,
9981                            SUM(q.totalRejected) AS totalRejected,
9982                            GROUP_CONCAT(q.groupConcatRejectedIds) AS groupConcatRejectedIds,
9983                            SUM(q.revenueRejected) revenueRejected,
9984                            SUM(q.totalNew) AS totalNew,
9985                            GROUP_CONCAT(q.groupConcatNewIds) AS groupConcatNewIds,
9986                            CASE
9987                                WHEN q.is_amountNew > 0 THEN
9988                                    SUM(q.revenueNew / q.newObjective) * 100
9989                                ELSE
9990                                    SUM(q.totalNew / q.newObjective) * 100
9991                                END
9992                            AS totalNewObjective,
9993                             CASE
9994                                WHEN q.is_amountNew > 0 THEN
9995                                    SUM(q.revenueNew / q.newObjectiveMonthly) * 100
9996                                ELSE
9997                                    SUM(q.totalNew / q.newObjectiveMonthly) * 100
9998                                END
9999                            AS totalNewObjectiveMonthly,
10000                             CASE
10001                                WHEN q.is_amountNew > 0 THEN
10002                                    SUM(q.revenueNew / q.newObjectiveYearly) * 100
10003                                ELSE
10004                                    SUM(q.totalNew / q.newObjectiveYearly) * 100
10005                                END
10006                            AS totalNewObjectiveYearly,
10007                            SUM(q.revenueNew) revenueNew,
10008                            q.totalVisit,
10009                            q.groupConcatVisitIds,
10010                            q.totalCall,
10011                            q.groupConcatCallIds,
10012                            q.priority,
10013                            SUM(q.is_amountIssue) AS is_amountIssue,
10014                            SUM(q.is_amountNew) AS is_amountNew,
10015                            SUM(q.is_amountAcceptance) AS is_amountAcceptance,
10016                            SUM(q.issueObjective) AS issueObjective,
10017                            SUM(q.issueObjectiveMonthly) AS issueObjectiveMonthly,
10018                            SUM(q.issueObjectiveYearly) AS issueObjectiveYearly,
10019                            SUM(q.newObjective) AS newObjective,
10020                            SUM(q.newObjectiveMonthly) AS newObjectiveMonthly,
10021                            SUM(q.newObjectiveYearly) AS newObjectiveYearly,
10022                            SUM(q.acceptanceObjective) AS acceptanceObjective,
10023                            SUM(q.acceptanceObjectiveMonthly) AS acceptanceObjectiveMonthly,
10024                            SUM(q.acceptanceObjectiveYearly) AS acceptanceObjectiveYearly
10025                            {$visitSubMainTableCols}
10026                        FROM (
10027                            SELECT
10028                                YEAR(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) 'year',
10029                                MONTH(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) 'month',
10030                                WEEK(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY)) 'week',
10031                                DATE_FORMAT(DATE_ADD(q.issue_date, INTERVAL - WEEKDAY(q.issue_date) DAY), '%W, %M %e') namedate,
10032                                COUNT(CASE WHEN q.issue_date IS NOT NULL THEN 1 END) issue_date,
10033                                0 acceptance_date,
10034                                0 created_at,
10035                                q.commercial,
10036                                btg.name budget_type,
10037                                GROUP_CONCAT(CASE WHEN q.issue_date IS NOT NULL THEN q.id END) AS groupConcatIds,
10038
10039                                SUM(CASE WHEN q.issue_date IS NOT NULL THEN q.amount END) AS revenueIssue,
10040                                NULL groupConcatCreatedAtIds,
10041                                0 totalIssueLessThan5,
10042                                NULL groupConcatIdsIssueLessThan5,
10043                                NULL groupConcatAcceptanceIds,
10044
10045                                0 revenueAcceptance,
10046                                COUNT(CASE WHEN bs.name = 'Rechazado' THEN 1 END) AS totalRejected,
10047                                GROUP_CONCAT(DISTINCT CASE WHEN bs.name = 'Rechazado' THEN q.id END) AS groupConcatRejectedIds,
10048                                COALESCE(SUM(CASE WHEN bs.name = 'Rechazado' THEN q.amount END), 0) AS revenueRejected,
10049                                COUNT(CASE WHEN ct.name = 'Nuevo' THEN 1 END) AS totalNew,
10050                                GROUP_CONCAT(DISTINCT CASE WHEN ct.name = 'Nuevo' THEN q.id END) AS groupConcatNewIds,
10051
10052                                COALESCE(SUM(CASE WHEN ct.name = 'Nuevo' THEN q.amount END), 0) revenueNew,
10053                                0 totalVisit,
10054                                NULL groupConcatVisitIds,
10055                                0 totalCall,
10056                                NULL groupConcatCallIds,
10057                                btg.priority,
10058                                CAST(
10059                                    CASE
10060                                        WHEN bg.issue_objective IS NOT NULL THEN bg.is_amount
10061                                        WHEN bgou.issue_objective IS NOT NULL THEN bgou.is_amount
10062                                        WHEN bg1.issue_objective IS NOT NULL THEN bg1.is_amount
10063                                        WHEN bgor.issue_objective IS NOT NULL THEN bgor.is_amount
10064                                        WHEN bgde.issue_objective IS NOT NULL THEN bgde.is_amount
10065                                        WHEN bg.issue_objective IS NULL AND bg1.issue_objective IS NULL THEN {$businessGoalsDefault->is_amount}
10066                                    END
10067                                AS DOUBLE) AS is_amountIssue,
10068                                CAST(
10069                                    CASE
10070                                        WHEN bg.new_objective IS NOT NULL THEN bg.is_amount
10071                                        WHEN bgou.new_objective IS NOT NULL THEN bgou.is_amount
10072                                        WHEN bg1.new_objective IS NOT NULL THEN bg1.is_amount
10073                                        WHEN bgor.new_objective IS NOT NULL THEN bgor.is_amount
10074                                        WHEN bgde.new_objective IS NOT NULL THEN bgde.is_amount
10075                                        WHEN bg.new_objective IS NULL AND bg1.new_objective IS NULL THEN {$businessGoalsDefault->is_amount}
10076                                    END
10077                                AS DOUBLE) AS is_amountNew,
10078                                0 is_amountAcceptance,
10079                                CAST(
10080                                    CASE
10081                                        WHEN bg.issue_objective IS NOT NULL THEN bg.issue_objective
10082                                        WHEN bgou.issue_objective IS NOT NULL THEN bgou.issue_objective
10083                                        WHEN bg1.issue_objective IS NOT NULL THEN bg1.issue_objective
10084                                        WHEN bgor.issue_objective IS NOT NULL THEN bgor.issue_objective
10085                                        WHEN bgde.issue_objective IS NOT NULL THEN bgde.issue_objective
10086                                        WHEN bg.issue_objective IS NULL AND bg1.issue_objective IS NULL THEN {$businessGoalsDefault->issue_objective}
10087                                    END {$aggregatedByCalc}
10088                                AS DOUBLE) AS issueObjective,
10089                                CAST(
10090                                    CASE
10091                                        WHEN bg.issue_objective IS NOT NULL THEN bg.issue_objective
10092                                        WHEN bgou.issue_objective IS NOT NULL THEN bgou.issue_objective
10093                                        WHEN bg1.issue_objective IS NOT NULL THEN bg1.issue_objective
10094                                        WHEN bgor.issue_objective IS NOT NULL THEN bgor.issue_objective
10095                                        WHEN bgde.issue_objective IS NOT NULL THEN bgde.issue_objective
10096                                        WHEN bg.issue_objective IS NULL AND bg1.issue_objective IS NULL THEN {$businessGoalsDefault->issue_objective}
10097                                    END
10098                                AS DOUBLE) AS issueObjectiveMonthly,
10099                                CAST(
10100                                    CASE
10101                                        WHEN bg.issue_objective IS NOT NULL THEN bg.issue_objective
10102                                        WHEN bgou.issue_objective IS NOT NULL THEN bgou.issue_objective
10103                                        WHEN bg1.issue_objective IS NOT NULL THEN bg1.issue_objective
10104                                        WHEN bgor.issue_objective IS NOT NULL THEN bgor.issue_objective
10105                                        WHEN bgde.issue_objective IS NOT NULL THEN bgde.issue_objective
10106                                        WHEN bg.issue_objective IS NULL AND bg1.issue_objective IS NULL THEN {$businessGoalsDefault->issue_objective}
10107                                    END * 12
10108                                AS DOUBLE) AS issueObjectiveYearly,
10109                                CAST(
10110                                    CASE
10111                                        WHEN bg.new_objective IS NOT NULL THEN bg.new_objective
10112                                        WHEN bgou.new_objective IS NOT NULL THEN bgou.new_objective
10113                                        WHEN bg1.new_objective IS NOT NULL THEN bg1.new_objective
10114                                        WHEN bgor.new_objective IS NOT NULL THEN bgor.new_objective
10115                                        WHEN bgde.new_objective IS NOT NULL THEN bgde.new_objective
10116                                        WHEN bg.new_objective IS NULL AND bg1.new_objective IS NULL THEN {$businessGoalsDefault->new_objective}
10117                                    END {$aggregatedByCalc}
10118                                AS DOUBLE) AS newObjective,
10119                                CAST(
10120                                    CASE
10121                                        WHEN bg.new_objective IS NOT NULL THEN bg.new_objective
10122                                        WHEN bgou.new_objective IS NOT NULL THEN bgou.new_objective
10123                                        WHEN bg1.new_objective IS NOT NULL THEN bg1.new_objective
10124                                        WHEN bgor.new_objective IS NOT NULL THEN bgor.new_objective
10125                                        WHEN bgde.new_objective IS NOT NULL THEN bgde.new_objective
10126                                        WHEN bg.new_objective IS NULL AND bg1.new_objective IS NULL THEN {$businessGoalsDefault->new_objective}
10127                                    END
10128                                AS DOUBLE) AS newObjectiveMonthly,
10129                                CAST(
10130                                    CASE
10131                                        WHEN bg.new_objective IS NOT NULL THEN bg.new_objective
10132                                        WHEN bgou.new_objective IS NOT NULL THEN bgou.new_objective
10133                                        WHEN bg1.new_objective IS NOT NULL THEN bg1.new_objective
10134                                        WHEN bgor.new_objective IS NOT NULL THEN bgor.new_objective
10135                                        WHEN bgde.new_objective IS NOT NULL THEN bgde.new_objective
10136                                        WHEN bg.new_objective IS NULL AND bg1.new_objective IS NULL THEN {$businessGoalsDefault->new_objective}
10137                                    END * 12
10138                                AS DOUBLE) AS newObjectiveYearly,
10139                                0 acceptanceObjective,
10140                                0 acceptanceObjectiveMonthly,
10141                                0 acceptanceObjectiveYearly
10142                                {$visitSubTableCols}
10143                            FROM
10144                            tbl_quotations q
10145                                LEFT JOIN tbl_sources s ON s.source_id = q.source_id
10146                                LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
10147                                LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
10148                                LEFT JOIN tbl_budget_type_groups btg ON btg.budget_type_group_id = bt.budget_type_group_id
10149                                LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
10150                                LEFT JOIN tbl_users u ON q.commercial = u.name
10151                                LEFT JOIN tbl_roles r ON u.role_id = r.role_id
10152                                LEFT JOIN tbl_business_goals bg ON bg.budget_type_group_id = btg.budget_type_group_id AND bg.user_id = u.id
10153                                LEFT JOIN tbl_business_goals bg1 ON bg1.budget_type_group_id = btg.budget_type_group_id AND bg1.role_id = r.role_id
10154                                LEFT JOIN tbl_business_goals bgde ON bgde.budget_type_group_id = btg.budget_type_group_id AND bgde.is_default = 1
10155                                LEFT JOIN business_goal_objective_users bgou ON bgou.user_id = u.id
10156                                LEFT JOIN business_goal_objective_roles bgor ON bgor.role_id = r.role_id
10157                            WHERE
10158                                q.budget_type_id != 7
10159                                AND q.budget_type_id IS NOT NULL
10160                                AND q.for_add = 0
10161                                AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
10162                                AND bt.include = 1
10163                                AND (q.commercial IS NOT NULL AND q.commercial != '')
10164                                {$where}
10165                                {$whereYear}
10166                            GROUP BY
10167                                {$groupBy}
10168                        ) q
10169                        GROUP BY
10170                            {$groupBy} WITH ROLLUP
10171
10172                        UNION ALL
10173
10174                        SELECT
10175                            q.year,
10176                            q.month,
10177                            q.week,
10178                            q.namedate,
10179                            q.issue_date,
10180                            SUM(q.acceptance_date) AS acceptance_date,
10181                            q.created_at,
10182                            q.commercial,
10183                            q.budget_type,
10184                            q.groupConcatIds,
10185                            q.issueObjective totalIssueObjective,
10186                            q.issueObjectiveMonthly totalIssueObjectiveMonthly,
10187                            q.issueObjectiveYearly totalIssueObjectiveYearly,
10188                            q.revenueIssue,
10189                            q.groupConcatCreatedAtIds,
10190                            q.totalIssueLessThan5,
10191                            q.groupConcatIdsIssueLessThan5,
10192                            GROUP_CONCAT(q.groupConcatAcceptanceIds) AS groupConcatAcceptanceIds,
10193                            CASE
10194                                WHEN q.is_amountAcceptance > 0 THEN
10195                                    SUM(q.revenueAcceptance / q.acceptanceObjective) * 100
10196                                ELSE
10197                                    SUM(q.acceptance_date / q.acceptanceObjective) * 100
10198                                END
10199                            AS totalAcceptanceObjective,
10200                            CASE
10201                                WHEN q.is_amountAcceptance > 0 THEN
10202                                    SUM(q.revenueAcceptance / q.acceptanceObjectiveMonthly) * 100
10203                                ELSE
10204                                    SUM(q.acceptance_date / q.acceptanceObjectiveMonthly) * 100
10205                                END
10206                            AS totalAcceptanceObjectiveMonthly,
10207                            CASE
10208                                WHEN q.is_amountAcceptance > 0 THEN
10209                                    SUM(q.revenueAcceptance / q.acceptanceObjectiveYearly) * 100
10210                                ELSE
10211                                    SUM(q.acceptance_date / q.acceptanceObjectiveYearly) * 100
10212                                END
10213                            AS totalAcceptanceObjectiveYearly,
10214                            SUM(q.revenueAcceptance) revenueAcceptance,
10215                            q.totalRejected,
10216                            q.groupConcatRejectedIds,
10217                            q.revenueRejected,
10218                            q.totalNew,
10219                            q.groupConcatNewIds,
10220                            q.newObjective totalNewObjective,
10221                            q.newObjectiveMonthly totalNewObjectiveMonthly,
10222                            q.newObjectiveYearly totalNewObjectiveYearly,
10223                            q.revenueNew,
10224                            q.totalVisit,
10225                            q.groupConcatVisitIds,
10226                            q.totalCall,
10227                            q.groupConcatCallIds,
10228                            q.priority,
10229                            SUM(q.is_amountIssue) AS is_amountIssue,
10230                            q.is_amountNew,
10231                            q.is_amountAcceptance,
10232                            SUM(q.issueObjective) AS issueObjective,
10233                            SUM(q.issueObjectiveMonthly) AS issueObjectiveMonthly,
10234                            SUM(q.issueObjectiveYearly) AS issueObjectiveYearly,
10235                            SUM(q.newObjective) AS newObjective,
10236                            SUM(q.newObjectiveMonthly) AS newObjectiveMonthly,
10237                            SUM(q.newObjectiveYearly) AS newObjectiveYearly,
10238                            SUM(q.acceptanceObjective) AS acceptanceObjective,
10239                            SUM(q.acceptanceObjectiveMonthly) AS acceptanceObjectiveMonthly,
10240                            SUM(q.acceptanceObjectiveYearly) AS acceptanceObjectiveYearly
10241                            {$visitSubMainTableCols}
10242                        FROM (
10243                            SELECT
10244                                YEAR(DATE_ADD(q.acceptance_date, INTERVAL - WEEKDAY(q.acceptance_date) DAY)) 'year',
10245                                MONTH(DATE_ADD(q.acceptance_date, INTERVAL - WEEKDAY(q.acceptance_date) DAY)) 'month',
10246                                WEEK(DATE_ADD(q.acceptance_date, INTERVAL - WEEKDAY(q.acceptance_date) DAY)) 'week',
10247                                DATE_FORMAT(DATE_ADD(q.acceptance_date, INTERVAL - WEEKDAY(q.acceptance_date) DAY), '%W, %M %e') namedate,
10248                                0 issue_date,
10249                                COUNT(CASE WHEN q.acceptance_date IS NOT NULL THEN 1 END) acceptance_date,
10250                                0 created_at,
10251                                q.commercial,
10252                                btg.name budget_type,
10253                                NULL groupConcatIds,
10254
10255                                0 revenueIssue,
10256                                NULL groupConcatCreatedAtIds,
10257                                0 totalIssueLessThan5,
10258                                NULL groupConcatIdsIssueLessThan5,
10259                                GROUP_CONCAT(CASE WHEN q.acceptance_date IS NOT NULL THEN q.id END) AS groupConcatAcceptanceIds,
10260
10261                                COALESCE(SUM(CASE WHEN q.acceptance_date IS NOT NULL THEN q.amount END), 0) AS revenueAcceptance,
10262                                0 totalRejected,
10263                                NULL groupConcatRejectedIds,
10264                                0 revenueRejected,
10265                                0 totalNew,
10266                                NULL groupConcatNewIds,
10267                                NULL totalNewObjective,
10268                                NULL totalNewObjectiveMonthly,
10269                                NULL totalNewObjectiveYearly,
10270                                0 revenueNew,
10271                                0 totalVisit,
10272                                NULL groupConcatVisitIds,
10273                                0 totalCall,
10274                                NULL groupConcatCallIds,
10275                                btg.priority,
10276                                0 is_amountIssue,
10277                                0 is_amountNew,
10278                                CAST(
10279                                    CASE
10280                                        WHEN bg.acceptance_objective IS NOT NULL THEN bg.is_amount
10281                                        WHEN bgou.acceptance_objective IS NOT NULL THEN bgou.is_amount
10282                                        WHEN bg1.acceptance_objective IS NOT NULL THEN bg1.is_amount
10283                                        WHEN bgor.acceptance_objective IS NOT NULL THEN bgor.is_amount
10284                                        WHEN bgde.acceptance_objective IS NOT NULL THEN bgde.is_amount
10285                                        WHEN bg.acceptance_objective IS NULL AND bg1.acceptance_objective IS NULL THEN {$businessGoalsDefault->is_amount}
10286                                    END
10287                                AS DOUBLE) AS is_amountAcceptance,
10288                                0 issueObjective,
10289                                0 issueObjectiveMonthly,
10290                                0 issueObjectiveYearly,
10291                                0 newObjective,
10292                                0 newObjectiveMonthly,
10293                                0 newObjectiveYearly,
10294                                CAST(
10295                                    CASE
10296                                        WHEN bg.acceptance_objective IS NOT NULL THEN bg.acceptance_objective
10297                                        WHEN bgou.acceptance_objective IS NOT NULL THEN bgou.acceptance_objective
10298                                        WHEN bg1.acceptance_objective IS NOT NULL THEN bg1.acceptance_objective
10299                                        WHEN bgor.acceptance_objective IS NOT NULL THEN bgor.acceptance_objective
10300                                        WHEN bgde.acceptance_objective IS NOT NULL THEN bgde.acceptance_objective
10301                                        WHEN bg.acceptance_objective IS NULL AND bg1.acceptance_objective IS NULL THEN {$businessGoalsDefault->acceptance_objective}
10302                                    END {$aggregatedByCalc}
10303                                AS DOUBLE) AS acceptanceObjective,
10304                                CAST(
10305                                    CASE
10306                                        WHEN bg.acceptance_objective IS NOT NULL THEN bg.acceptance_objective
10307                                        WHEN bgou.acceptance_objective IS NOT NULL THEN bgou.acceptance_objective
10308                                        WHEN bg1.acceptance_objective IS NOT NULL THEN bg1.acceptance_objective
10309                                        WHEN bgor.acceptance_objective IS NOT NULL THEN bgor.acceptance_objective
10310                                        WHEN bgde.acceptance_objective IS NOT NULL THEN bgde.acceptance_objective
10311                                        WHEN bg.acceptance_objective IS NULL AND bg1.acceptance_objective IS NULL THEN {$businessGoalsDefault->acceptance_objective}
10312                                    END
10313                                AS DOUBLE) AS acceptanceObjectiveMonthly,
10314                                CAST(
10315                                    CASE
10316                                        WHEN bg.acceptance_objective IS NOT NULL THEN bg.acceptance_objective
10317                                        WHEN bgou.acceptance_objective IS NOT NULL THEN bgou.acceptance_objective
10318                                        WHEN bg1.acceptance_objective IS NOT NULL THEN bg1.acceptance_objective
10319                                        WHEN bgor.acceptance_objective IS NOT NULL THEN bgor.acceptance_objective
10320                                        WHEN bgde.acceptance_objective IS NOT NULL THEN bgde.acceptance_objective
10321                                        WHEN bg.acceptance_objective IS NULL AND bg1.acceptance_objective IS NULL THEN {$businessGoalsDefault->acceptance_objective}
10322                                    END * 12
10323                                AS DOUBLE) AS acceptanceObjectiveYearly
10324                                {$visitSubTableCols}
10325                            FROM
10326                            tbl_quotations q
10327                                LEFT JOIN tbl_sources s ON s.source_id = q.source_id
10328                                LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
10329                                LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
10330                                LEFT JOIN tbl_budget_type_groups btg ON btg.budget_type_group_id = bt.budget_type_group_id
10331                                LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
10332                                LEFT JOIN tbl_users u ON q.commercial = u.name
10333                                LEFT JOIN tbl_roles r ON u.role_id = r.role_id
10334                                LEFT JOIN tbl_business_goals bg ON bg.budget_type_group_id = btg.budget_type_group_id AND bg.user_id = u.id
10335                                LEFT JOIN tbl_business_goals bg1 ON bg1.budget_type_group_id = btg.budget_type_group_id AND bg1.role_id = r.role_id
10336                                LEFT JOIN tbl_business_goals bgde ON bgde.budget_type_group_id = btg.budget_type_group_id AND bgde.is_default = 1
10337                                LEFT JOIN business_goal_objective_users bgou ON bgou.user_id = u.id
10338                                LEFT JOIN business_goal_objective_roles bgor ON bgor.role_id = r.role_id
10339                            WHERE
10340                                q.budget_type_id != 7
10341                                AND q.budget_type_id IS NOT NULL
10342                                AND q.for_add = 0
10343                                AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
10344                                AND bt.include = 1
10345                                AND (q.commercial IS NOT NULL AND q.commercial != '')
10346                                {$whereAcceptanceDate}
10347                                {$whereYear}
10348                            GROUP BY
10349                                {$groupBy}
10350                        ) q
10351                        GROUP BY
10352                            {$groupBy} WITH ROLLUP
10353
10354                        UNION ALL
10355
10356                        SELECT
10357                            q.year,
10358                            q.month,
10359                            q.week,
10360                            q.namedate,
10361                            q.issue_date,
10362                            q.acceptance_date,
10363                            SUM(q.created_at) AS created_at,
10364                            q.commercial,
10365                            q.budget_type,
10366                            q.groupConcatIds,
10367                            q.issueObjective totalIssueObjective,
10368                            q.issueObjectiveMonthly totalIssueObjectiveMonthly,
10369                            q.issueObjectiveYearly totalIssueObjectiveYearly,
10370                            q.revenueIssue,
10371                            GROUP_CONCAT(q.groupConcatCreatedAtIds) AS groupConcatCreatedAtIds,
10372                            SUM(q.totalIssueLessThan5) AS totalIssueLessThan5,
10373                            GROUP_CONCAT(q.groupConcatIdsIssueLessThan5) AS groupConcatIdsIssueLessThan5,
10374                            NULL groupConcatAcceptanceIds,
10375                            0 totalAcceptanceObjective,
10376                            0 totalAcceptanceObjectiveMonthly,
10377                            0 totalAcceptanceObjectiveYearly,
10378                            SUM(q.revenueAcceptance) revenueAcceptance,
10379                            q.totalRejected,
10380                            q.groupConcatRejectedIds,
10381                            q.revenueRejected,
10382                            q.totalNew,
10383                            q.groupConcatNewIds,
10384                            q.newObjective totalNewObjective,
10385                            q.newObjectiveMonthly totalNewObjectiveMonthly,
10386                            q.newObjectiveYearly totalNewObjectiveYearly,
10387                            q.revenueNew,
10388                            q.totalVisit,
10389                            q.groupConcatVisitIds,
10390                            q.totalCall,
10391                            q.groupConcatCallIds,
10392                            q.priority,
10393                            SUM(q.is_amountIssue) AS is_amountIssue,
10394                            q.is_amountNew,
10395                            q.is_amountAcceptance,
10396                            SUM(q.issueObjective) AS issueObjective,
10397                            SUM(q.issueObjectiveMonthly) AS issueObjectiveMonthly,
10398                            SUM(q.issueObjectiveYearly) AS issueObjectiveYearly,
10399                            SUM(q.newObjective) AS newObjective,
10400                            SUM(q.newObjectiveMonthly) AS newObjectiveMonthly,
10401                            SUM(q.newObjectiveYearly) AS newObjectiveYearly,
10402                            SUM(q.acceptanceObjective) AS acceptanceObjective,
10403                            SUM(q.acceptanceObjectiveMonthly) AS acceptanceObjectiveMonthly,
10404                            SUM(q.acceptanceObjectiveYearly) AS acceptanceObjectiveYearly
10405                            {$visitSubMainTableCols}
10406                        FROM (
10407                            SELECT
10408                                YEAR(DATE_ADD(q.created_at, INTERVAL - WEEKDAY(q.created_at) DAY)) 'year',
10409                                MONTH(DATE_ADD(q.created_at, INTERVAL - WEEKDAY(q.created_at) DAY)) 'month',
10410                                WEEK(DATE_ADD(q.created_at, INTERVAL - WEEKDAY(q.created_at) DAY)) 'week',
10411                                DATE_FORMAT(DATE_ADD(q.created_at, INTERVAL - WEEKDAY(q.created_at) DAY), '%W, %M %e') namedate,
10412                                0 issue_date,
10413                                0 acceptance_date,
10414                                COUNT(CASE WHEN q.created_at IS NOT NULL THEN 1 END) created_at,
10415                                q.commercial,
10416                                btg.name budget_type,
10417                                NULL groupConcatIds,
10418
10419                                0 revenueIssue,
10420                                GROUP_CONCAT(CASE WHEN q.created_at IS NOT NULL THEN q.id END) AS groupConcatCreatedAtIds,
10421                                COUNT(CASE WHEN ABS(DATEDIFF(q.created_at, q.issue_date)) < 5 THEN 1 END) AS totalIssueLessThan5,
10422                                GROUP_CONCAT(CASE WHEN ABS(DATEDIFF(q.created_at, q.issue_date)) < 5 THEN q.id END) AS groupConcatIdsIssueLessThan5,
10423                                NULL groupConcatAcceptanceIds,
10424
10425                                0 AS revenueAcceptance,
10426                                0 totalRejected,
10427                                NULL groupConcatRejectedIds,
10428                                0 revenueRejected,
10429                                0 totalNew,
10430                                NULL groupConcatNewIds,
10431                                NULL totalNewObjective,
10432                                NULL totalNewObjectiveMonthly,
10433                                NULL totalNewObjectiveYearly,
10434                                0 revenueNew,
10435                                0 totalVisit,
10436                                NULL groupConcatVisitIds,
10437                                0 totalCall,
10438                                NULL groupConcatCallIds,
10439                                btg.priority,
10440                                0 is_amountIssue,
10441                                0 is_amountNew,
10442                                0 is_amountAcceptance,
10443                                0 issueObjective,
10444                                0 issueObjectiveMonthly,
10445                                0 issueObjectiveYearly,
10446                                0 newObjective,
10447                                0 newObjectiveMonthly,
10448                                0 newObjectiveYearly,
10449                                0 acceptanceObjective,
10450                                0 acceptanceObjectiveMonthly,
10451                                0 acceptanceObjectiveYearly
10452                                {$visitSubTableCols}
10453                            FROM
10454                            tbl_quotations q
10455                                LEFT JOIN tbl_sources s ON s.source_id = q.source_id
10456                                LEFT JOIN tbl_budget_status bs ON bs.budget_status_id = q.budget_status_id
10457                                LEFT JOIN tbl_budget_types bt ON q.budget_type_id = bt.budget_type_id
10458                                LEFT JOIN tbl_budget_type_groups btg ON btg.budget_type_group_id = bt.budget_type_group_id
10459                                LEFT JOIN tbl_customer_types ct ON q.customer_type_id = ct.customer_type_id
10460                                LEFT JOIN tbl_users u ON q.commercial = u.name
10461                                LEFT JOIN tbl_roles r ON u.role_id = r.role_id
10462                            WHERE
10463                                q.budget_type_id != 7
10464                                AND q.budget_type_id IS NOT NULL
10465                                AND q.for_add = 0
10466                                AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
10467                                AND bt.include = 1
10468                                AND (q.commercial IS NOT NULL AND q.commercial != '')
10469                                {$whereCreatedAt}
10470                                {$whereYear}
10471                            GROUP BY
10472                                {$groupBy}
10473                        ) q
10474                        GROUP BY
10475                            {$groupBy} WITH ROLLUP
10476
10477                        UNION ALL
10478
10479                        SELECT
10480                            YEAR(DATE_ADD(q.visit_date, INTERVAL - WEEKDAY(q.visit_date) DAY)) YEAR,
10481                            MONTH(DATE_ADD(q.visit_date, INTERVAL - WEEKDAY(q.visit_date) DAY)) MONTH,
10482                            WEEK(DATE_ADD(q.visit_date, INTERVAL - WEEKDAY(q.visit_date) DAY)) WEEK,
10483                            DATE_FORMAT(DATE_ADD(visit_date, INTERVAL - WEEKDAY(q.visit_date) DAY), '%W, %M %e') namedate,
10484                            0 issue_date,
10485                            0 acceptance_date,
10486                            0 created_at,
10487                            commercial,
10488                            NULL budget_type,
10489                            NULL groupConcatIds,
10490                            NULL totalIssueObjective,
10491                            NULL totalIssueObjectiveMonthly,
10492                            NULL totalIssueObjectiveYearly,
10493                            0 revenueIssue,
10494                            NULL groupConcatCreatedAtIds,
10495                            0 totalIssueLessThan5,
10496                            NULL groupConcatIdsIssueLessThan5,
10497                            NULL groupConcatAcceptanceIds,
10498                            NULL totalAcceptanceObjective,NULL totalAcceptanceObjectiveMonthly,
10499                            NULL totalAcceptanceObjectiveYearly,
10500                            0 revenueAcceptance,
10501                            0 totalRejected,
10502                            NULL groupConcatRejectedIds,
10503                            0 revenueRejected,
10504                            0 totalNew,
10505                            NULL groupConcatNewIds,
10506                            NULL totalNewObjective,NULL totalNewObjectiveMonthly,
10507                            NULL totalNewObjectiveYearly,
10508                            0 revenueNew,
10509                            COUNT(CASE WHEN q.visit_date IS NOT NULL AND q.visit_call = 'Visita' THEN 1 END) totalVisit,
10510                            GROUP_CONCAT(CASE WHEN q.visit_date IS NOT NULL AND q.visit_call = 'Visita' THEN q.id END) AS groupConcatVisitIds,
10511                            COUNT(CASE WHEN q.visit_date IS NOT NULL AND q.visit_call = 'Llamada' THEN 1 END) totalCall,
10512                            GROUP_CONCAT(CASE WHEN q.visit_date IS NOT NULL AND q.visit_call = 'Llamada' THEN q.id END) AS groupConcatCallIds,
10513                            0 priority,
10514                            0 is_amountIssue,
10515                            0 is_amountNew,
10516                            0 is_amountAcceptance,
10517                            0 issueObjective,
10518                            0 issueObjectiveMonthly,
10519                            0 issueObjectiveYearly,
10520                            0 newObjective,
10521                            0 newObjectiveMontly,
10522                            0 newObjectiveYearly,
10523                            0 acceptanceObjective,
10524                            0 acceptanceObjectiveMonthly,
10525                            0 acceptanceObjectiveYearly
10526                            {$visitCols}
10527                        FROM
10528                            tbl_pipelines q
10529                        LEFT JOIN tbl_users u ON q.commercial = u.name
10530                        LEFT JOIN tbl_roles r ON u.role_id = r.role_id
10531                        LEFT JOIN tbl_visit_types v ON q.visit_type_id = v.visit_type_id
10532                        WHERE q.visit_date IS NOT NULL
10533                            {$whereVisit}
10534                        GROUP BY
10535                            {$groupBy}
10536                             WITH ROLLUP
10537                    ) q
10538                    WHERE
10539                        q.year NOT IN (2021, 2022)
10540                    GROUP BY
10541                        {$groupBy}
10542                    {$gO}";
10543
10544        $value = Cache::get(base64_encode($query));
10545
10546        if (! $value) {
10547            $result = DB::select($query);
10548
10549            $structureData = new StructureData;
10550            $result = $structureData->parse($result, $groupByFilter, $aggregatedBy);
10551
10552            Cache::put(base64_encode($query), $result, 600);
10553        } else {
10554            $result = $value;
10555        }
10556
10557        return response([
10558            'message' => 'OK',
10559            'data' => $result,
10560        ]);
10561
10562        // } catch (\Exception $e) {
10563        //     return response(['message' => 'KO', 'error' => $e->getMessage()]);
10564        // }
10565    }
10566
10567    function list_quotations_deleted($companyId): ResponseFactory|HttpResponse{
10568
10569        try {
10570
10571            $companyId = addslashes((string) $companyId);
10572            $where = "";
10573
10574            if ($companyId != 0) {
10575                $where = " a.company_id = {$companyId} ";
10576            } else {
10577                $where = " a.company_id IN ({$this->companyId})";
10578            }
10579
10580            $query = "SELECT
10581                        a.id,
10582                        a.quote_id,
10583                        a.internal_quote_id,
10584                        a.company_id,
10585                        b.name company_name,
10586                        a.client,
10587                        c.name client_type,
10588                        c.customer_type_id,
10589                        s.name segment,
10590                        s.segment_id,
10591                        a.request_date,
10592                        a.visit_date,
10593                        a.issue_date,
10594                        a.acceptance_date,
10595                        a.internal_quote_id,
10596                        DATE_FORMAT(a.request_date, '%d/%m/%Y') request_date_translate,
10597                        DATE_FORMAT(a.issue_date, '%d/%m/%Y') issue_date_translate,
10598                        DATE_FORMAT(a.acceptance_date, '%d/%m/%Y') acceptance_date_translate,
10599                        DATE_FORMAT(a.last_follow_up_date, '%d/%m/%Y') last_follow_up_date_translate,
10600                        DATE_FORMAT(a.created_at, '%d/%m/%Y') created_at_translate,
10601                        DATE_FORMAT(a.accepted_at, '%d/%m/%Y') accepted_at_translate,
10602                        a.phone_number,
10603                        a.email,
10604                        a.duration,
10605                        a.order_number,
10606                        d.name 'type',
10607                        d.budget_type_id,
10608                        e.name 'status',
10609                        e.budget_status_id,
10610                        f.name as source,
10611                        f.source_id,
10612                        a.amount,
10613                        g.name reason_for_not_following_up,
10614                        a.reason_for_not_following_up_id,
10615                        a.reason_for_rejection_id,
10616                        a.last_follow_up_date,
10617                        a.last_follow_up_comment,
10618                        CASE WHEN a.reason_for_rejection_id IS NULL THEN a.reason_for_rejection ELSE h.name END reason_for_rejection,
10619                        a.commercial,
10620                        a.user_commercial_by_g3w,
10621                        a.user_create_by_g3w,
10622                        a.created_by,
10623                        a.created_at,
10624                        a.updated_by,
10625                        a.updated_at,
10626                        a.total_sent,
10627                        a.has_attachment,
10628                        a.for_approval,
10629                        a.box_work_g3w,
10630                        a.people_assigned_to_the_job,
10631                        a.duration_of_job_in_days,
10632                        a.estimated_cost_of_materials,
10633                        a.budget_margin_enabled,
10634                        a.question_enabled,
10635                        a.cost_of_labor,
10636                        a.total_cost_of_job,
10637                        CASE WHEN a.budget_margin_enabled > 0 THEN a.invoice_margin ELSE NULL END invoice_margin,
10638                        CASE WHEN a.budget_margin_enabled > 0 THEN a.margin_for_the_company ELSE NULL END margin_for_the_company,
10639                        a.margin_on_invoice_per_day_per_worker,
10640                        a.revenue_per_date_per_worked,
10641                        a.commission_cost,
10642                        a.commission_pct,
10643                        a.gross_margin,
10644                        a.labor_percentage,
10645                        a.question_ids,
10646                        a.question_ids_no,
10647                        a.approved_at,
10648                        a.approved_by,
10649                        a.rejected_at,
10650                        a.rejected_by,
10651                        a.approved_at_v2,
10652                        a.approved_by_v2,
10653                        a.rejected_at_v2,
10654                        a.rejected_by_v2,
10655                        a.accepted_at,
10656                        a.accepted_by,
10657                        a.is_validated,
10658                        a.resource_id,
10659                        a.x_status,
10660                        a.likehood,
10661                        a.sync_import,
10662                        a.sync_import_edited,
10663                        a.g3w_warning,
10664                        a.g3w_warning_fields
10665                    FROM tbl_quotations_deleted a                    
10666                    LEFT JOIN tbl_companies b ON a.company_id = b.company_id
10667                    LEFT JOIN tbl_customer_types c ON a.customer_type_id = c.customer_type_id
10668                    LEFT JOIN tbl_segments s ON a.segment_id = s.segment_id
10669                    LEFT JOIN tbl_budget_types d ON a.budget_type_id = d.budget_type_id
10670                    LEFT JOIN tbl_budget_status e ON a.budget_status_id = e.budget_status_id
10671                    LEFT JOIN tbl_sources f ON a.source_id = f.source_id
10672                    LEFT JOIN tbl_reason_for_not_following_up g ON a.reason_for_not_following_up_id = g.reason_for_not_following_up_id
10673                    LEFT JOIN tbl_reason_for_rejection h ON a.reason_for_rejection_id = h.reason_for_rejection_id
10674                    WHERE {$where}
10675                    ORDER BY a.updated_at DESC";
10676
10677            $result = DB::select($query);
10678
10679            return response([
10680                'message' => 'OK',
10681                'data' => $result,
10682            ]);
10683        } catch (\Exception $e) {
10684            report(AppException::fromException($e, 'LIST_QUOTATIONS_DELETED_EXCEPTION'));
10685            return response(['message' => 'KO', 'error' => $e->getMessage()]);
10686        }
10687    }
10688
10689    function delete_sengrid($id): ResponseFactory|HttpResponse{
10690
10691        try {
10692
10693            $id = addslashes((string) $id);
10694
10695            $order = TblQuotations::where('id', $id)->first();
10696
10697            if ($order) {
10698                if ($order->x_message_id != null) {
10699                    TblSendgridWebhook::where('quotation_id', $id)->where('x_message_id', $order->x_message_id)->delete();
10700
10701                    TblQuotations::where('id', $id)->update(
10702                        [
10703                            'x_message_id' => null,
10704                            'x_status' => null
10705                        ]
10706                    );
10707
10708                    Cache::flush();
10709                }
10710            }
10711
10712            return response([
10713                'message' => 'OK',
10714            ]);
10715
10716        } catch (\Exception $e) {
10717            report(AppException::fromException($e, 'DELETE_SENDGRID_EXCEPTION'));
10718            return response(['message' => 'KO', 'error' => $e->getMessage()]);
10719        }
10720    }
10721
10722    function download_productivity_commercial(Request $request): ResponseFactory|HttpResponse{
10723
10724        try {
10725
10726            ini_set('max_execution_time', 123456);
10727
10728            $data = $request->all();
10729
10730            $result = $this->list_quotation_analytics_commercial_productivity($request);
10731            $result = $result->original['data'];
10732
10733            $spreadsheet = new Spreadsheet;
10734            $worksheet = new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'Inputs');
10735            $spreadsheet->addSheet($worksheet, 0);
10736            $col = range('A', 'Z');
10737
10738            for ($i = 0; $i < 26; $i++) {
10739                $worksheet->getColumnDimension($col[$i])->setAutoSize(true);
10740                if ($i != 1) {
10741                    $worksheet->getStyle($col[$i])
10742                        ->getAlignment()
10743                        ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
10744                }
10745            }
10746
10747            $l = 1;
10748            $worksheet->setCellValue('A'.$l, 'Años');
10749
10750            if ($data['group_by'] == 1) {
10751                $worksheet->setCellValue('B'.$l, 'Comercials');
10752                $worksheet->setCellValue('C'.$l, 'Meses');
10753                $worksheet->setCellValue('D'.$l, 'Semanas');
10754            } else {
10755                $worksheet->setCellValue('B'.$l, 'Meses');
10756                $worksheet->setCellValue('C'.$l, 'Semanas');
10757                $worksheet->setCellValue('D'.$l, 'Comercials');
10758            }
10759
10760            $worksheet->setCellValue('E'.$l, 'Categorías de presupuesto');
10761            $worksheet->setCellValue('F'.$l, 'Presupuestos emitidos (#)');
10762            $worksheet->setCellValue('G'.$l, 'Presupuestos emitidos (€)');
10763            $worksheet->setCellValue('H'.$l, 'Presupuestos emitidos (Objetivo)');
10764            $worksheet->setCellValue('I'.$l, 'Presupuestos aceptados (#)');
10765            $worksheet->setCellValue('J'.$l, 'Presupuestos aceptados (€)');
10766            $worksheet->setCellValue('K'.$l, 'Presupuestos aceptados (Objetivo)');
10767            $worksheet->setCellValue('L'.$l, 'Presupuestos rechazados (#)');
10768            $worksheet->setCellValue('M'.$l, 'Presupuestos rechazados (€)');
10769            $worksheet->setCellValue('N'.$l, 'Presupuestos emitidos a nuevos clientes (#)');
10770            $worksheet->setCellValue('O'.$l, 'Presupuestos emitidos a nuevos clientes (€)');
10771            $worksheet->setCellValue('P'.$l, 'Presupuestos emitidos a nuevos clientes (Objetivo)');
10772            $worksheet->setCellValue('Q'.$l, 'Venta (Llamada #)');
10773            $worksheet->setCellValue('R'.$l, 'Venta (Visita #)');
10774            $worksheet->setCellValue('S'.$l, 'Servicio (Llamada #)');
10775            $worksheet->setCellValue('T'.$l, 'Servicio (Visita #)');
10776
10777            $l++;
10778
10779            foreach ($result as $item) {
10780
10781                $worksheet->setCellValue('A'.$l, $item['year']);
10782                $worksheet->setCellValue('F'.$l, $item['totalIssue']);
10783                $worksheet->setCellValue('G'.$l, $item['revenueIssue']);
10784                $worksheet->setCellValue('H'.$l, '-');
10785
10786                $worksheet->setCellValue('I'.$l, $item['totalAcceptance']);
10787                $worksheet->setCellValue('J'.$l, $item['revenueAcceptance']);
10788                $worksheet->setCellValue('K'.$l, '-');
10789
10790                $worksheet->setCellValue('L'.$l, $item['totalRejected']);
10791                $worksheet->setCellValue('M'.$l, $item['revenueRejected']);
10792
10793                $worksheet->setCellValue('N'.$l, $item['totalNew']);
10794                $worksheet->setCellValue('O'.$l, $item['revenueNew']);
10795                $worksheet->setCellValue('P'.$l, '-');
10796
10797                $worksheet->setCellValue('Q'.$l, $item['totalLlamada1']);
10798                $worksheet->setCellValue('R'.$l, $item['totalVisita1']);
10799
10800                $worksheet->setCellValue('S'.$l, $item['totalLlamada2']);
10801                $worksheet->setCellValue('T'.$l, $item['totalVisita2']);
10802
10803                $l++;
10804
10805                if ($data['group_by'] == 1) {
10806
10807                    if (count($item['commercials']) > 0) {
10808
10809                        foreach ($item['commercials'] as $c) {
10810                            $worksheet->setCellValue('A'.$l, $item['year']);
10811
10812                            $worksheet->setCellValue('B'.$l, $c['commercial']);
10813                            $worksheet->setCellValue('F'.$l, $c['totalIssue']);
10814                            $worksheet->setCellValue('G'.$l, $c['revenueIssue']);
10815                            $worksheet->setCellValue('H'.$l, $c['totalIssueObjectiveYearly']);
10816
10817                            $worksheet->setCellValue('I'.$l, $c['totalAcceptance']);
10818                            $worksheet->setCellValue('J'.$l, $c['revenueAcceptance']);
10819                            $worksheet->setCellValue('K'.$l, $c['totalAcceptanceObjectiveYearly']);
10820
10821                            $worksheet->setCellValue('L'.$l, $c['totalRejected']);
10822                            $worksheet->setCellValue('M'.$l, $c['revenueRejected']);
10823
10824                            $worksheet->setCellValue('N'.$l, $c['totalNew']);
10825                            $worksheet->setCellValue('O'.$l, $c['revenueNew']);
10826                            $worksheet->setCellValue('P'.$l, $c['totalNewObjectiveYearly']);
10827
10828                            $worksheet->setCellValue('Q'.$l, $c['totalLlamada1']);
10829                            $worksheet->setCellValue('R'.$l, $c['totalVisita1']);
10830
10831                            $worksheet->setCellValue('S'.$l, $c['totalLlamada2']);
10832                            $worksheet->setCellValue('T'.$l, $c['totalVisita2']);
10833                            $l++;
10834
10835                            if (isset($c['budget_types']) && count($c['budget_types']) > 0) {
10836                                foreach ($c['budget_types'] as $b) {
10837                                    $worksheet->setCellValue('A'.$l, $item['year']);
10838                                    $worksheet->setCellValue('B'.$l, $c['commercial']);
10839
10840                                    $worksheet->setCellValue('E'.$l, $b['name']);
10841                                    $worksheet->setCellValue('F'.$l, $b['totalIssue']);
10842                                    $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
10843                                    $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
10844
10845                                    $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
10846                                    $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
10847                                    $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
10848
10849                                    $worksheet->setCellValue('L'.$l, $b['totalRejected']);
10850                                    $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
10851
10852                                    $worksheet->setCellValue('N'.$l, $b['totalNew']);
10853                                    $worksheet->setCellValue('O'.$l, $b['revenueNew']);
10854                                    $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
10855
10856                                    $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
10857                                    $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
10858
10859                                    $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
10860                                    $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
10861                                    $l++;
10862                                }
10863                            }
10864
10865                            if (isset($c['months']) && count($c['months']) > 0) {
10866                                foreach ($c['months'] as $m) {
10867                                    $worksheet->setCellValue('A'.$l, $item['year']);
10868                                    $worksheet->setCellValue('B'.$l, $c['commercial']);
10869
10870                                    $worksheet->setCellValue('C'.$l, $m['month']);
10871                                    $worksheet->setCellValue('F'.$l, $m['totalIssue']);
10872                                    $worksheet->setCellValue('G'.$l, $m['revenueIssue']);
10873                                    $worksheet->setCellValue('H'.$l, $m['totalIssueObjectiveMonthly']);
10874
10875                                    $worksheet->setCellValue('I'.$l, $m['totalAcceptance']);
10876                                    $worksheet->setCellValue('J'.$l, $m['revenueAcceptance']);
10877                                    $worksheet->setCellValue('K'.$l, $m['totalAcceptanceObjectiveMonthly']);
10878
10879                                    $worksheet->setCellValue('L'.$l, $m['totalRejected']);
10880                                    $worksheet->setCellValue('M'.$l, $m['revenueRejected']);
10881
10882                                    $worksheet->setCellValue('N'.$l, $m['totalNew']);
10883                                    $worksheet->setCellValue('O'.$l, $m['revenueNew']);
10884                                    $worksheet->setCellValue('P'.$l, $m['totalNewObjectiveMonthly']);
10885
10886                                    $worksheet->setCellValue('Q'.$l, $m['totalLlamada1']);
10887                                    $worksheet->setCellValue('R'.$l, $m['totalVisita1']);
10888
10889                                    $worksheet->setCellValue('S'.$l, $m['totalLlamada2']);
10890                                    $worksheet->setCellValue('T'.$l, $m['totalVisita2']);
10891                                    $l++;
10892
10893                                    if (isset($m['weeks']) && count(@$m['weeks']) > 0 && count(@$m['weeks']) != 1) {
10894                                        foreach ($m['weeks'] as $w) {
10895                                            $worksheet->setCellValue('A'.$l, $item['year']);
10896                                            $worksheet->setCellValue('B'.$l, $c['commercial']);
10897                                            $worksheet->setCellValue('C'.$l, $m['month']);
10898
10899                                            $worksheet->setCellValue('D'.$l, $w['created_at']);
10900                                            $worksheet->setCellValue('F'.$l, $w['totalIssue']);
10901                                            $worksheet->setCellValue('G'.$l, $w['revenueIssue']);
10902                                            $worksheet->setCellValue('H'.$l, $w['totalIssueObjective']);
10903
10904                                            $worksheet->setCellValue('I'.$l, $w['totalAcceptance']);
10905                                            $worksheet->setCellValue('J'.$l, $w['revenueAcceptance']);
10906                                            $worksheet->setCellValue('K'.$l, $w['totalAcceptanceObjective']);
10907
10908                                            $worksheet->setCellValue('L'.$l, $w['totalRejected']);
10909                                            $worksheet->setCellValue('M'.$l, $w['revenueRejected']);
10910
10911                                            $worksheet->setCellValue('N'.$l, $w['totalNew']);
10912                                            $worksheet->setCellValue('O'.$l, $w['revenueNew']);
10913                                            $worksheet->setCellValue('P'.$l, $w['totalNewObjective']);
10914
10915                                            $worksheet->setCellValue('Q'.$l, $w['totalLlamada1']);
10916                                            $worksheet->setCellValue('R'.$l, $w['totalVisita1']);
10917
10918                                            $worksheet->setCellValue('S'.$l, $w['totalLlamada2']);
10919                                            $worksheet->setCellValue('T'.$l, $w['totalVisita2']);
10920                                            $l++;
10921
10922                                            if (count($w['budget_types']) > 0) {
10923                                                foreach ($w['budget_types'] as $b) {
10924                                                    $worksheet->setCellValue('A'.$l, $item['year']);
10925                                                    $worksheet->setCellValue('B'.$l, $c['commercial']);
10926                                                    $worksheet->setCellValue('C'.$l, $m['month']);
10927
10928                                                    $worksheet->setCellValue('E'.$l, $b['name']);
10929                                                    $worksheet->setCellValue('F'.$l, $b['totalIssue']);
10930                                                    $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
10931                                                    $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
10932
10933                                                    $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
10934                                                    $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
10935                                                    $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
10936
10937                                                    $worksheet->setCellValue('L'.$l, $b['totalRejected']);
10938                                                    $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
10939
10940                                                    $worksheet->setCellValue('N'.$l, $b['totalNew']);
10941                                                    $worksheet->setCellValue('O'.$l, $b['revenueNew']);
10942                                                    $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
10943
10944                                                    $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
10945                                                    $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
10946
10947                                                    $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
10948                                                    $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
10949                                                    $l++;
10950                                                }
10951                                            }
10952                                        }
10953                                    } elseif (isset($m['weeks']) && count(@$m['weeks']) == 1) {
10954                                        foreach ($m['weeks'] as $w) {
10955                                            if (count($w['budget_types']) > 0) {
10956                                                foreach ($w['budget_types'] as $b) {
10957                                                    $worksheet->setCellValue('A'.$l, $item['year']);
10958                                                    $worksheet->setCellValue('B'.$l, $c['commercial']);
10959                                                    $worksheet->setCellValue('C'.$l, $m['month']);
10960
10961                                                    $worksheet->setCellValue('E'.$l, $b['name']);
10962                                                    $worksheet->setCellValue('F'.$l, $b['totalIssue']);
10963                                                    $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
10964                                                    $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
10965
10966                                                    $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
10967                                                    $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
10968                                                    $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
10969
10970                                                    $worksheet->setCellValue('L'.$l, $b['totalRejected']);
10971                                                    $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
10972
10973                                                    $worksheet->setCellValue('N'.$l, $b['totalNew']);
10974                                                    $worksheet->setCellValue('O'.$l, $b['revenueNew']);
10975                                                    $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
10976
10977                                                    $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
10978                                                    $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
10979
10980                                                    $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
10981                                                    $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
10982                                                    $l++;
10983                                                }
10984                                            }
10985                                        }
10986
10987                                    }
10988                                }
10989                            }
10990                        }
10991                    }
10992                } else {
10993
10994                    if (isset($item['months']) && count($item['months']) > 0) {
10995                        foreach ($item['months'] as $m) {
10996                            $worksheet->setCellValue('A'.$l, $item['year']);
10997                            $worksheet->setCellValue('B'.$l, $m['month']);
10998                            $worksheet->setCellValue('F'.$l, $m['totalIssue']);
10999                            $worksheet->setCellValue('G'.$l, $m['revenueIssue']);
11000                            $worksheet->setCellValue('H'.$l, '-');
11001
11002                            $worksheet->setCellValue('I'.$l, $m['totalAcceptance']);
11003                            $worksheet->setCellValue('J'.$l, $m['revenueAcceptance']);
11004                            $worksheet->setCellValue('K'.$l, '-');
11005
11006                            $worksheet->setCellValue('L'.$l, $m['totalRejected']);
11007                            $worksheet->setCellValue('M'.$l, $m['revenueRejected']);
11008
11009                            $worksheet->setCellValue('N'.$l, $m['totalNew']);
11010                            $worksheet->setCellValue('O'.$l, $m['revenueNew']);
11011                            $worksheet->setCellValue('P'.$l, '-');
11012
11013                            $worksheet->setCellValue('Q'.$l, $m['totalLlamada1']);
11014                            $worksheet->setCellValue('R'.$l, $m['totalVisita1']);
11015
11016                            $worksheet->setCellValue('S'.$l, $m['totalLlamada2']);
11017                            $worksheet->setCellValue('T'.$l, $m['totalVisita2']);
11018                            $l++;
11019
11020                            if (isset($m['weeks']) && count(@$m['weeks']) > 0) {
11021                                foreach ($m['weeks'] as $w) {
11022                                    $worksheet->setCellValue('A'.$l, $item['year']);
11023                                    $worksheet->setCellValue('B'.$l, $m['month']);
11024
11025                                    $worksheet->setCellValue('C'.$l, $w['created_at']);
11026                                    $worksheet->setCellValue('F'.$l, $w['totalIssue']);
11027                                    $worksheet->setCellValue('G'.$l, $w['revenueIssue']);
11028                                    $worksheet->setCellValue('H'.$l, '-');
11029
11030                                    $worksheet->setCellValue('I'.$l, $w['totalAcceptance']);
11031                                    $worksheet->setCellValue('J'.$l, $w['revenueAcceptance']);
11032                                    $worksheet->setCellValue('K'.$l, '-');
11033
11034                                    $worksheet->setCellValue('L'.$l, $w['totalRejected']);
11035                                    $worksheet->setCellValue('M'.$l, $w['revenueRejected']);
11036
11037                                    $worksheet->setCellValue('N'.$l, $w['totalNew']);
11038                                    $worksheet->setCellValue('O'.$l, $w['revenueNew']);
11039                                    $worksheet->setCellValue('P'.$l, '-');
11040
11041                                    $worksheet->setCellValue('Q'.$l, $w['totalLlamada1']);
11042                                    $worksheet->setCellValue('R'.$l, $w['totalVisita1']);
11043
11044                                    $worksheet->setCellValue('S'.$l, $w['totalLlamada2']);
11045                                    $worksheet->setCellValue('T'.$l, $w['totalVisita2']);
11046                                    $l++;
11047
11048                                    if (count($w['commercials']) > 0) {
11049                                        foreach ($w['commercials'] as $c) {
11050                                            $worksheet->setCellValue('A'.$l, $item['year']);
11051                                            $worksheet->setCellValue('B'.$l, $m['month']);
11052
11053                                            $worksheet->setCellValue('D'.$l, $c['commercial']);
11054                                            $worksheet->setCellValue('F'.$l, $c['totalIssue']);
11055                                            $worksheet->setCellValue('G'.$l, $c['revenueIssue']);
11056                                            $worksheet->setCellValue('H'.$l, $c['totalIssueObjective']);
11057
11058                                            $worksheet->setCellValue('I'.$l, $c['totalAcceptance']);
11059                                            $worksheet->setCellValue('J'.$l, $c['revenueAcceptance']);
11060                                            $worksheet->setCellValue('K'.$l, $c['totalAcceptanceObjective']);
11061
11062                                            $worksheet->setCellValue('L'.$l, $c['totalRejected']);
11063                                            $worksheet->setCellValue('M'.$l, $c['revenueRejected']);
11064
11065                                            $worksheet->setCellValue('N'.$l, $c['totalNew']);
11066                                            $worksheet->setCellValue('O'.$l, $c['revenueNew']);
11067                                            $worksheet->setCellValue('P'.$l, $c['totalNewObjective']);
11068
11069                                            $worksheet->setCellValue('Q'.$l, $c['totalLlamada1']);
11070                                            $worksheet->setCellValue('R'.$l, $c['totalVisita1']);
11071
11072                                            $worksheet->setCellValue('S'.$l, $c['totalLlamada2']);
11073                                            $worksheet->setCellValue('T'.$l, $c['totalVisita2']);
11074                                            $l++;
11075
11076                                            if (count($c['budget_types']) > 0) {
11077                                                foreach ($c['budget_types'] as $b) {
11078                                                    $worksheet->setCellValue('A'.$l, $item['year']);
11079                                                    $worksheet->setCellValue('B'.$l, $m['month']);
11080                                                    $worksheet->setCellValue('D'.$l, $c['commercial']);
11081
11082                                                    $worksheet->setCellValue('E'.$l, $b['name']);
11083                                                    $worksheet->setCellValue('F'.$l, $b['totalIssue']);
11084                                                    $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
11085                                                    $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
11086
11087                                                    $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
11088                                                    $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
11089                                                    $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
11090
11091                                                    $worksheet->setCellValue('L'.$l, $b['totalRejected']);
11092                                                    $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
11093
11094                                                    $worksheet->setCellValue('N'.$l, $b['totalNew']);
11095                                                    $worksheet->setCellValue('O'.$l, $b['revenueNew']);
11096                                                    $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
11097
11098                                                    $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
11099                                                    $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
11100
11101                                                    $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
11102                                                    $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
11103                                                    $l++;
11104                                                }
11105                                            }
11106                                        }
11107                                    }
11108                                }
11109                            }
11110
11111                            if (count($m['commercials']) > 0) {
11112                                foreach ($m['commercials'] as $c) {
11113                                    $worksheet->setCellValue('A'.$l, $item['year']);
11114                                    $worksheet->setCellValue('B'.$l, $m['month']);
11115
11116                                    $worksheet->setCellValue('D'.$l, $c['commercial']);
11117                                    $worksheet->setCellValue('F'.$l, $c['totalIssue']);
11118                                    $worksheet->setCellValue('G'.$l, $c['revenueIssue']);
11119                                    $worksheet->setCellValue('H'.$l, $c['totalIssueObjectiveYearly']);
11120
11121                                    $worksheet->setCellValue('I'.$l, $c['totalAcceptance']);
11122                                    $worksheet->setCellValue('J'.$l, $c['revenueAcceptance']);
11123                                    $worksheet->setCellValue('K'.$l, $c['totalAcceptanceObjectiveYearly']);
11124
11125                                    $worksheet->setCellValue('L'.$l, $c['totalRejected']);
11126                                    $worksheet->setCellValue('M'.$l, $c['revenueRejected']);
11127
11128                                    $worksheet->setCellValue('N'.$l, $c['totalNew']);
11129                                    $worksheet->setCellValue('O'.$l, $c['revenueNew']);
11130                                    $worksheet->setCellValue('P'.$l, $c['totalNewObjectiveYearly']);
11131
11132                                    $worksheet->setCellValue('Q'.$l, $c['totalLlamada1']);
11133                                    $worksheet->setCellValue('R'.$l, $c['totalVisita1']);
11134
11135                                    $worksheet->setCellValue('S'.$l, $c['totalLlamada2']);
11136                                    $worksheet->setCellValue('T'.$l, $c['totalVisita2']);
11137                                    $l++;
11138
11139                                    if (count($c['budget_types']) > 0) {
11140                                        foreach ($c['budget_types'] as $b) {
11141                                            $worksheet->setCellValue('A'.$l, $item['year']);
11142                                            $worksheet->setCellValue('B'.$l, $m['month']);
11143                                            $worksheet->setCellValue('D'.$l, $c['commercial']);
11144
11145                                            $worksheet->setCellValue('E'.$l, $b['name']);
11146                                            $worksheet->setCellValue('F'.$l, $b['totalIssue']);
11147                                            $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
11148                                            $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
11149
11150                                            $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
11151                                            $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
11152                                            $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
11153
11154                                            $worksheet->setCellValue('L'.$l, $b['totalRejected']);
11155                                            $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
11156
11157                                            $worksheet->setCellValue('N'.$l, $b['totalNew']);
11158                                            $worksheet->setCellValue('O'.$l, $b['revenueNew']);
11159                                            $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
11160
11161                                            $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
11162                                            $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
11163
11164                                            $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
11165                                            $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
11166                                            $l++;
11167                                        }
11168                                    }
11169                                }
11170                            }
11171                        }
11172                    }
11173
11174                    if (count($item['commercials']) > 0) {
11175                        foreach ($item['commercials'] as $c) {
11176                            $worksheet->setCellValue('A'.$l, $item['year']);
11177
11178                            $worksheet->setCellValue('D'.$l, $c['commercial']);
11179                            $worksheet->setCellValue('F'.$l, $c['totalIssue']);
11180                            $worksheet->setCellValue('G'.$l, $c['revenueIssue']);
11181                            $worksheet->setCellValue('H'.$l, $c['totalIssueObjectiveYearly']);
11182
11183                            $worksheet->setCellValue('I'.$l, $c['totalAcceptance']);
11184                            $worksheet->setCellValue('J'.$l, $c['revenueAcceptance']);
11185                            $worksheet->setCellValue('K'.$l, $c['totalAcceptanceObjectiveYearly']);
11186
11187                            $worksheet->setCellValue('L'.$l, $c['totalRejected']);
11188                            $worksheet->setCellValue('M'.$l, $c['revenueRejected']);
11189
11190                            $worksheet->setCellValue('N'.$l, $c['totalNew']);
11191                            $worksheet->setCellValue('O'.$l, $c['revenueNew']);
11192                            $worksheet->setCellValue('P'.$l, $c['totalNewObjectiveYearly']);
11193
11194                            $worksheet->setCellValue('Q'.$l, $c['totalLlamada1']);
11195                            $worksheet->setCellValue('R'.$l, $c['totalVisita1']);
11196
11197                            $worksheet->setCellValue('S'.$l, $c['totalLlamada2']);
11198                            $worksheet->setCellValue('T'.$l, $c['totalVisita2']);
11199                            $l++;
11200
11201                            if (count($c['budget_types']) > 0) {
11202                                foreach ($c['budget_types'] as $b) {
11203                                    $worksheet->setCellValue('A'.$l, $item['year']);
11204                                    $worksheet->setCellValue('D'.$l, $c['commercial']);
11205
11206                                    $worksheet->setCellValue('E'.$l, $b['name']);
11207                                    $worksheet->setCellValue('F'.$l, $b['totalIssue']);
11208                                    $worksheet->setCellValue('G'.$l, $b['revenueIssue']);
11209                                    $worksheet->setCellValue('H'.$l, $b['totalIssueObjective']);
11210
11211                                    $worksheet->setCellValue('I'.$l, $b['totalAcceptance']);
11212                                    $worksheet->setCellValue('J'.$l, $b['revenueAcceptance']);
11213                                    $worksheet->setCellValue('K'.$l, $b['totalAcceptanceObjective']);
11214
11215                                    $worksheet->setCellValue('L'.$l, $b['totalRejected']);
11216                                    $worksheet->setCellValue('M'.$l, $b['revenueRejected']);
11217
11218                                    $worksheet->setCellValue('N'.$l, $b['totalNew']);
11219                                    $worksheet->setCellValue('O'.$l, $b['revenueNew']);
11220                                    $worksheet->setCellValue('P'.$l, $b['totalNewObjective']);
11221
11222                                    $worksheet->setCellValue('Q'.$l, $b['totalLlamada1']);
11223                                    $worksheet->setCellValue('R'.$l, $b['totalVisita1']);
11224
11225                                    $worksheet->setCellValue('S'.$l, $b['totalLlamada2']);
11226                                    $worksheet->setCellValue('T'.$l, $b['totalVisita2']);
11227                                    $l++;
11228                                }
11229                            }
11230                        }
11231                    }
11232                }
11233
11234                $l++;
11235            }
11236
11237            if ($data['group_by'] == 1) {
11238                if ($data['aggregated_by'] == 3) {
11239                    $worksheet->removeColumn('C');
11240                    $worksheet->removeColumn('C');
11241                } elseif ($data['aggregated_by'] == 2) {
11242                    $worksheet->removeColumn('D');
11243                }
11244            } else {
11245                if ($data['aggregated_by'] == 3) {
11246                    $worksheet->removeColumn('B');
11247                    $worksheet->removeColumn('B');
11248                } elseif ($data['aggregated_by'] == 2) {
11249                    $worksheet->removeColumn('C');
11250                }
11251            }
11252
11253            $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
11254            ob_start();
11255            $writer->save('php://output');
11256            $file = ob_get_contents();
11257            ob_end_clean();
11258
11259            return response($file);
11260
11261        } catch (\Exception $e) {
11262            report(AppException::fromException($e, 'DOWNLOAD_PRODUCTIVITY_COMMERCIAL_EXCEPTION'));
11263            return response(['message' => 'KO', 'error' => $e->getMessage()]);
11264        }
11265
11266    }
11267
11268    function update_commercial_numbers($companyId): void
11269    {
11270        $phpBinary = '/usr/bin/php';
11271
11272        $artisanPath = escapeshellarg(base_path('artisan'));
11273
11274        $command = sprintf(
11275            '%s %s update:commercial-numbers %s > /dev/null 2>&1 &',
11276            $phpBinary,
11277            $artisanPath,
11278            $companyId
11279        );
11280
11281        exec($command, $output, $returnVar);
11282    }
11283
11284    function list_quotation_analytics_by_service_type(Request $request): ResponseFactory|HttpResponse{
11285
11286        try {
11287
11288            $data = $request->all();
11289            $companyId = addslashes((string) $data['company_id']);
11290            $where  = "";
11291
11292            if ($companyId != 0) {
11293                $where .= " AND c.company_id = {$companyId} ";
11294            } else {
11295                $where .= " AND c.company_id IN ({$this->companyId}";
11296            }
11297
11298            $col = '1';
11299
11300            if (isset($data['data_to_display']) && $data['data_to_display'] != null) {
11301                if ($data['data_to_display'] == 1) {
11302                    $col = '1';
11303                }
11304
11305                if ($data['data_to_display'] == 2) {
11306                    $col = 'q.amount';
11307                }
11308            }
11309
11310            if (isset($data['approvals'])) {
11311                $approvals = addslashes($data['approvals']);
11312
11313                if ($approvals == 2) {
11314                    $where .= ' AND q.for_approval != 1 ';
11315                }
11316
11317                if ($approvals == 3) {
11318                    $where .= ' AND q.for_approval > 0 ';
11319                }
11320
11321                if ($approvals == 4) {
11322                    $where .= ' AND q.approved_by IS NOT NULL';
11323                }
11324
11325                if ($approvals == 5) {
11326                    $where .= ' AND q.for_approval = 2 AND q.approved_by IS NULL';
11327                }
11328
11329                if ($approvals == 6) {
11330                    $where .= ' AND q.approved_by IS NOT NULL AND q.approved_by_v2 IS NOT NULL';
11331                }
11332
11333                if ($approvals == 7) {
11334                    $where .= ' AND q.for_approval = 3 AND q.approved_by IS NOT NULL AND q.approved_by_v2 IS NULL';
11335                }
11336
11337                if ($approvals == 8) {
11338                    $where .= ' AND q.for_approval = 3 AND q.approved_by IS NULL AND q.approved_by_v2 IS NOT NULL';
11339                }
11340
11341                if ($approvals == 9) {
11342                    $where .= ' AND q.for_approval = 3 AND q.approved_by IS NULL AND q.approved_by_v2 IS NULL';
11343                }
11344            }
11345
11346            if (isset($data['role_id']) && $data['role_id'] != null) {
11347                $roleIds = implode(',', $data['role_id']);
11348                if (count($data['role_id']) > 0) {
11349                    $where .= " AND u.role_id IN ({$roleIds}, 999999999)";
11350                }
11351            }
11352
11353            if (isset($data['client_type']) && $data['client_type'] != null) {
11354                $where .= " AND q.customer_type_id = {$data['client_type']}";
11355            }
11356
11357            $weekDay = '- WEEKDAY(q.date)';
11358
11359            if(isset($data['start_of_the_week']) && $data['start_of_the_week'] != null){
11360                $weekDay = match ($data['start_of_the_week']) {
11361                    0 => "- WEEKDAY(q.date)",
11362                    1 => "(1 - WEEKDAY(q.date))",
11363                    2 => "(2 - WEEKDAY(q.date))",
11364                    3 => "(3 - WEEKDAY(q.date))",
11365                    4 => "(4 - WEEKDAY(q.date))",
11366                    default => "- WEEKDAY(q.date)",
11367                };
11368            }
11369
11370            $whereDates = '';
11371
11372            if ((isset($data['start_date']) && $data['start_date'] != null) && (isset($data['end_date']) && $data['end_date'] != null)) {
11373                $whereDates = " AND q.date BETWEEN '{$data['start_date']}' AND '{$data['end_date']}";
11374            }
11375
11376            $query = "SELECT
11377                        q.region,
11378                        YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) 'year',
11379                        LPAD(MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)), 2, 0) 'month',
11380                        LPAD(WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)), 2, 0) 'week',
11381                        DATE_FORMAT(DATE_ADD(q.date, INTERVAL {$weekDay} DAY), '%W, %M %e') namedate,
11382
11383                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' THEN q.id END) AS groupConcatIdsTotalEnviado,
11384                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END), 0) AS totalIssueEnviado,
11385
11386                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN q.id END) AS groupConcatIdsMantenimientoEnviado,
11387                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END), 0) totalMantenimientoEnviado,
11388                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalMantenimientoPercentageEnviado,
11389
11390                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN q.id END) AS groupConcatIdsCorrectivosEnviado,
11391                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END), 0) totalCorrectivosEnviado,
11392                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalCorrectivosPercentageEnviado,
11393
11394                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN q.id END) AS groupConcatIdsObrasEnviado,
11395                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END), 0) totalObrasEnviado,
11396                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalObrasPercentageEnviado,
11397
11398                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN q.id END) AS groupConcatIdsOtrosEnviado,
11399                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END), 0) totalOtrosEnviado,
11400                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalOtrosPercentageEnviado,
11401
11402                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN q.id END) AS groupConcatIdsOtrosEnviado,
11403                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END), 0) totalOtrosEnviado,
11404                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalOtrosPercentageEnviado,
11405
11406                        CASE
11407                            WHEN SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11408                            ELSE SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.s1 ELSE 0 END)
11409                                / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END)
11410                        END AS weightedAverageMarginForTheCompanyEnviado,
11411                        CASE
11412                            WHEN SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11413                            ELSE SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.s2 ELSE 0 END)
11414                                / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END)
11415                        END AS weightedAverageInvoiceEnviado,
11416
11417                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' THEN q.id END) AS groupConcatIdsTotalAceptado,
11418                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END), 0) AS totalIssueAceptado,
11419
11420                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN q.id END) AS groupConcatIdsMantenimientoAceptado,
11421                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END), 0) totalMantenimientoAceptado,
11422                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalMantenimientoPercentageAceptado,
11423
11424                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN q.id END) AS groupConcatIdsCorrectivosAceptado,
11425                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END), 0) totalCorrectivosAceptado,
11426                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalCorrectivosPercentageAceptado,
11427
11428                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN q.id END) AS groupConcatIdsObrasAceptado,
11429                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END), 0) totalObrasAceptado,
11430                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalObrasPercentageAceptado,
11431
11432                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN q.id END) AS groupConcatIdsOtrosAceptado,
11433                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END), 0) totalOtrosAceptado,
11434                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalOtrosPercentageAceptado,
11435
11436                        CASE
11437                            WHEN SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11438                            ELSE SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.s1 ELSE 0 END)
11439                                / SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END)
11440                        END AS weightedAverageMarginForTheCompanyAceptado,
11441                        CASE
11442                            WHEN SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11443                            ELSE SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.s2 ELSE 0 END)
11444                                / SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END)
11445                        END AS weightedAverageInvoiceAceptado,
11446
11447                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' THEN 1 END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalIssuePercentage,
11448                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END) * 100, 0) totalMantenimientoPercentage,
11449                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END) * 100, 0) totalCorrectivosPercentage,
11450                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END) * 100, 0) totalObrasPercentage,
11451                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) * 100 , 0) totalOtrosPercentage,
11452
11453                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL THEN {$col} END) * 100, 0) totalIssuePercentageLead,
11454                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 3 THEN {$col} END) * 100, 0) totalMantenimientoPercentageLead,
11455                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 5 THEN {$col} END) * 100, 0) totalCorrectivosPercentageLead,
11456                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 4 THEN {$col} END) * 100, 0) totalObrasPercentageLead,
11457                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) * 100 , 0) totalOtrosPercentageLead
11458                    FROM
11459                    (
11460                        SELECT
11461                            c.region,
11462                            q.issue_date AS DATE,
11463                            'issue' AS date_type,
11464                            q.acceptance_date,
11465                            q.issue_date,
11466                            q.id,
11467                            q.margin_for_the_company,
11468                            CASE
11469                                WHEN bt.budget_type_group_id = 4
11470                                AND q.budget_margin_enabled > 0
11471                                AND q.budget_margin_enabled IS NOT NULL
11472                                AND q.margin_for_the_company <> 0
11473                            THEN q.margin_for_the_company * q.amount
11474                            END s1,
11475                            q.invoice_margin,
11476                            CASE
11477                                WHEN bt.budget_type_group_id = 4
11478                                AND q.budget_margin_enabled > 0
11479                                AND q.budget_margin_enabled IS NOT NULL
11480                                AND q.invoice_margin <> 0
11481                            THEN q.invoice_margin * q.amount
11482                            END s2,
11483                            q.budget_type_id,
11484                            q.budget_status_id,
11485                            q.amount,
11486                            bt.budget_type_group_id,
11487                            q.budget_margin_enabled
11488                        FROM
11489                            tbl_quotations q
11490                        JOIN tbl_companies c
11491                            ON c.company_id = q.company_id
11492                        LEFT JOIN tbl_budget_types bt
11493                            ON q.budget_type_id = bt.budget_type_id
11494                        LEFT JOIN tbl_users u
11495                            ON u.name = q.created_by
11496                        LEFT JOIN tbl_roles r
11497                            ON r.role_id = u.role_id
11498                        WHERE
11499                            q.issue_date IS NOT NULL
11500                            AND q.for_add = 0
11501                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
11502                            AND (q.commercial IS NOT NULL AND q.commercial != '')
11503                            AND q.budget_type_id != 7
11504                            AND q.budget_type_id IS NOT NULL
11505                            {$where}
11506
11507                        UNION ALL
11508
11509                        SELECT
11510                            c.region,
11511                            q.acceptance_date AS DATE,
11512                            'acceptance' AS date_type,
11513                            q.acceptance_date,
11514                            q.issue_date,
11515                            q.id,
11516                            q.margin_for_the_company,
11517                            CASE
11518                                WHEN bt.budget_type_group_id = 4
11519                                AND q.budget_margin_enabled > 0
11520                                AND q.budget_margin_enabled IS NOT NULL
11521                                AND q.margin_for_the_company > 0
11522                            THEN q.margin_for_the_company * q.amount
11523                            END s1,
11524                            q.invoice_margin,
11525                            CASE
11526                                WHEN bt.budget_type_group_id = 4
11527                                AND q.budget_margin_enabled <> 0
11528                                AND q.budget_margin_enabled IS NOT NULL
11529                                AND q.invoice_margin <> 0
11530                            THEN q.invoice_margin * q.amount
11531                            END s2,
11532                            q.budget_type_id,
11533                            q.budget_status_id,
11534                            q.amount,
11535                            bt.budget_type_group_id,
11536                            q.budget_margin_enabled
11537                        FROM
11538                            tbl_quotations q
11539                        JOIN tbl_companies c
11540                            ON c.company_id = q.company_id
11541                        LEFT JOIN tbl_budget_types bt
11542                            ON q.budget_type_id = bt.budget_type_id
11543                        LEFT JOIN tbl_users u
11544                            ON u.name = q.created_by
11545                        LEFT JOIN tbl_roles r
11546                            ON r.role_id = u.role_id
11547                        WHERE
11548                            q.acceptance_date IS NOT NULL
11549                            AND q.for_add = 0
11550                            AND q.budget_type_id IS NOT NULL
11551                            AND q.budget_type_id != 7
11552                            AND q.acceptance_date != '0000-00-00 00:00:00'
11553                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
11554                            {$where}
11555                    ) AS q
11556                    WHERE q.date != '0000-00-00 00:00:00' {$whereDates}
11557                    GROUP BY
11558                        q.region,
11559                        YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)),
11560                        MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)),
11561                        WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) WITH ROLLUP
11562                    ORDER BY
11563                        q.region IS NULL,
11564                        q.region,
11565                        CASE WHEN q.region IS NOT NULL
11566                            AND YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11567                            AND MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11568                            AND WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11569                        THEN 0
11570                        ELSE 1 END,
11571                    YEAR DESC,
11572                    MONTH ASC,
11573                    WEEK ASC";
11574
11575            $value = Cache::get(base64_encode($query));
11576
11577            if (! $value) {
11578                $result = DB::select($query);
11579
11580                Cache::put(base64_encode($query), $result, 600);
11581            } else {
11582                $result = $value;
11583            }
11584
11585            $query = "SELECT
11586                        q.region,
11587                        YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) 'year',
11588                        LPAD(MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)), 2, 0) 'month',
11589                        LPAD(WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)), 2, 0) 'week',
11590                        DATE_FORMAT(DATE_ADD(q.date, INTERVAL {$weekDay} DAY), '%W, %M %e') namedate,
11591
11592                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' THEN q.id END) AS groupConcatIdsTotalEnviado,
11593                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END), 0) AS totalIssueEnviado,
11594
11595                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN q.id END) AS groupConcatIdsMantenimientoEnviado,
11596                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END), 0) totalMantenimientoEnviado,
11597                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalMantenimientoPercentageEnviado,
11598
11599                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN q.id END) AS groupConcatIdsCorrectivosEnviado,
11600                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END), 0) totalCorrectivosEnviado,
11601                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalCorrectivosPercentageEnviado,
11602
11603                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN q.id END) AS groupConcatIdsObrasEnviado,
11604                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END), 0) totalObrasEnviado,
11605                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalObrasPercentageEnviado,
11606
11607                        GROUP_CONCAT(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN q.id END) AS groupConcatIdsOtrosEnviado,
11608                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END), 0) totalOtrosEnviado,
11609                        COALESCE(SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalOtrosPercentageEnviado,
11610
11611                        CASE
11612                            WHEN SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11613                            ELSE SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.s1 ELSE 0 END)
11614                                / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END)
11615                        END AS weightedAverageMarginForTheCompanyEnviado,
11616                        CASE
11617                            WHEN SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11618                            ELSE SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.s2 ELSE 0 END)
11619                                / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END)
11620                        END AS weightedAverageInvoiceEnviado,
11621
11622                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' THEN q.id END) AS groupConcatIdsTotalAceptado,
11623                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END), 0) AS totalIssueAceptado,
11624
11625                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN q.id END) AS groupConcatIdsMantenimientoAceptado,
11626                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END), 0) totalMantenimientoAceptado,
11627                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalMantenimientoPercentageAceptado,
11628
11629                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN q.id END) AS groupConcatIdsCorrectivosAceptado,
11630                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END), 0) totalCorrectivosAceptado,
11631                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalCorrectivosPercentageAceptado,
11632
11633                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN q.id END) AS groupConcatIdsObrasAceptado,
11634                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END), 0) totalObrasAceptado,
11635                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalObrasPercentageAceptado,
11636
11637                        GROUP_CONCAT(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN q.id END) AS groupConcatIdsOtrosAceptado,
11638                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END), 0) totalOtrosAceptado,
11639                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) * 100, 0) totalOtrosPercentageAceptado,
11640
11641                        CASE
11642                            WHEN SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11643                            ELSE SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.s1 ELSE 0 END)
11644                                / SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.margin_for_the_company <> 0 THEN q.amount ELSE 0 END)
11645                        END AS weightedAverageMarginForTheCompanyAceptado,
11646                        CASE
11647                            WHEN SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END) = 0 THEN 0
11648                            ELSE SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.s2 ELSE 0 END)
11649                                / SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 AND q.budget_margin_enabled > 0 AND q.invoice_margin <> 0 THEN q.amount ELSE 0 END)
11650                        END AS weightedAverageInvoiceAceptado,
11651
11652                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' THEN {$col} END) * 100, 0) totalIssuePercentage,
11653                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 3 THEN {$col} END) * 100, 0) totalMantenimientoPercentage,
11654                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 5 THEN {$col} END) * 100, 0) totalCorrectivosPercentage,
11655                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id = 4 THEN {$col} END) * 100, 0) totalObrasPercentage,
11656                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) * 100 , 0) totalOtrosPercentage,
11657
11658                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL THEN {$col} END) * 100, 0) totalIssuePercentageLead,
11659                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 3 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 3 THEN {$col} END) * 100, 0) totalMantenimientoPercentageLead,
11660                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 5 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 5 THEN {$col} END) * 100, 0) totalCorrectivosPercentageLead,
11661                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id = 4 THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id = 4 THEN {$col} END) * 100, 0) totalObrasPercentageLead,
11662                        COALESCE(SUM(CASE WHEN q.date_type = 'acceptance' AND q.issue_date IS NOT NULL AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) / SUM(CASE WHEN q.date_type = 'issue' AND q.acceptance_date IS NOT NULL AND q.budget_type_group_id IN (6, 7, 8) THEN {$col} END) * 100 , 0) totalOtrosPercentageLead
11663                    FROM
11664                    (
11665                        SELECT
11666                            'Total Grupo FIRE' region,
11667                            q.issue_date AS DATE,
11668                            'issue' AS date_type,
11669                            q.acceptance_date,
11670                            q.issue_date,
11671                            q.id,
11672                            q.margin_for_the_company,
11673                            CASE
11674                                WHEN bt.budget_type_group_id = 4
11675                                AND q.budget_margin_enabled > 0
11676                                AND q.budget_margin_enabled IS NOT NULL
11677                                AND q.margin_for_the_company <> 0
11678                            THEN q.margin_for_the_company * q.amount
11679                            END s1,
11680                            q.invoice_margin,
11681                            CASE
11682                                WHEN bt.budget_type_group_id = 4
11683                                AND q.budget_margin_enabled > 0
11684                                AND q.budget_margin_enabled IS NOT NULL
11685                                AND q.invoice_margin <> 0
11686                            THEN q.invoice_margin * q.amount
11687                            END s2,
11688                            q.budget_type_id,
11689                            q.budget_status_id,
11690                            q.amount,
11691                            bt.budget_type_group_id,
11692                            q.budget_margin_enabled
11693                        FROM
11694                            tbl_quotations q
11695                        JOIN tbl_companies c
11696                            ON c.company_id = q.company_id
11697                        LEFT JOIN tbl_budget_types bt
11698                            ON q.budget_type_id = bt.budget_type_id
11699                        LEFT JOIN tbl_users u
11700                            ON u.name = q.created_by
11701                        LEFT JOIN tbl_roles r
11702                            ON r.role_id = u.role_id
11703                        WHERE
11704                            q.issue_date IS NOT NULL
11705                            AND q.for_add = 0
11706                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
11707                            AND (q.commercial IS NOT NULL AND q.commercial != '')
11708                            AND q.budget_type_id != 7
11709                            AND q.budget_type_id IS NOT NULL
11710                            {$where}
11711
11712                        UNION ALL
11713
11714                        SELECT
11715                            'Total Grupo FIRE' region,
11716                            q.acceptance_date AS DATE,
11717                            'acceptance' AS date_type,
11718                            q.acceptance_date,
11719                            q.issue_date,
11720                            q.id,
11721                            q.margin_for_the_company,
11722                            CASE
11723                                WHEN bt.budget_type_group_id = 4
11724                                AND q.budget_margin_enabled > 0
11725                                AND q.budget_margin_enabled IS NOT NULL
11726                                AND q.margin_for_the_company <> 0
11727                            THEN q.margin_for_the_company * q.amount
11728                            END s1,
11729                            q.invoice_margin,
11730                            CASE
11731                                WHEN bt.budget_type_group_id = 4
11732                                AND q.budget_margin_enabled > 0
11733                                AND q.budget_margin_enabled IS NOT NULL
11734                                AND q.invoice_margin <> 0
11735                            THEN q.invoice_margin * q.amount
11736                            END s2,
11737                            q.budget_type_id,
11738                            q.budget_status_id,
11739                            q.amount,
11740                            bt.budget_type_group_id,
11741                            q.budget_margin_enabled
11742                        FROM
11743                            tbl_quotations q
11744                        JOIN tbl_companies c
11745                            ON c.company_id = q.company_id
11746                        LEFT JOIN tbl_budget_types bt
11747                            ON q.budget_type_id = bt.budget_type_id
11748                        LEFT JOIN tbl_users u
11749                            ON u.name = q.created_by
11750                        LEFT JOIN tbl_roles r
11751                            ON r.role_id = u.role_id
11752                        WHERE
11753                            q.acceptance_date IS NOT NULL
11754
11755                            AND q.for_add = 0
11756                            AND q.amount REGEXP '^[0-9]+\\.?[0-9]*$' = 1
11757                            AND (q.commercial IS NOT NULL AND q.commercial != '')
11758                            AND q.budget_type_id != 7
11759                            AND q.budget_type_id IS NOT NULL
11760                            {$where}
11761                    ) AS q
11762                    WHERE q.date != '0000-00-00 00:00:00' {$whereDates}
11763                    GROUP BY
11764                        q.region,
11765                        YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)),
11766                        MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)),
11767                        WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) WITH ROLLUP
11768                    ORDER BY
11769                        q.region IS NULL,
11770                        q.region,
11771                        CASE WHEN q.region IS NOT NULL
11772                            AND YEAR(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11773                            AND MONTH(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11774                            AND WEEK(DATE_ADD(q.date, INTERVAL {$weekDay} DAY)) IS NULL
11775                        THEN 0
11776                        ELSE 1 END,
11777                    YEAR DESC,
11778                    MONTH ASC,
11779                    WEEK ASC";
11780
11781            $totalGroupValue = Cache::get(base64_encode($query));
11782
11783            if (! $totalGroupValue) {
11784                $totalGroup = DB::select($query);
11785
11786                Cache::put(base64_encode($query), $totalGroup, 600);
11787            } else {
11788                $totalGroup = $totalGroupValue;
11789            }
11790
11791            array_pop($result);
11792            $merged = array_merge($result, $totalGroup);
11793
11794            return response([
11795                'message' => 'OK',
11796                'data' => $merged,
11797            ]);
11798
11799        } catch (\Exception $e) {
11800            report(AppException::fromException($e, 'LIST_QUOTATIONS_ANALYTICS_BY_SERVICE_TYPE_EXCEPTION'));
11801            return response(['message' => 'KO', 'error' => $e->getMessage()]);
11802        }
11803
11804    }
11805
11806    public function getIdsFromInternalQuoteIds($ids){
11807        $idsArray = array_filter(explode(',', (string) $ids), is_numeric(...));
11808        return TblQuotations::whereIn("internal_quote_id", $idsArray)
11809            ->pluck("id")
11810            ->toArray();
11811    }
11812
11813    public function checkQuotationExistByInternalQuoteId(Request $request): ResponseFactory|HttpResponse
11814    {
11815        try{
11816            $idsString = $request->all()["ids"];
11817            $ids = explode(',',(string) $idsString);
11818            $region = urldecode((string) request()->header('Region'));
11819            $company = TblCompanies::where("region", $region)->first();
11820
11821            if (! $company) {
11822                throw new \Exception('Region no encontrada');
11823            }
11824
11825            $companyId = $company->company_id;
11826
11827            $idsChecked = [];
11828
11829            foreach ($ids as $id) {
11830                $quote = TblQuotations::where('internal_quote_id', $id)->where('company_id', $companyId)->first();
11831                if (
11832                    ($companyId === 18 || $companyId === 22)
11833                    && ! $quote
11834                ) {
11835                    $quote = TblQuotations::where('internal_quote_id', $id)->whereIn('company_id', [18, 22])->first();
11836                }
11837                $idsChecked[$id] = $quote ? $quote->id : null;
11838            }
11839
11840            return response([
11841                'message' => 'OK',
11842                'data' => $idsChecked,
11843            ]);
11844
11845        }catch (\Exception $e) {
11846            report(AppException::fromException($e, 'CHECK_QUOTATION_EXIST_BY_INTERNAL_QUOTE_ID_EXCEPTION'));
11847            return response(['message' => 'KO', 'error' => $e->getMessage()]);
11848        }
11849
11850    }
11851
11852    public function addUpdateLog($id, $userId, $field, $oldData, $newData, int $category = 4): void {
11853        $categoryOptions = [
11854            'Crear solicitud',
11855            'Modificacion de solicitud',
11856            'Creacion presupuesto',
11857            'Solicitud a presupuesto',
11858            'Modificacion de presupuesto',
11859            'Accion del presupuesto',
11860            'Eliminacion del presupuesto',
11861            'Convertir presupuesto a trabajo',
11862        ];
11863
11864        if ($field === 'amount') {
11865            $oldData = (float) $oldData;
11866            $newData = (float) $newData;
11867        }
11868
11869        if (
11870            ($oldData === $newData && ! is_null($oldData)) ||
11871            $field === 'updated_at' ||
11872            $field === 'created_at' ||
11873            $field === 'last_follow_up_comment' ||
11874            $field === 'reason_for_rejection_id' ||
11875            $field === 'for_add' ||
11876            $field === 'has_attachment' ||
11877            $field === 'question_ids' ||
11878            $field === 'question_ids_no' ||
11879            $field === 'x_message_id' ||
11880            $field === 'from_company_id' ||
11881            $field === 'type_by_g3w' ||
11882            $field === 'user_create_by_g3w' ||
11883            $field === 'user_commercial_by_g3w' ||
11884            $field === 'user_technical_by_g3w' ||
11885            $field === 'user_responsible_by_g3w' ||
11886            $field === 'resource_id' ||
11887            $field === 'y_message_id' ||
11888            $field === 'y_status' ||
11889            $field === 'sync_import_edited' ||
11890            $field === 'updated_by' ||
11891            $field === 'likehood' ||
11892            $field === 'g3w_warning' ||
11893            $field === 'gross_margin' ||
11894            $field === 'duration' ||
11895            ($field === 'budget_type_id' && is_null($oldData) && is_null($newData)) ||
11896            ($field === 'margin_on_invoice_per_day_per_worker' && $newData === 0)
11897        ) {
11898            return;
11899        }
11900
11901        $oldRegister = null;
11902        $newRegister = null;
11903
11904        if (! is_null($oldData) || ! is_null($newData)) {
11905            switch ($field) {
11906                case 'company_id':
11907                    $oldRegister = TblCompanies::where('company_id', $oldData)->first();
11908                    $newRegister = TblCompanies::where('company_id', $newData)->first();
11909                    break;
11910                case 'customer_type_id':
11911                    $oldRegister = TblCustomerTypes::where('customer_type_id', $oldData)->first();
11912                    $newRegister = TblCustomerTypes::where('customer_type_id', $newData)->first();
11913                    break;
11914                case 'segment_id':
11915                    $oldRegister = TblSegments::where('segment_id', $oldData)->first();
11916                    $newRegister = TblSegments::where('segment_id', $newData)->first();
11917                    break;
11918                case 'budget_type_id':
11919                    $oldRegister = TblBudgetTypes::where('budget_type_id', $oldData)->first();
11920                    $newRegister = TblBudgetTypes::where('budget_type_id', $newData)->first();
11921                    break;
11922                case 'budget_status_id':
11923                    $oldRegister = TblBudgetStatus::where('budget_status_id', $oldData)->first();
11924                    $newRegister = TblBudgetStatus::where('budget_status_id', $newData)->first();
11925                    break;
11926                case 'source_id':
11927                    $oldRegister = TblSources::where('source_id', $oldData)->first();
11928                    $newRegister = TblSources::where('source_id', $newData)->first();
11929                    break;
11930            }
11931        }
11932
11933        $finalOld = $oldRegister ? $oldRegister->name : ($oldData ?? 'N/A');
11934        $finalNew = $newRegister ? $newRegister->name : ($newData ?? 'N/A');
11935
11936        if (is_numeric($userId)) {
11937            $userObj = TblUsers::where('id', $userId)->first();
11938            $userName = $userObj ? $userObj->name : "Usuario desconocido ($userId)";
11939        } else {
11940            $userName = $userId;
11941        }
11942
11943        TblQuotationsLog::create([
11944            'category' => $categoryOptions[$category] ?? 'Otros',
11945            'quotation_id' => $id,
11946            'user' => $userName,
11947            'field' => $field,
11948            'old_value' => $finalOld,
11949            'new_value' => $finalNew,
11950        ]);
11951    }
11952
11953    public function setSolicitudDuplicity(Request $request): ResponseFactory|HttpResponse{
11954        try{
11955            $type = $request->all()["type"];
11956            $quoteId = $request->all()["quoteId"];
11957            $companyId = $request->all()["companyId"];
11958
11959            $quote = TblQuotations::where('quote_id', $quoteId)->where('company_id', $companyId)->first();
11960
11961            if (! $quote) {
11962                throw new \Exception('Quote no encontrada');
11963            }
11964
11965            $newIdSolicitudDuplicityValue = null;
11966
11967            if ($type === 'reject') {
11968                $quote->budget_status_id = 20;
11969                $newIdSolicitudDuplicityValue = 'R'.$quote->id_solicitud_duplicity;
11970            }
11971
11972            $this->addUpdateLog($quote->id, 'IA', 'id_solicitud_duplicity', $quote->id_solicitud_duplicity, $newIdSolicitudDuplicityValue);
11973
11974            $quote->id_solicitud_duplicity = $newIdSolicitudDuplicityValue;
11975
11976            $quote->save();
11977
11978            return response([
11979                'message' => 'OK',
11980                'data' => $quote,
11981            ]);
11982
11983        }catch (\Exception $e) {
11984            report(AppException::fromException($e, 'SET_SOLICITUD_DUPLICITY_EXCEPTION'));
11985            return response(['message' => 'KO', 'error' => $e->getMessage()]);
11986        }
11987    }
11988
11989    public function getQuoteIdOfDuplicityById($id): ResponseFactory|HttpResponse{
11990        try{
11991            $quote = TblQuotations::where("id", $id)->first();            
11992
11993            if (! $quote) {
11994                throw new \Exception('Quote no encontrada');
11995            }
11996
11997            return response([
11998                'message' => 'OK',
11999                'data' => $quote,
12000            ]);
12001
12002        } catch (\Exception $e) {
12003            report(AppException::fromException($e, 'GET_QUOTE_ID_OF_DUPLICITY_BY_QUOTE_ID_EXCEPTION'));
12004            return response(['message' => 'KO', 'error' => $e->getMessage()]);
12005        }
12006    }
12007
12008    public function download_s3_files(Request $request)
12009    {
12010        ini_set('max_execution_time', 123456);
12011
12012        try {
12013
12014            $data = $request->all();
12015            $zipName = 'files_'.time().'.zip';
12016
12017            $r = new Request([
12018                'filterModel' => $data['filterModel'],
12019                'sortModel' => $data['sortModel'],
12020                'start' => 0,
12021                'end' => 999999999,
12022                'company_id' => $data['company_id'],
12023                'user_id' => $data['user_id'],
12024                'ids' => $data['ids'],
12025                'searchText' => $data['searchText'],
12026                'ids_not_in' => $data['ids_not_in'],
12027            ]);
12028
12029            $result = $this->list_quotations($r);
12030            $result = $result->original['data'];
12031            $orderIds = array_column($result, 'id');
12032
12033            $files = TblFiles::whereIn('quotation_id', $orderIds)->where('is_internal', null)->get();
12034            if ($files->isEmpty()) {
12035                return response(['message' => 'KO', 'error' => 'No files found']);
12036            }
12037
12038            $tempDir = storage_path('app/temp');
12039
12040            if (! file_exists($tempDir)) {
12041                mkdir($tempDir, 0755, true);
12042            }
12043
12044            $zipPath = $tempDir.'/'.$zipName;
12045
12046            $zip = new ZipArchive;
12047            if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
12048                return response(['message' => 'KO', 'error' => 'Could not create ZIP file']);
12049            }
12050
12051            foreach ($files as $file) {
12052                $filePath = 'uploads/'.$file->filename;
12053
12054                if (Storage::disk('s3')->exists($filePath)) {
12055                    $contents = Storage::disk('s3')->get($filePath);
12056                    $zip->addFromString(basename($filePath), $contents);
12057                }
12058            }
12059
12060            $zip->close();
12061
12062            if (! file_exists($zipPath)) {
12063                return response(['message' => 'KO', 'error' => 'ZIP file creation failed'], 500);
12064            }
12065
12066            $content = file_get_contents($zipPath);
12067
12068            unlink($zipPath);
12069
12070            return response($content, 200, [
12071                'Content-Type' => 'application/zip',
12072                'Content-Disposition' => 'attachment; filename="'.$zipName.'"',
12073            ]);
12074        } catch (\Exception $e) {
12075            /** @disregard P1014 */
12076            $e->exceptionCode = 'DOWNLOAD_S3_FILES_EXCEPTION';
12077            report($e);
12078
12079            return response(['message' => 'KO', 'error' => $e->getMessage()]);
12080        }
12081    }
12082
12083    /**
12084     * FIRE-976: Return budget status and amount by Gestiona internal_quote_id + Region.
12085     * If the quotation doesn't exist locally, syncs it from Gestiona first.
12086     */
12087    public function getQuotationStatusByInternalId(Request $request, $id): ResponseFactory|\Illuminate\Http\Response
12088    {
12089        try {
12090            $region = urldecode((string) $request->header('Region'));
12091            if ($region === 'Catalunya') {
12092                $region = 'Cataluña';
12093            }
12094
12095            $company = TblCompanies::where('region', $region)->first();
12096            if (!$company) {
12097                return response(['message' => 'KO', 'error' => 'Region not found'], 404);
12098            }
12099
12100            $companyId = $company->company_id;
12101
12102            $quotation = $this->findQuotationByInternalId($id, $companyId);
12103
12104            // Found locally — return Titan data
12105            if ($quotation) {
12106                $statusName = null;
12107                if ($quotation->budget_status_id) {
12108                    $status = TblBudgetStatus::where('budget_status_id', $quotation->budget_status_id)->first();
12109                    $statusName = $status?->name;
12110                }
12111
12112                return response([
12113                    'message' => 'OK',
12114                    'data' => [
12115                        'internal_quote_id' => $quotation->internal_quote_id,
12116                        'amount' => $quotation->amount,
12117                        'budget_status_id' => $quotation->budget_status_id,
12118                        'budget_status' => $statusName,
12119                    ],
12120                ]);
12121            }
12122
12123            // Not found locally — try syncing from Gestiona
12124            $presupuestosService = app(\App\Services\PresupuestosService::class);
12125            $syncResult = $presupuestosService->syncById($id, $region);
12126
12127            if (!empty($syncResult['success'])) {
12128                // Sync succeeded — return freshly created Titan data
12129                $quotation = $this->findQuotationByInternalId($id, $companyId);
12130                if ($quotation) {
12131                    $statusName = null;
12132                    if ($quotation->budget_status_id) {
12133                        $status = TblBudgetStatus::where('budget_status_id', $quotation->budget_status_id)->first();
12134                        $statusName = $status?->name;
12135                    }
12136
12137                    return response([
12138                        'message' => 'OK',
12139                        'data' => [
12140                            'internal_quote_id' => $quotation->internal_quote_id,
12141                            'amount' => $quotation->amount,
12142                            'budget_status_id' => $quotation->budget_status_id,
12143                            'budget_status' => $statusName,
12144                        ],
12145                    ]);
12146                }
12147            }
12148
12149            // Sync failed — try to return raw Gestiona data with normalized status
12150            $gestionaResult = $presupuestosService->fetchFromGestiona($id, $region);
12151
12152            if (!empty($gestionaResult['success'])) {
12153                return response([
12154                    'message' => 'OK',
12155                    'data' => $gestionaResult['data'],
12156                    'source' => 'gestiona',
12157                ]);
12158            }
12159
12160            return response(['message' => 'KO', 'error' => 'Quotation not found'], 404);
12161        } catch (\Exception $e) {
12162            report(AppException::fromException($e, 'GET_QUOTATION_STATUS_BY_INTERNAL_ID_EXCEPTION'));
12163            return response(['message' => 'KO', 'error' => $e->getMessage()], 500);
12164        }
12165    }
12166
12167    /**
12168     * FIRE-976: Find a quotation by internal_quote_id + company_id, with 18/22 fallback.
12169     */
12170    private function findQuotationByInternalId($id, int $companyId)
12171    {
12172        $quotation = TblQuotations::where('internal_quote_id', $id)
12173            ->where('company_id', $companyId)
12174            ->first();
12175
12176        if (!$quotation && ($companyId === 18 || $companyId === 22)) {
12177            $quotation = TblQuotations::where('internal_quote_id', $id)
12178                ->whereIn('company_id', [18, 22])
12179                ->first();
12180        }
12181
12182        return $quotation;
12183    }
12184}