Files
api/services/nginx/app/objects/department_time_bookings_entries_o.php
T
Jepp9350 ca71c34b75 Add department time bookings functionality
Implemented management of department time bookings, including API routes, objects, and logic for opening hours, types, and entries. Enhanced the gateway API to handle optional empty recipient handling.
2025-05-13 12:33:44 +02:00

293 lines
12 KiB
PHP

<?php
namespace objects;
use classes\db;
use classes\gatewayapi;
use classes\object_property;
use Exception;
use traits\db_object_t;
class department_time_bookings_entries_o extends db
{
use db_object_t;
public object_property $department; // The department id
public object_property $type; // The booking type id
public object_property $start; // The start timestamp of the start time
public object_property $end; // The end timestamp of the end time
public object_property $note; // The note for the booking
public object_property $reg; // The registration number plate (optional)
public object_property $phone; // The phone number (optional))
public object_property $phone_country_code; // The phone country code (optional)
public object_property $deleted_at; // The deleted at timestamp
public function structure(): void
{
$this->setTable('department_time_bookings_entries');
}
/**
* Convert the object to an array
* @throws Exception If the object is not selected
* @throws Exception If the object is not found
*/
public function asArray(): array
{
self::requireSelected();
return [
'id' => (int)$this->id,
'department' => (int)$this->department->value(),
'type' => (int)$this->type->value(),
'start' => (string)$this->start->value(),
'end' => (string)$this->end->value(),
'note' => $this->note->value(),
'reg' => $this->reg->value(),
'phone' => (int)$this->phone->value(),
'phone_country_code' => (int)$this->phone_country_code->value(),
];
}
/**
* Add a new object to the database
* @param int $department_id The department id
* @param int $type_id The booking type id
* @param int $start The start timestamp of the start time
* @param int $end The end timestamp of the end time
* @param int $phone_country_code The phone country code
* @param int $phone The phone number
* @param null|string $note The note for the booking
* @param null|string $reg The registration number plate (optional)
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(
int $department_id,
int $type_id,
string $start,
string $end,
int $phone_country_code,
int $phone,
?string $note = null,
?string $reg = null,
): void
{
self::preChecks(...func_get_args());
$tmp_id = self::add_object([
'department' => (int)$department_id,
'type' => (int)$type_id,
'start' => $start,
'end' => $end,
'note' => $note,
'reg' => $reg,
'phone' => $phone,
'phone_country_code' => $phone_country_code,
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
// Notify the parties involved
self::notifyBooking(true);
}
/**
* Pre-checks for the add method
* @param int $department_id The department id
* @param int $type_id The booking type id
* @param string $start The start timestamp of the start time
* @param string $end The end timestamp of the end time
* @param int $phone_country_code The phone country code
* @param int $phone The phone number
* @param null|string $note The note for the booking
* @param null|string $reg The registration number plate (optional)
* @return void
* @throws Exception
*/
public static function preChecks(
int $department_id,
int $type_id,
string $start,
string $end,
int $phone_country_code,
int $phone,
?string $note = null,
?string $reg = null,
): void
{
// Check if the type_id exists
$type = new department_time_bookings_types_o();
$type->select($type_id);
$type->requireSelected();
// Check if the type_id belongs to the department
if ((int)$type->department->value() !== (int)$department_id) {
throw new Exception('Type ID does not belong to the department');
}
// Check if the time slot is during the opening hours
$opening_hours = new department_time_bookings_opening_hours_o();
$opening_hours->selectByDepartment((int)$department_id);
if (!$opening_hours->isOpen($start, $end)) {
throw new Exception('The time slot is not during the opening hours.');
}
// Make sure the start time is before the end time
if ($start >= $end) {
throw new Exception('The start time must be before the end time.');
}
// Check if the time slot is already booked
$bookings = new department_time_bookings_entries_o();
$bookings_overlapping = $bookings->getBookingsByDepartmentAndTime((int)$department_id, $start, $end);
if (count($bookings_overlapping) > 0) {
throw new Exception('The time slot is already booked');
}
}
private function getBookingsByDepartmentAndTime(int $department_id, string $start, string $end): array
{
global $db;
$query = "SELECT * FROM {$this->table} WHERE department = ? AND start < ? AND end > ?";
$stmt = $db->prepare($query);
$stmt->bind_param('iss', $department_id, $end, $start);
$stmt->execute();
$result = $stmt->get_result();
$bookings = [];
while ($row = $result->fetch_assoc()) {
$bookings[] = $row;
}
$stmt->close();
return $bookings;
}
public function getObjectProperties(): void
{
$this->department = new object_property($this->table, $this->id, 'department', 'int', true);
$this->type = new object_property($this->table, $this->id, 'type', 'int', true);
$this->start = new object_property($this->table, $this->id, 'start', 'int', true);
$this->end = new object_property($this->table, $this->id, 'end', 'int', true);
$this->note = new object_property($this->table, $this->id, 'note', 'string', true);
$this->reg = new object_property($this->table, $this->id, 'reg', 'string', true);
$this->phone = new object_property($this->table, $this->id, 'phone', 'int', true);
$this->phone_country_code = new object_property($this->table, $this->id, 'phone_country_code', 'int', true);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'int', true);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Notify the parties involved
* @param bool $is_new_booking True if the booking is new, false if it is an update
* @return void
* @throws Exception If the object is not selected
* @throws Exception If the department is not found
*/
public function notifyBooking(bool $is_new_booking): void
{
self::requireSelected();
$department = new departments_o();
$department->select((int)$this->department->value());
$gateway = new gatewayapi();
if ($gateway->isEnabled()) {
// Send the message to the department
$gateway->send(
[...$department->notificationSmsPhoneNumbers()],
($is_new_booking
? self::generateNewBookingMessage(true)
: self::generateUpdateBookingMessage(true)),
true,
);
print_r($department->notificationSmsPhoneNumbers());
// Get the phone numbers of the customer
$customer_phone_numbers = [];
$customer_phone_numbers[] = '' . $this->phone_country_code->value() . $this->phone->value();
print_r($customer_phone_numbers);
// Send the message to the customer
$gateway->send(
[...$customer_phone_numbers],
($is_new_booking
? $this->generateNewBookingMessage(false)
: $this->generateUpdateBookingMessage(false)),
true,
);
}
}
/**
* Generate the message for a new booking
* @param bool $is_to_department True if the message is to the department, false if it is to the customer
* @return string The message
* @throws Exception If the object is not selected
* @throws Exception If the department is not found
*/
public function generateNewBookingMessage(bool $is_to_department = false): string
{
self::requireSelected();
$department = new departments_o();
$department->select((int)$this->department->value());
$type = new department_time_bookings_types_o();
$type->select((int)$this->type->value());
$message = '';
if ($is_to_department) {
$message .= self::addNewline('Din afdeling har modtaget en ny booking');
} else {
$message .= self::addNewline('Bookingbekræftelse');
}
return $this->generateBookingMessageContent($department, $message, $type, $is_to_department);
}
private function addNewline(string $message): string
{
return $message . "\n";
}
/**
* @param departments_o $department
* @param string $message
* @param department_time_bookings_types_o $type
* @param bool $is_to_department
* @return string
* @throws Exception
*/
public function generateBookingMessageContent(departments_o $department, string $message, department_time_bookings_types_o $type, bool $is_to_department): string
{
self::requireSelected();
$message .= self::addNewline('Afdeling: ' . $department->name->value());
$message .= self::addNewline('Tidspunkt: ' . (string)$this->start->value());
$message .= self::addNewline('Type: ' . (string)$type->name->value());
$registration_number = $this->reg->value();
if (!empty($registration_number)) {
$message .= self::addNewline('Registreringsnummer: ' . $registration_number);
}
$message .= self::addNewline('Telefonnummer: +' . $this->phone_country_code->value() . $this->phone->value());
if (!empty($this->note->value())) {
$message .= self::addNewline('Note: ' . $this->note->value());
}
if (!$is_to_department) {
$message .= self::addNewline('For mere information, kontakt venligst afdelingen.');
}
$message .= 'Denne besked er genereret automatisk og kan ikke besvares.';
return $message;
}
/**
* Generate the message for an updated booking
* @param bool $is_to_department True if the message is to the department, false if it is to the customer
* @return string The message
* @throws Exception If the object is not selected
* @throws Exception If the department is not found
*/
public function generateUpdateBookingMessage(bool $is_to_department = false): string
{
self::requireSelected();
$department = new departments_o();
$department->select((int)$this->department->value());
$type = new department_time_bookings_types_o();
$type->select((int)$this->type->value());
$message = $is_to_department
? self::addNewline('Din afdeling har modtaget en opdatering på en booking')
: self::addNewline('Du har modtaget en opdatering på en booking');
return $this->generateBookingMessageContent($department, $message, $type, $is_to_department);
}
}