Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 247
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
EmailReportController
0.00% covered (danger)
0.00%
0 / 247
0.00% covered (danger)
0.00%
0 / 12
4290
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 sendDailyReportEmails
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
240
 getDailyReportEmailHTML
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 1
182
 getJSONData
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
 saveToJSON
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 viewEmail
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 saveToPDF
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
42
 getEmailsArray
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getContactName
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 sendEmail
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
12
 checkPredis
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 addToPredis
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BuyerKiosk\DailyReport\Controllers;
4
5
6use BuyerKiosk\DailyReport\EmailReport;
7use BuyerKiosk\DailyReport\SalesReport;
8class EmailReportController extends \BuyerKiosk\Core\Controllers\BaseController {
9    private $store;
10    private $log;
11    private $redis;
12    public function __construct($app, \Store $store)
13    {
14        parent::__construct($app);
15        $this->store = $store;
16        $this->log = new \KLogger($_ENV['LOG_DIR']."/dailyReports.log", \KLogger::DEBUG);
17        $this->redis = new \Predis\Client($_ENV['REDIS_URL']);
18    }
19
20    public function sendDailyReportEmails($data) {
21        try {
22            // Validate required data structures
23            if (!is_array($data)) {
24                throw new \Exception("Invalid data format: expected array");
25            }
26            
27            // Ensure required top-level keys exist
28            if (!isset($data['HISTORY']) || !is_array($data['HISTORY']) || empty($data['HISTORY'])) {
29                $data['HISTORY'] = array(array());
30                $this->log->LogDebug("HISTORY data missing, using default empty structure");
31            }
32            
33            if (!isset($data['DAILYRESULTS']) || !is_array($data['DAILYRESULTS']) || empty($data['DAILYRESULTS'])) {
34                $data['DAILYRESULTS'] = array(array('Date' => date('Y-m-d')));
35                $this->log->LogDebug("DAILYRESULTS data missing, using default structure");
36            }
37            
38            $emailReport = new EmailReport($this->store->getTypeNum(), $data);
39            //Submit the sales report
40            $salesReport = new SalesReport($this->store->getTypeNum());
41            $salesReport->createFromDataArray($data);
42            $emailsArray = $this->getEmailsArray($emailReport);
43            
44            // Check if we have any recipients
45            if (empty($emailsArray)) {
46                error_log("ERROR: EmailReportController::sendDailyReportEmails - No email recipients found for store " . $this->store->getTypeNum());
47                $this->log->LogError("No email recipients found for daily report");
48            }
49
50            $this->saveToJSON($data);
51            $successes = 0;
52            $errors = 0;
53
54            foreach ($emailsArray as $contact) {
55                $this->log->LogDebug("Contact: ".print_r($contact, true));
56                try {
57                    $this->log->LogDebug("Sending Daily Email to [" . $contact['email'] . "]");
58                    if (!$this->checkPredis($contact['email'], $this->store->getTypeNum())) {
59                        $html = $this->getDailyReportEmailHTML($emailReport, $salesReport, $contact);
60                        $fullName = $this->getContactName($contact);
61
62                        if ($this->sendEmail($html, $contact['email'], $fullName['firstName'] . " " . $fullName['lastName'], $this->store, $emailReport->date->format("Y-m-d"))) {
63                            $this->log->LogDebug("Sent Daily Email to [" . $contact['email'] . "]");
64                            $emailReport->insertIntoReportLogDatabase($contact['email'], $fullName['firstName'], $fullName['lastName'], 1);
65                            $successes++;
66                        } else {
67                            $emailReport->insertIntoReportLogDatabase($contact['email'], $fullName['firstName'], $fullName['lastName'], 0);
68                            $errors++;
69                        }
70                    }
71                } catch (\Exception $e) {
72                    $email = isset($contact['email']) ? $contact['email'] : 'unknown';
73                    error_log("ERROR: EmailReportController::sendDailyReportEmails - Failed to send email to $email" . $e->getMessage());
74                    $this->log->LogDebug("Error Sending Daily Email to [" . $email . "]");
75                    $this->log->LogError($e->getMessage());
76                    $this->log->LogError($e->getTraceAsString());
77                    $this->log->LogError("Line: ".$e->getLine());
78                    $errors++;
79                    // Continue to next recipient even if this one failed
80                }
81            }
82            return array("errors" => $errors, "successes" => $successes);
83        } catch (\Exception $e) {
84            error_log("ERROR: EmailReportController::sendDailyReportEmails - Failed to setup daily emails for store " . $this->store->getTypeNum() . ": " . $e->getMessage());
85            $this->log->LogDebug("Error Setting Up Daily Email");
86            $this->log->LogError($e->getMessage());
87            $this->log->LogError($e->getTraceAsString());
88            $this->log->LogError("Line: ".$e->getLine());
89            return array("errors" => 1, "successes" => 0);
90        }
91    }
92    public function getDailyReportEmailHTML($emailReport, $salesReport, $contact) {
93        // Safely get X-To-Date data with fallback
94        $XToDate = null;
95        if (isset($emailReport->data["X-To-Date"]) && isset($emailReport->data["X-To-Date"][0])) {
96            $XToDate = $emailReport->data["X-To-Date"][0];
97        } else {
98            $this->log->LogDebug("X-To-Date data not found in email report data");
99            // Provide default empty structure for X-To-Date
100            $XToDate = array(
101                'lastYearGrossSalesCostWeekToDate' => 0,
102                'lastYearGrossSalesRetailWeekToDate' => 0,
103                'lastYearNetSalesCostWeekToDate' => 0,
104                'lastYearNetSalesRetailWeekToDate' => 0,
105                'currentYearGrossSalesCostWeekToDate' => 0,
106                'currentYearGrossSalesRetailWeekToDate' => 0,
107                'currentYearNetSalesCostWeekToDate' => 0,
108                'currentYearNetSalesRetailWeekToDate' => 0,
109                'lastYearGrossSalesCostMonthToDate' => 0,
110                'lastYearGrossSalesRetailMonthToDate' => 0,
111                'lastYearNetSalesCostMonthToDate' => 0,
112                'lastYearNetSalesRetailMonthToDate' => 0,
113                'currentYearGrossSalesCostMonthToDate' => 0,
114                'currentYearGrossSalesRetailMonthToDate' => 0,
115                'currentYearNetSalesCostMonthToDate' => 0,
116                'currentYearNetSalesRetailMonthToDate' => 0
117            );
118        }
119        
120        $lf = $salesReport->getLiveFinancials();
121        $this->log->LogDebug("Live Financials: ".print_r($lf, true));
122        if(!isset($lf) || !is_array($lf)) {
123            $lf = array("buysGoal" => "0", "salesGoal" => "0", "buysOutstanding" => "0");
124        }
125        
126        $contactName = $this->getContactName($contact);
127        $firstName = isset($contactName['firstName']) ? $contactName['firstName'] : '';
128        $lastName = isset($contactName['lastName']) ? $contactName['lastName'] : '';
129
130        $salesReport->insert();
131        //URL Decode the user submitted fields
132        $emailReport->decodeDailyResultsFields();
133
134
135
136
137        //Add safe alerts:
138        $repository = new \BuyerKiosk\Cash\CashActivityRepository($this->store);
139        $latestSafeBalance = $repository->getLatestSafeBalance();
140        if($latestSafeBalance == null) {
141            $alerts = null;
142        } else {
143            $safeAlerts = new \BuyerKiosk\Cash\SafeLevelAlert($latestSafeBalance, $this->store);
144            $alerts = $safeAlerts->checkAlertLevels();
145            $cashActivityController = new \BuyerKiosk\Cash\CashActivityController($repository);
146            $closingDate = isset($emailReport->data['HISTORY'][0]["closingDate"]) ? $emailReport->data['HISTORY'][0]["closingDate"] : date('Y-m-d');
147            $cashActivityController->getDailyTransactions($closingDate);
148        }
149        $cashActivityController = new \BuyerKiosk\Cash\CashActivityController($repository);
150        $closingDate = isset($emailReport->data['HISTORY'][0]["closingDate"]) ? $emailReport->data['HISTORY'][0]["closingDate"] : date('Y-m-d');
151        $dailyTransactionsArray = $cashActivityController->getDailyTransactions($closingDate);
152        if($dailyTransactionsArray['status'] == 'error') {
153            $cashBalance = null;
154        } else {
155            $cashBalancer = new \BuyerKiosk\Cash\CashBalancer($dailyTransactionsArray['data'], $salesReport);
156            $cashBalance =  $cashBalancer->calculateVariance();
157        }
158        $employeeArray = $emailReport->getEmployeeArray();
159        $taskGroupsArray = $emailReport->getStoreTaskGroups();
160        $checkListArray = $emailReport->getCheckListArray($taskGroupsArray, $employeeArray);
161        $printLogArray = $emailReport->getPrintLogArray();
162        $diffArray = $emailReport->getDiffArray($XToDate);
163        $errors = 0;
164        $successes = 0;
165        ob_start();
166        $this->_app->render('email/daily_summary.html', [
167            "name" => $firstName." ".$lastName,
168            "email" => $contact['email'],
169            "cl" => $checkListArray,
170            "dr" => isset($emailReport->data['DAILYRESULTS'][0]) ? $emailReport->data['DAILYRESULTS'][0] : array(),
171            "fd" => isset($emailReport->data['HISTORY'][0]) ? $emailReport->data['HISTORY'][0] : array(),
172            "store" => getStoreInfo($this->store),
173            "pl" => $printLogArray,
174            "xd" => $XToDate,
175            "diff" => $diffArray,
176            "lf"  => $lf,
177            "alerts" => $alerts,
178            "cashBalance" => $cashBalance,
179        ]);
180        $html = ob_get_clean();
181        return $html;
182    }
183    public function getJSONData($date) {
184        try {
185            $dir = $_ENV['HOME_DIR']."/emails/".$this->store->getTypeNum()."/json/".$date.".json";
186            if(file_exists($dir)) {
187                //return the json data and convert it to an associative array
188                $content = file_get_contents($dir);
189                if ($content === false) {
190                    error_log("ERROR: EmailReportController::getJSONData - Failed to read file: $dir");
191                    $this->log->LogError("Failed to read JSON file: $dir");
192                    return null;
193                }
194                $decoded = json_decode($content, true);
195                if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
196                    error_log("ERROR: EmailReportController::getJSONData - JSON decode failed: " . json_last_error_msg());
197                    $this->log->LogError("JSON decode error: " . json_last_error_msg());
198                    return null;
199                }
200                return $decoded;
201            }
202            return null;
203        } catch (\Exception $e) {
204            error_log("ERROR: EmailReportController::getJSONData - Exception: " . $e->getMessage());
205            $this->log->LogDebug("Error Getting JSON Data");
206            $this->log->LogError($e->getMessage());
207            return null;
208        }
209
210    }
211
212    private function saveToJSON($data) {
213        try {
214            $date = new \DateTime('now', new \DateTimeZone('utc'));
215            $date = $date->format("Y-m-d");
216            //Make a directory in the emails folder for today
217            $dir = $_ENV['HOME_DIR']."/emails/".$this->store->getTypeNum()."/json";
218            if(!file_exists($dir)) {
219                if (!mkdir($dir, 0777, true)) {
220                    error_log("ERROR: EmailReportController::saveToJSON - Failed to create directory: $dir");
221                    $this->log->LogError("Failed to create directory: $dir");
222                    return;
223                }
224            }
225            //Write the json to a file
226            $file = $dir."/".$date.".json";
227            $result = file_put_contents($file, json_encode($data));
228            if ($result === false) {
229                error_log("ERROR: EmailReportController::saveToJSON - Failed to write file: $file");
230                $this->log->LogError("Failed to write JSON file: $file");
231            }
232        } catch (\Exception $e) {
233            error_log("ERROR: EmailReportController::saveToJSON - Exception: " . $e->getMessage());
234            $this->log->LogDebug("Error Saving JSON Data");
235            $this->log->LogError($e->getMessage());
236        }
237    }
238    public function viewEmail($date) {
239        $data = $this->getJSONData($date);
240        if($data !== null) {
241            $emailReport = new EmailReport($this->store->getTypeNum(), $data);
242            $salesReport = new SalesReport($this->store->getTypeNum());
243            $salesReport->createFromDataArray($data);
244            $emailsArray = array(array("email" => "sample@buyerkiosk.com","full_name_array" => array("Sample", "User")));
245            $html = $this->getDailyReportEmailHTML($emailReport, $salesReport, $emailsArray[0]);
246            echo $html;
247        } else {
248            echo "No Email From That Date";
249        }
250    }
251
252    private function saveToPDF($contact, $html) {
253        try {
254            $date = new \DateTime('now', new \DateTimeZone('utc'));
255            $date = $date->format("Y-m-d");
256            //Make a directory in the emails folder for today
257            $dir = $_ENV['HOME_DIR']."/emails/".$date."/".$this->store->getTypeNum();
258            if(!file_exists($dir)) {
259                if (!mkdir($dir, 0777, true)) {
260                    error_log("ERROR: EmailReportController::saveToPDF - Failed to create directory: $dir");
261                    $this->log->LogError("Failed to create directory: $dir");
262                    return;
263                }
264            }
265            
266            $email = isset($contact['email']) ? $contact['email'] : 'unknown';
267            
268            //Write the html to a file
269            $file = $dir."/".$email.".html";
270            if (file_put_contents($file, $html) === false) {
271                error_log("ERROR: EmailReportController::saveToPDF - Failed to write HTML file: $file");
272                $this->log->LogError("Failed to write HTML file: $file");
273            }
274            
275            //Write the email to a file
276            $file = $dir."/".$email.".pdf";
277            $pdf = new \TCPDF();
278            $pdf->AddPage();
279            $pdf->writeHTML($html);
280            $pdf->Output($file, 'F');
281        } catch (\Exception $e) {
282            $this->log->LogError($e->getMessage());
283        }
284    }
285    private function getEmailsArray($emailReport) {
286        $emailsArray = $emailReport->getUsersForEmail();
287        //add the dailyreports@buyerkiosk.com email to contact list to get a copy of the report
288        $tempContact =  array("email" => "dailyreports@buyerkiosk.com","full_name_array" => array("Buyerkiosk", "Admin"));
289        $emailsArray[] = $tempContact;
290        return $emailsArray;
291    }
292    private function getContactName($contact) {
293        $firstName = "";
294        $lastName = "";
295        
296        if (isset($contact['full_name_array']) && is_array($contact['full_name_array'])) {
297            if (isset($contact['full_name_array'][0])) {
298                $firstName = $contact['full_name_array'][0];
299            }
300            if (count($contact['full_name_array']) > 1 && isset($contact['full_name_array'][1])) {
301                $lastName = $contact['full_name_array'][1];
302            }
303        } else {
304            $this->log->LogWarning("Contact missing full_name_array: " . print_r($contact, true));
305        }
306        
307        return array("firstName" => $firstName, "lastName" => $lastName);
308    }
309
310    public function sendEmail($html, $email, $fullName, \Store $store, $date) {
311        $sendgrid_apikey = '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';
312        $url = 'https://api.sendgrid.com/';
313
314        $params = array(
315            'to'        => $email,
316            'toname'    => $fullName,
317            'from'      => "admin@buyerkiosk.com",
318            'fromname'  => "BuyerKiosk",
319            'subject'   => 'BuyerKiosk Daily Summary - '.$store->getStoreNum().' | '.$date,
320            'html'      => $html
321        );
322
323        $request =  $url.'api/mail.send.json';
324        $session = curl_init($request);
325        curl_setopt($session, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
326        curl_setopt($session, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $sendgrid_apikey));
327        curl_setopt ($session, CURLOPT_POST, true);
328        curl_setopt ($session, CURLOPT_POSTFIELDS, $params);
329        curl_setopt($session, CURLOPT_HEADER, false);
330        curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
331
332        $response = curl_exec($session);
333        if ($response === false) {
334            $curl_error = curl_error($session);
335            error_log("ERROR: EmailReportController::sendEmail - cURL failed: " . $curl_error . " for email: " . $email);
336            $this->log->LogError("cURL error sending email to $email" . $curl_error);
337        } else {
338            // Check if response indicates an error
339            $result = json_decode($response, true);
340            if (isset($result['errors'])) {
341                error_log("ERROR: EmailReportController::sendEmail - SendGrid error: " . print_r($result['errors'], true) . " for email: " . $email);
342                $this->log->LogError("SendGrid error for $email" . print_r($result['errors'], true));
343            }
344        }
345        curl_close($session);
346        ob_start();
347        print_r($response);
348        $result = ob_get_clean();
349        $this->log->LogDebug($result);
350        $this->addToPredis($email, $store->getTypeNum());
351        return true;
352    }
353    private function checkPredis($email, $typeNum) {
354        if($this->redis->hexists($typeNum."_dailyEmail", $email) && $email !=="ryan@v2ts.com" && $email !=="dailyreports@buyerkiosk.com") {
355            $sent = $this->redis->hget($typeNum."_dailyEmail", $email);
356            if((int)substr($sent, 0, 7) > 250000) {
357                $this->redis->hdel($typeNum."_dailyEmail", $email);
358                return false;
359            }
360            $date = new \DateTime('now', new \DateTimeZone('utc'));
361            $sent = new \DateTime($sent, new \DateTimeZone('utc'));
362            $diff = $date->getTimestamp() - $sent->getTimestamp();
363
364            if($diff < 14400) {
365                $this->log->LogDebug("Prevented Multiple Email Spam To: [".$email."] [".$diff."]");
366                return true;
367            }
368        }
369        return false;
370    }
371
372    private function addToPredis($email, $typeNum) {
373        $date = new \DateTime('now', new \DateTimeZone('utc'));
374        $this->redis->hset($typeNum."_dailyEmail", $email, $date->format("Y-m-d H:i:s"));
375    }
376}