setTable('bookings'); } public function getCustomerBookingsPaginated(int $customer_number, int $page = 1, int $limit = 10, array $order = ['id' => 'DESC'], string $search = null, array $filters = null): array { global /** @var response $response */ $db, $response; // Add the customer number to the filters $filters['customer_number'] = $customer_number; // List the objects with pagination $array = $this->listObjectsWithPagination($page, $limit, $search, $filters, $order); // Get the total number of objects $total = $this->getTotalObjects($search, $filters); $response->paginate($page, $limit, $total); return $array; } public function getDepartmentBookingsUnfulfilledCount(int $department_id): int { global /** @var response $response */ $db, $response; // Check if the count is cached if (redis->get_department_booking_count($department_id)) { $response->add_meta('cached', true); return redis->get_department_booking_count($department_id); } $sql = "SELECT COUNT(*) as count FROM $this->table WHERE department = $department_id AND status = 'pending'"; $result = $db->query($sql); $row = $db->fetch_assoc($result); // Cache the count redis->cache_department_booking_count($department_id, $row['count']); return $row['count']; } public function delete(int $id): void { $this->id = $id; $this->getObjectProperties(); // Set the status to cancelled $this->status->set('cancelled'); // Remove the cache redis->clear_department_booking_count($this->department->value()); } public function getObjectProperties(): void { $this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', true); $this->wash_type = new object_property($this->table, $this->id, 'wash_type', 'string', true); $this->contact_email = new object_property($this->table, $this->id, 'contact_email', 'string', true); $this->reference_number = new object_property($this->table, $this->id, 'reference_number', 'string', true); $this->regNrTraekker = new object_property($this->table, $this->id, 'regNrTraekker', 'string', true); $this->regNrTrailer = new object_property($this->table, $this->id, 'regNrTrailer', 'string', true); $this->washCertificateEmail = new object_property($this->table, $this->id, 'washCertificateEmail', 'string', true); $this->date = new object_property($this->table, $this->id, 'date', 'string', true); $this->department = new object_property($this->table, $this->id, 'department', 'string', true); $this->pickup_bool = new object_property($this->table, $this->id, 'pickup_bool', 'int', true); $this->notes = new object_property($this->table, $this->id, 'notes', 'string', true); $this->washCertificateStatus = new object_property($this->table, $this->id, 'washCertificateStatus', 'string', true); $this->washCertificateUrl = new object_property($this->table, $this->id, 'washCertificateUrl', 'string', true); $this->status = new object_property($this->table, $this->id, 'status', 'string', true); } public function parseBookings(array $listObjectsWithPaginationIfSet): array { // Parse the customer numbers $bookings = $this->parseCustomerNumbers($listObjectsWithPaginationIfSet); return $bookings; } public function parseCustomerNumbers(array $listObjectsWithPaginationIfSet): array { // Parse the customer numbers foreach ( $listObjectsWithPaginationIfSet as $key => $value ) { $listObjectsWithPaginationIfSet[$key]['customer_name'] = (new users_o())->getCustomerName($value['customer_number']); } return $listObjectsWithPaginationIfSet; } public function syncBookings(): array { global /** @var response $response */ $db, $response; $wordpress_bookings_remote = new wordpress_bookings_remote(); // Get all the bookings from the remote API $bookings = $wordpress_bookings_remote->get_all_bookings()['data']['all_wash_bookings']; // Parse the bookings $parsed_bookings = $wordpress_bookings_remote->parse_bookings($bookings); // Remove the cancelled bookings from the $parsed_bookings array $cancelled_bookings = $this->getAllCancelledBookings(); foreach ( $cancelled_bookings as $cancelled_booking ) { // Remove the cancelled booking from the parsed bookings foreach ( $parsed_bookings as $key => $parsed_booking ) { if ((int)$parsed_booking['id'] === (int)$cancelled_booking['id']) { unset($parsed_bookings[$key]); } } } // Sync the bookings foreach ( $parsed_bookings as $booking ) { // Add or update the booking $this->addOrUpdate( $booking['id'], $booking['customer_number'], $booking['wash_type'], $booking['contact_email'], $booking['reference_number'], $booking['regNrTraekker'], $booking['regNrTrailer'], $booking['washCertificateEmail'], $booking['date'], $booking['department'], $booking['pickup_bool'] ? 1 : 0, $booking['notes'], $booking['washCertificateStatus'], $booking['washCertificateUrl'], $booking['status'] ); } return [ "bookings" => count($bookings), "cancelled" => count($cancelled_bookings), "parsed" => count($parsed_bookings), ]; } public function getAllCancelledBookings(): array { global $db; // Get all the cancelled bookings $sql = "SELECT * FROM $this->table WHERE status = 'cancelled' OR washCertificateStatus = 'cancelled'"; $result = $db->query($sql); return $db->fetch_all($result); } public function addOrUpdate(int $id, $customer_number, $wash_type, $contact_email, $reference_number, $regNrTraekker, $regNrTrailer, $washCertificateEmail, $date, $department, $pickup_bool, $notes, $washCertificateStatus, $washCertificateUrl, $status): void { global $db; // Avoid SQL injection $wash_type = $db->escape_string($wash_type); $contact_email = $db->escape_string($contact_email); $reference_number = $db->escape_string($reference_number); $regNrTraekker = $db->escape_string($regNrTraekker); $regNrTrailer = $db->escape_string($regNrTrailer); $washCertificateEmail = $db->escape_string($washCertificateEmail); $date = $db->escape_string($date); // Parse the department name to id $department = $this->getDepartmentIdByLegacyName($department); $notes = $db->escape_string($notes); $washCertificateStatus = $db->escape_string($washCertificateStatus); $washCertificateUrl = $db->escape_string($washCertificateUrl); $status = $db->escape_string($status); // Check if the entry already exists $sql = "SELECT * FROM $this->table WHERE id = $id"; $result = $db->query($sql); if ($db->num_rows($result) === 0) { // Send a department webhook if the booking is new $slack = new slack(); try { $slack->send_department_booking_notification($department, $slack->format_new_booking( $id, $customer_number, $wash_type, $contact_email, $reference_number, $regNrTraekker, $regNrTrailer, $washCertificateEmail, $date, $department, $pickup_bool, $notes, $washCertificateStatus, $washCertificateUrl, $status )); } catch (\Exception $e) { // Log the error $logs = new logs_o(); $logs->add('slack', 0, 3, 0, 'SEND_DEPARTMENT_BOOKING_NOTIFICATION', $e->getMessage()); } } // Create a new record in the database ( Replace the code, if an entry already exists ) $sql = "INSERT INTO $this->table (id, customer_number, wash_type, contact_email, reference_number, regNrTraekker, regNrTrailer, washCertificateEmail, date, department, pickup_bool, notes, washCertificateStatus, washCertificateUrl, status) VALUES ($id, $customer_number, '$wash_type', '$contact_email', '$reference_number', '$regNrTraekker', '$regNrTrailer', '$washCertificateEmail', '$date', '$department', $pickup_bool, '$notes', '$washCertificateStatus', '$washCertificateUrl', '$status') ON DUPLICATE KEY UPDATE customer_number = $customer_number, wash_type = '$wash_type', contact_email = '$contact_email', reference_number = '$reference_number', regNrTraekker = '$regNrTraekker', regNrTrailer = '$regNrTrailer', washCertificateEmail = '$washCertificateEmail', date = '$date', department = '$department', pickup_bool = $pickup_bool, notes = '$notes', washCertificateStatus = '$washCertificateStatus', washCertificateUrl = '$washCertificateUrl', status = '$status'"; $db->query($sql); // Clear the cache redis->clear_department_booking_count($department); } public function getDepartmentIdByLegacyName(string $departmentName): int { // Get the department id by the legacy name $departmentLegacyNames = [ 'køge' => 4, 'taastrup' => 2, 'aarhusc' => 5, 'roskilde' => 6, 'hvidovre' => 1, 'glostrup' => 3, ]; return $departmentLegacyNames[strtolower($departmentName)] ?? 0; } public function add(int $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, string $department, int $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status): void { global $db; // Avoid SQL injection $wash_type = $db->escape_string($wash_type); $contact_email = $db->escape_string($contact_email); $reference_number = $db->escape_string($reference_number); $regNrTraekker = $db->escape_string($regNrTraekker); $regNrTrailer = $db->escape_string($regNrTrailer); $washCertificateEmail = $db->escape_string($washCertificateEmail); $date = $db->escape_string($date); // Parse the department name to id $department = $this->getDepartmentIdByLegacyName($department); $notes = $db->escape_string($notes); $washCertificateStatus = $db->escape_string($washCertificateStatus); $washCertificateUrl = $db->escape_string($washCertificateUrl); $status = $db->escape_string($status); // Create a new record in the database ( Replace the code, if an entry already exists ) $sql = "INSERT INTO $this->table (customer_number, wash_type, contact_email, reference_number, regNrTraekker, regNrTrailer, washCertificateEmail, date, department, pickup_bool, notes, washCertificateStatus, washCertificateUrl, status) VALUES ($customer_number, '$wash_type', '$contact_email', '$reference_number', '$regNrTraekker', '$regNrTrailer', '$washCertificateEmail', '$date', '$department', $pickup_bool, '$notes', '$washCertificateStatus', '$washCertificateUrl', '$status')"; $db->query($sql); // Clear the cache redis->clear_department_booking_count($department); // Get the id of the new record $this->id = $db->insert_id(); } public function checkUnfulfilledBookings(): void { global $db; // Get all the unfulfilled bookings $bookings = $this->listObjectsWithPagination(1, 100000, null, ['status' => 'pending']); // Check if the booking has been fulfilled foreach ( $bookings as $booking ) { if ($this->isCancelled($booking['id'])) { echo "Booking with ID $booking[id] has been cancelled\n"; continue; } // Check if the booking has been fulfilled $fulfilled = $this->checkBookingFulfilled($booking['id']); if (!$fulfilled) { echo "Booking with ID $booking[id] has not been fulfilled\n"; // Send a department webhook if the booking has not been fulfilled $slack = new slack(); try { $slack->send_department_booking_notification($booking['department'], $slack->format_unfulfilled_booking($booking['id'], $booking['customer_number'], $booking['wash_type'], $booking['contact_email'], $booking['reference_number'], $booking['regNrTraekker'], $booking['regNrTrailer'], $booking['washCertificateEmail'], $booking['date'], $booking['department'], $booking['pickup_bool'], $booking['notes'], $booking['washCertificateStatus'], $booking['washCertificateUrl'], $booking['status'])); } catch (\Exception $e) { // Log the error $logs = new logs_o(); $logs->add('slack', 0, 3, 0, 'SEND_DEPARTMENT_BOOKING_NOTIFICATION', $e->getMessage()); } } } } private static function isCancelled(int $id): bool { global $db; // Check if the booking has been cancelled $sql = "SELECT status FROM bookings WHERE id = $id"; $result = $db->query($sql); $row = $db->fetch_assoc($result); return $row['status'] === 'cancelled'; } public function checkBookingFulfilled(int $booking_id): bool { // Check if the booking has a wash certificate $wash_certificate_store = new wash_certificate_store(); return $wash_certificate_store->washCertificateExists($booking_id); } public function completeWashWithoutWashCertificate(int $id): void { global $db; // Set the status to completed $sql = "UPDATE $this->table SET status = 'completed', washCertificateStatus = 'cancelled' WHERE id = $id"; $db->query($sql); } }