setTable('department_daily_reports'); } public function asArray(): array { return [ 'id' => (int)$this->id, 'department_id' => (int)$this->department_id->value(), 'water_usage' => (int)$this->water_usage->value(), 'water_usage_morning' => (int)$this->water_usage_morning->value(), 'notes' => (string)$this->notes->value(), 'filled_by' => (int)$this->filled_by->value(), 'created_at' => (string)$this->created_at->value(), ]; } /** * Get the amount of products sold on a given date * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param int $product_id The id of the product * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return int The amount of products sold on the given date (Not counting products from removed orders) * @throws Exception */ public function getProductsSoldOnDate(string $date, int $department_id, int $product_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function () use ($date, $department_id, $product_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Method cache for 2 minutes $department = (new departments_o())->select($department_id); $department->requireSelected(); return $department->getTotalAddonsSoldInDepartment([$product_id], $date, $date_to, $department_id); }); } /** * Get the amount of products sold, where the product would be applicable as an addon * E.g. if the product is "Cheese" this would return the amount of "Pizza" sold * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param int $product_id The id of the product (the addon) * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return int The amount of products sold on the given date (Not counting products from removed orders) * @throws Exception */ public function getProductsSoldWithAddonApplicable(string $date, int $department_id, int $product_id, string $date_to = null): int { global /** @var db $db */ $db; // Get all the addons for the product $addons = (new product_options_o())->getOptionProducts($product_id); $addons = array_map(function ($addon) { return $addon['product_id']; }, $addons); $addons = implode(',', $addons); // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } $department = (new departments_o())->select($department_id); return $department->getTotalMaxAddonsInDepartment([$product_id], $date, $date_to, $department_id); } /** * Get the amount of products sold on a given date * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return int The amount of products sold on the given date (Not counting products from removed orders) */ public function getTransactionProductsOnDateCount(string $date, int $department_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($date, $department_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Set the date time to cover the entire day $date = date('Y-m-d 00:00:00', strtotime($date)); $date_to = date('Y-m-d 23:59:59', strtotime($date_to)); $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT SUM(oi.quantity) as amount FROM orders o JOIN order_items oi ON o.id = oi.order_id WHERE o.department_id = ? AND DATE(o.created_at) BETWEEN ? AND ? AND o.deleted_at IS NULL AND oi.deleted_at IS NULL' ); if ($stmt) { $stmt->bind_param('iss', $department_id, $date, $date_to); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field $amount = $data['amount']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount; }); } /** * Get the amount of transactions on a given date * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return int The amount of transactions on the given date (Not counting products from removed orders) */ public function getTransactionsOnDateCount(string $date, int $department_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($date, $department_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Set the date time to cover the entire day $date = date('Y-m-d 00:00:00', strtotime($date)); $date_to = date('Y-m-d 23:59:59', strtotime($date_to)); $conn = $db->conn(); // Make sure only to count orders with at least one non-deleted order item $stmt = $conn->prepare( 'SELECT COUNT(DISTINCT o.id) as amount FROM orders o JOIN order_items oi ON o.id = oi.order_id WHERE o.department_id = ? AND DATE(o.created_at) BETWEEN ? AND ? AND o.deleted_at IS NULL AND oi.deleted_at IS NULL' ); if ($stmt) { $stmt->bind_param('iss', $department_id, $date, $date_to); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field $amount = $data['amount']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount; }); } /** * Select the department daily report for today * @param int $department_id The id of the department * @return department_daily_reports_o The department daily report * @throws Exception If a department daily report does not exist for today * @throws Exception If the object was not selected */ public function selectDepartmentDailyReport(int $department_id): department_daily_reports_o { global $db; if (!$this->doesDepartmentDailyReportExist($department_id)) { throw new Exception('Department daily report does not exist'); } $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT id FROM department_daily_reports o WHERE o.department_id = ? AND DATE(o.created_at) = CURDATE()' ); // Check if the statement was prepared successfully if ($stmt) { $stmt->bind_param('i', $department_id); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field $id = $data['id']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } $this->select($id); return $this; } /** * Check if a department daily report exists for today * @param int $department_id The id of the department * @param string $date The date to check for (YYYY-MM-DD) * @return bool True if the report exists, false otherwise */ public function doesDepartmentDailyReportExist(int $department_id, string $date = 'today'): bool { global $db; if ($date === 'today') { $date = date('Y-m-d'); } $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT COUNT(*) as amount FROM department_daily_reports o WHERE o.department_id = ? AND DATE(o.created_at) = ?' ); if ($stmt) { $stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field $amount = $data['amount']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount > 0; } /** * Select the last department daily report for a department * @param int $department_id The id of the department * @return department_daily_reports_o The department daily report * @throws Exception If the object was not selected */ public function selectLastDepartmentDailyReport(int $department_id): department_daily_reports_o { global $db; $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT id FROM department_daily_reports o WHERE o.department_id = ? ORDER BY o.created_at DESC LIMIT 1' ); // Check if the statement was prepared successfully if ($stmt) { $stmt->bind_param('i', $department_id); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "id" field if (!$data) { throw new Exception('Department daily report does not exist'); } $id = $data['id']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } $this->select($id); return $this; } public function getTransactionsOnDateEarnings(string $date, int $department_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($date, $department_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Set the date time to cover the entire day $date = date('Y-m-d 00:00:00', strtotime($date)); $date_to = date('Y-m-d 23:59:59', strtotime($date_to)); $conn = $db->conn(); // Get all the product prices, in all the orders (Not counting removed orders), for the given date and department, and sum them $stmt = $conn->prepare( 'SELECT SUM(oi.price * oi.quantity) as amount FROM orders o JOIN order_items oi ON o.id = oi.order_id WHERE o.department_id = ? AND DATE(o.created_at) BETWEEN ? AND ? AND o.deleted_at IS NULL AND oi.deleted_at IS NULL' ); if ($stmt) { $stmt->bind_param('iss', $department_id, $date, $date_to); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field if (!$data) { // If there are no orders, set the amount to 0 $amount = 0; } else { $amount = $data['amount']; } $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount; }); } /** * Check if a department daily report exists for today * @param int $department_id The id of the department * @return bool True if the report exists, false otherwise */ public function hasReportForToday(int $department_id): bool { return $this->doesDepartmentDailyReportExist($department_id); } /** * Select the department daily report for a given date * @param int $department_id The id of the department * @param string $date The date to get the report for (YYYY-MM-DD) * @return department_daily_reports_o The department daily report * @throws Exception If a department daily report does not exist for today * @throws Exception If the object was not selected */ public function selectDepartmentReportByDate(int $department_id, string $date): department_daily_reports_o { global $db; if (!$this->doesDepartmentDailyReportExist($department_id, $date)) { // Create a new report for the given date, if it does not exist return $this->add($department_id, 0, 0, '', 0, $date); } $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT id FROM department_daily_reports o WHERE o.department_id = ? AND DATE(o.created_at) = ?' ); // Check if the statement was prepared successfully if ($stmt) { $stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field if (!$data) { throw new Exception('Department daily report does not exist'); } $id = $data['id']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } $this->select($id); return $this; } /** * Add a department daily report * @param int $department_id The id of the department * @param int $water_usage The cm3 of water used in the department today * @param int $water_usage_morning The cm3 of water used in the department (Checked in the morning) * @param string $notes Any notes for the report * @param int $filled_by The user (id) that filled the report * @return department_daily_reports_o * @throws Exception If the object was not created successfully */ public function add( int $department_id, int $water_usage, int $water_usage_morning, string $notes, int $filled_by, string $created_at = null ): department_daily_reports_o { // Validate the created_at date, if provided if ($created_at !== null) { $dateTime = \DateTime::createFromFormat('Y-m-d', $created_at); if (!$dateTime || $dateTime->format('Y-m-d') !== $created_at) { throw new Exception('Invalid date format for created_at. Expected format: YYYY-MM-DD'); } } $tmp_id = $this->add_object([ 'department_id' => (int)$department_id, 'water_usage' => (int)$water_usage, 'water_usage_morning' => (int)$water_usage_morning, 'notes' => (string)$notes, 'filled_by' => (int)$filled_by, 'created_at' => $created_at ? $created_at : date('Y-m-d H:i:s'), ]); $this->id = $tmp_id; $this->getObjectProperties(); $this->objectChanged(); return $this; } public function getObjectProperties(): void { $this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', false); $this->water_usage = new object_property($this->table, $this->id, 'water_usage', 'int', false); $this->water_usage_morning = new object_property($this->table, $this->id, 'water_usage_morning', 'int', false); $this->notes = new object_property($this->table, $this->id, 'notes', 'string', false); $this->filled_by = new object_property($this->table, $this->id, 'filled_by', 'int', false); $this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false); } public function objectChanged(): void { //TODO: Add cache invalidation } public function hasReport(int $department_id, string $date): bool { return $this->doesDepartmentDailyReportExist($department_id, $date); } public function getBookingsOnDateCount(string $date, int $department_id, string $status): int { global /** @var db $db */ $db; $conn = $db->conn(); $stmt = $conn->prepare( 'SELECT COUNT(*) as amount FROM bookings_new o WHERE o.department = ? AND DATE(o.created_at) = ? AND o.status = ?' ); if ($stmt) { $stmt->bind_param('iss', $department_id, $date, $status); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field $amount = $data['amount']; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount; } /** * This function calculates how many of the transactions on a given date included a wash * That is defined as any product that has the "is_wash" field set to true in the products table * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return void */ public function getTransactionsOnDateWashesCount(string $date, int $department_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($date, $department_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Set the date time to cover the entire day $date = date('Y-m-d 00:00:00', strtotime($date)); $date_to = date('Y-m-d 23:59:59', strtotime($date_to)); $conn = $db->conn(); // Get all the product prices, in all the orders (Not counting removed orders), for the given date and department, and sum them $stmt = $conn->prepare( 'SELECT COUNT(DISTINCT o.id) as amount FROM orders o JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id WHERE o.department_id = ? AND DATE(o.created_at) BETWEEN ? AND ? AND o.deleted_at IS NULL AND oi.deleted_at IS NULL AND p.is_wash = 1' ); if ($stmt) { $stmt->bind_param('iss', $department_id, $date, $date_to); // Bind parameters (i = integer, s = string) $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array // Access the "amount" field if (!$data) { // If there are no orders, set the amount to 0 $amount = 0; } else { $amount = $data['amount']; } $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return (int)$amount; }); } /** * This function calculates how much water has been used on the transactions on a given date * That is calculated by getting the water usage report on the earliest report before the given date * and the water usage report on the latest report on or before the given date * The difference between the two is the water usage for the given date * @note This does not count products from removed orders, only products from orders that are still active * @param string $date The date to get the report for (YYYY-MM-DD) * @param int $department_id The id of the department * @param string|null $date_to (Optional) The end date to get the report for (YYYY-MM-DD). If provided, the report will be for the date range between $date and $date_to * @return int The amount of water used on the given date */ public function getTransactionsOnDateWaterUsage(string $date, int $department_id, string $date_to = null): int { return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($date, $department_id, $date_to) { global /** @var db $db */ $db; // If the date_to is null, set it to the date if ($date_to === null) { $date_to = $date; // Making the report for an entire day } // Set the date time to cover the entire day $date = date('Y-m-d 00:00:00', strtotime($date)); $date_to = date('Y-m-d 23:59:59', strtotime($date_to)); $conn = $db->conn(); // Get the earliest report before the given date $stmt = $conn->prepare( 'SELECT water_usage FROM department_daily_reports o WHERE o.created_at < ? AND o.department_id = ? AND o.water_usage > 0 ORDER BY o.created_at DESC LIMIT 1' ); if ($stmt) { $stmt->bind_param('si', $date, $department_id); // Bind parameters $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array $earliest_usage = $data ? (int)$data['water_usage'] : 0; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } // Get the report closest to the date_to, but not after $stmt = $conn->prepare( 'SELECT water_usage FROM department_daily_reports o WHERE o.created_at <= ? AND o.department_id = ? AND o.water_usage > 0 ORDER BY o.created_at DESC LIMIT 1' ); if ($stmt) { $stmt->bind_param('si', $date_to, $department_id); // Bind parameters $stmt->execute(); $result = $stmt->get_result(); // Get the result set from the statement $data = $result->fetch_assoc(); // Fetch the result as an associative array $latest_usage = $data ? (int)$data['water_usage'] : 0; $stmt->close(); // Close the statement } else { // Handle query preparation error die('Query preparation failed: ' . $conn->error); } return $latest_usage - $earliest_usage; }); } /** * @param array $department_ids * @return array{quantity:int,products:int,earnings:int,washes:int,water_usage:int} * @throws Exception */ public function getTransactionSummaryForDepartments(string $date, array $department_ids, string $date_to = null): array { global /** @var db $db */ $db; $normalized_department_ids = $this->normalizeDepartmentIds($department_ids); if ($normalized_department_ids === []) { return [ 'quantity' => 0, 'products' => 0, 'earnings' => 0, 'washes' => 0, 'water_usage' => 0, ]; } [$date_start, $date_end] = $this->resolveDateRange($date, $date_to); $department_ids_sql = implode(',', $normalized_department_ids); $escaped_start = $db->escape_string($date_start); $escaped_end = $db->escape_string($date_end); $sql = "SELECT COUNT(DISTINCT o.id) AS quantity, COALESCE(SUM(oi.quantity), 0) AS products, COALESCE(SUM(oi.price * oi.quantity), 0) AS earnings, COUNT(DISTINCT CASE WHEN p.is_wash = 1 THEN o.id END) AS washes FROM orders o JOIN order_items oi ON oi.order_id = o.id LEFT JOIN products p ON p.id = oi.product_id WHERE o.department_id IN ($department_ids_sql) AND o.created_at BETWEEN '$escaped_start' AND '$escaped_end' AND o.deleted_at IS NULL AND oi.deleted_at IS NULL"; $result = $db->query($sql); $row = is_object($result) ? $result->fetch_assoc() : null; return [ 'quantity' => (int)($row['quantity'] ?? 0), 'products' => (int)($row['products'] ?? 0), 'earnings' => (int)round((float)($row['earnings'] ?? 0)), 'washes' => (int)($row['washes'] ?? 0), 'water_usage' => $this->getWaterUsageForDepartments($date, $normalized_department_ids, $date_to), ]; } /** * @param array $department_ids * @return array{completed:int,total:int} * @throws Exception */ public function getBookingSummaryForDepartments(string $date, array $department_ids, string $date_to = null): array { global /** @var db $db */ $db; $normalized_department_ids = $this->normalizeDepartmentIds($department_ids); if ($normalized_department_ids === []) { return [ 'completed' => 0, 'total' => 0, ]; } [$date_start, $date_end] = $this->resolveDateRange($date, $date_to); $department_ids_sql = implode(',', $normalized_department_ids); $escaped_start = $db->escape_string($date_start); $escaped_end = $db->escape_string($date_end); $sql = "SELECT COUNT(*) AS total, COALESCE(SUM(CASE WHEN order_id IS NOT NULL THEN 1 ELSE 0 END), 0) AS completed FROM order_bookings WHERE department IN ($department_ids_sql) AND datetime BETWEEN '$escaped_start' AND '$escaped_end' AND deleted_at IS NULL"; $result = $db->query($sql); $row = is_object($result) ? $result->fetch_assoc() : null; return [ 'completed' => (int)($row['completed'] ?? 0), 'total' => (int)($row['total'] ?? 0), ]; } /** * @param array $department_ids * @param array $product_ids * @return array * @throws Exception */ public function getProductOverviewForDepartments(string $date, array $department_ids, array $product_ids, string $date_to = null): array { $normalized_department_ids = $this->normalizeDepartmentIds($department_ids); $normalized_product_ids = $this->normalizeDepartmentIds($product_ids); $overview = []; foreach ($normalized_product_ids as $product_id) { $quantity = 0; $out_of = 0; foreach ($normalized_department_ids as $department_id) { $quantity += $this->getProductsSoldOnDate($date, $department_id, $product_id, $date_to); $out_of += (int)(new departments_o())->getTotalMaxAddonsInDepartment( [$product_id], $date, $date_to ?? $date, $department_id ); } $overview[$product_id] = [ 'product_id' => (int)$product_id, 'quantity' => (int)$quantity, 'out_of' => (int)$out_of, ]; } return $overview; } /** * @param array $department_ids * @return int * @throws Exception */ public function getWaterUsageForDepartments(string $date, array $department_ids, string $date_to = null): int { $normalized_department_ids = $this->normalizeDepartmentIds($department_ids); $water_usage = 0; foreach ($normalized_department_ids as $department_id) { $water_usage += $this->getTransactionsOnDateWaterUsage($date, $department_id, $date_to); } return $water_usage; } /** * @param array $department_ids * @return array * @throws Exception */ public function getWashTransactionsForDepartments(string $date, array $department_ids, string $date_to = null): array { global /** @var db $db */ $db; $normalized_department_ids = $this->normalizeDepartmentIds($department_ids); if ($normalized_department_ids === []) { return []; } [$date_start, $date_end] = $this->resolveDateRange($date, $date_to); $department_ids_sql = implode(',', $normalized_department_ids); $escaped_start = $db->escape_string($date_start); $escaped_end = $db->escape_string($date_end); $sql = "SELECT DISTINCT o.id, o.department_id, o.created_at FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id WHERE o.department_id IN ($department_ids_sql) AND o.created_at BETWEEN '$escaped_start' AND '$escaped_end' AND o.deleted_at IS NULL AND oi.deleted_at IS NULL AND p.is_wash = 1 ORDER BY o.created_at ASC"; $result = $db->query($sql); if (!is_object($result) || $result->num_rows === 0) { return []; } $rows = []; while ($row = $result->fetch_assoc()) { $rows[] = [ 'id' => (int)($row['id'] ?? 0), 'department_id' => (int)($row['department_id'] ?? 0), 'created_at' => (string)($row['created_at'] ?? ''), ]; } return $rows; } /** * @param array $values * @return array */ private function normalizeDepartmentIds(array $values): array { $normalized = []; foreach ($values as $value) { $id = (int)$value; if ($id > 0) { $normalized[$id] = $id; } } return array_values($normalized); } /** * @return array{0:string,1:string} * @throws Exception */ private function resolveDateRange(string $date, string $date_to = null): array { if ($date_to === null) { $date_to = $date; } $date_start = date('Y-m-d 00:00:00', strtotime($date)); $date_end = date('Y-m-d 23:59:59', strtotime($date_to)); if ($date_start === false || $date_end === false) { throw new Exception('Invalid date range provided'); } return [$date_start, $date_end]; } }