840 lines
35 KiB
PHP
840 lines
35 KiB
PHP
<?php
|
|
|
|
namespace objects;
|
|
|
|
use classes\db;
|
|
use classes\object_property;
|
|
use Exception;
|
|
use traits\db_object_t;
|
|
|
|
class department_daily_reports_o extends db
|
|
{
|
|
use db_object_t;
|
|
|
|
/**
|
|
* The id of the department
|
|
* @var object_property $department_id
|
|
*/
|
|
public object_property $department_id;
|
|
/**
|
|
* The cm3 of water used in the department today
|
|
* @var object_property $water_usage
|
|
*/
|
|
public object_property $water_usage;
|
|
/**
|
|
* The cm3 of water used in the department today
|
|
* @var object_property $water_usage_morning
|
|
*/
|
|
public object_property $water_usage_morning;
|
|
|
|
/**
|
|
* Any notes for the report
|
|
* @var object_property $notes
|
|
*/
|
|
public object_property $notes;
|
|
|
|
/**
|
|
* The user (id) that filled the report
|
|
* @var object_property $filled_by
|
|
*/
|
|
public object_property $filled_by;
|
|
/**
|
|
* The timestamp of when the object was created
|
|
* @var object_property $created_at
|
|
*/
|
|
public object_property $created_at;
|
|
|
|
|
|
public function structure(): void
|
|
{
|
|
$this->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<int|string> $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<int|string> $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<int|string> $department_ids
|
|
* @param array<int|string> $product_ids
|
|
* @return array<int,array{product_id:int,quantity:int,out_of:int}>
|
|
* @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<int|string> $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<int|string> $department_ids
|
|
* @return array<int,array{id:int,department_id:int,created_at:string}>
|
|
* @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<int|string> $values
|
|
* @return array<int>
|
|
*/
|
|
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];
|
|
}
|
|
|
|
}
|