setTable('departments'); } public function objectChanged(): void { // Clear the cache redis->clear_departments(); } /** * Add a department * @param string $name * @param string $description * @throws Exception If the object was not created successfully */ public function create(string $name, string $description): void { global $db; // Avoid SQL injection $name = $db->escape_string($name); $description = $db->escape_string($description); // Create a new record in the database $sql = "INSERT INTO $this->table (name, description) VALUES ('$name', '$description')"; $db->query($sql); // Get the id of the new record $this->id = $db->insert_id(); // Set the values of the object properties $this->getObjectProperties(); // Clear the cache redis->clear_departments(); } /** * Get the object properties of the department * @throws Exception If the object is not selected */ public function getObjectProperties(): void { self::requireSelected(); $this->name = new object_property($this->table, $this->id, 'name', 'string', true); $this->description = new object_property($this->table, $this->id, 'description', 'string', false); $this->economic_department_id = new object_property($this->table, $this->id, 'economic_department_id', 'int', false); $this->slack_webhook = new object_property($this->table, $this->id, 'slack_webhook', 'string', false); $this->variables = (new department_variables_o())->selectDepartment($this->id); $this->dimension = new object_property($this->table, $this->id, 'dimension', 'int', false); $this->branding = new object_property($this->table, $this->id, 'branding', 'int', false); $this->visible = new object_property($this->table, $this->id, 'visible', 'int', false); $this->longitude = new object_property($this->table, $this->id, 'longitude', 'float', false); $this->latitude = new object_property($this->table, $this->id, 'latitude', 'float', false); $this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false); $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false); } public function edit(int $id, string $name, string $description, int $economic_department_id): void { global $db; $this->id = $id; // Avoid SQL injection $name = $db->escape_string($name); $description = $db->escape_string($description); // If the economic_department_id is 0, set it to null if ($economic_department_id === 0) { $economic_department_id = null; } // Update the record in the database $sql = "UPDATE $this->table SET name = '$name', description = '$description', economic_department_id = " . ($economic_department_id ?? 'NULL') . " WHERE id = $this->id"; $db->query($sql); // Set the values of the object properties $this->getObjectProperties(); // Clear the cache redis->clear_department($id); } public function list($superUser = false): array { global $db; // Check if the departments are cached $departments = redis->get_departments(); // If the departments are not cached, get them from the database if ($departments === null) { $sql = "SELECT * FROM $this->table"; $result = $db->query($sql); $departments = $db->fetch_all($result); // Cache the departments (If it is not empty or null) if (!empty($departments)) { redis->cache_departments($departments); } } // Only show the name, description and id if the user isn't a super user if (!$superUser) { // Map the departments to only show the name, description and id $departments = array_map(function ($department) { return [ 'name' => $department['name'], 'description' => $department['description'], 'id' => $department['id'], 'visible' => $department['visible'], ]; }, $departments); } return $departments; } public function getDepartmentById(int $id, bool $force_all = false): array { global $db; // Check if the department is cached $department = redis->get_department($id); // If the department is not cached, get it from the database if ($department === null || $force_all) { $sql = "SELECT * FROM $this->table WHERE id = $id"; $result = $db->query($sql); $department = $db->fetch_assoc($result); // Cache the department (If it is not empty or null) if (!empty($department)) { redis->cache_department($id, $department); } } return $department; } /** * Get the price of a product in a department * @param int $department_id * @param int $product_id * @param int $price * @return void */ public function setDepartmentProductPrice(int $department_id, int $product_id, int $price): void { global $db; // Check if the record already exists $this->removeDepartmentProductPriceIfExist($department_id, $product_id); // If the price is 0, return if ($price === 0) { return; } // Create a new record in the database $sql = "INSERT INTO product_department_prices (department_id, product_id, price) VALUES ($department_id, $product_id, $price)"; $db->query($sql); } /** * Remove the price of a product in a department * @param int $department_id * @param int $product_id * @return void */ private function removeDepartmentProductPriceIfExist(int $department_id, int $product_id): void { global $db; // Get the price from the database $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id AND product_id = $product_id"; $result = $db->query($sql); if ($result->num_rows > 0) { // Remove the record from the database $sql = "DELETE FROM product_department_prices WHERE department_id = $department_id AND product_id = $product_id"; $db->query($sql); } } public function getDepartmentProductPrices(int $department_id): array { global $db; $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id"; $result = $db->query($sql); return $db->fetch_all($result); } public function getDepartmentName(int $department): string { global $db; // Check if the department name is cached $name = redis->get_department_name($department); // If the department name is not cached, get it from the database if ($name === null) { try { $this->id = $department; $this->getObjectProperties(); $name = $this->name->value(); // Cache the department name (If it is not empty or null) if (!empty($name)) { redis->cache_department_name($department, $name); } } catch (Exception $e) { $name = 'Unable to get department name: ' . $department; } return $name; } return redis->get_department_name($department) ?? 'Unable to get department name: ' . $department; } /** * Get the Stripe terminal location department variable * @note This is a link to the department_variables_o::getVariable(stripe_terminal_location) method * @return string The Stripe terminal location id * @throws Exception If the department variable is not set * @throws Exception If the department is not selected */ public function getStripeTerminalLocation(): string { self::requireSelected(); if (!$this->variables->isSet('stripe_terminal_location')) { throw new Exception('The Stripe terminal location is not set for the department.'); } return $this->variables->getVariable('stripe_terminal_location'); } /** * Check if the Stripe terminal location variable is set * @note This is a link to the department_variables_o::isSet(stripe_terminal_location) method * @return bool true If the Stripe terminal location is set * @throws Exception If the department is not selected */ public function isStripeTerminalLocationSet(): bool { self::requireSelected(); return $this->variables->isSet('stripe_terminal_location'); } /** * Set the Stripe terminal location department variable * @note This is a link to the department_variables_o::set(stripe_terminal_location) method * @param string $location The Stripe terminal location id * @throws Exception If the department is not selected * @throws Exception If the department variable is not updated successfully */ public function setStripeTerminalLocation(string $location): void { self::requireSelected(); $this->variables->set('stripe_terminal_location', $location); } /** * Get the Stripe terminal readers * @note This is a link to the stripe_endpoint_readers::listLocation method * @throws Exception If the department is not selected * @throws Exception If the Stripe terminal location is not set for the department */ public function getStripeTerminalReaders(): \Stripe\Collection { self::requireSelected(); if (!$this->variables->isSet('stripe_terminal_location')) { throw new Exception('The Stripe terminal location is not set for the department.'); } return (new stripe())->readers->listLocation($this->variables->getVariable('stripe_terminal_location')); } /** * Convert the object to an array * @param array|null $options The options for the conversion (e.g. ['slack_webhook' => true]) * @return array The object as an array * @throws Exception If the object is not selected */ public function asArray(array $options = null): array { self::requireSelected(); $tmp = [ 'id' => (int)$this->id, 'name' => (string)$this->name->value(), 'description' => (string)$this->description->value(), 'economic_department_id' => (int)$this->economic_department_id->value(), 'dimension' => (int)$this->dimension->value(), 'branding' => (int)$this->branding->value(), 'longitude' => (float)$this->longitude->value(), 'latitude' => (float)$this->latitude->value(), 'created_at' => (string)$this->created_at->value(), 'updated_at' => (string)$this->updated_at->value(), ]; // Check if the webhook should be included if ($options && $options['slack_webhook']) { $tmp['slack_webhook'] = (string)$this->slack_webhook->value(); } return $tmp; } /** * Check if the Stripe is configured for the department * @note This is a link to the department_variables_o::isSet(stripe_terminal_location) method * @return bool true If the Stripe is configured * @throws Exception If the department is not selected */ public function isStripeConfigured(): bool { self::requireSelected(); if ($this->variables->isSet('stripe_terminal_location')) { return true; } return false; } /** * @throws Exception If the department is not selected * @throws Exception If the branding is not set * @throws Exception If the branding is not found */ public function getBranding(): branding_o { self::requireSelected(); return (new branding_o())->select($this->branding->value()); } /** * Get the phone numbers for the SMS notification * @note This is a link to the department_notification_sms_o::getDepartmentActivePhoneNumbers method * @throws Exception If the department is not selected */ public function notificationSmsPhoneNumbers(): array { self::requireSelected(); return (new department_notification_sms_o())->getDepartmentActivePhoneNumbers($this->id); } /** * @throws Exception */ public function isModuleTimeBookingsEnabled(): bool { self::requireSelected(); return (bool)$this->variables->getVariable('bookingsystem_time_based_enabled'); } /** * Select a department by its name (case-sensitive, exact match) * @throws Exception If the department selection fails or the department is not found * @example "Hvidovre" */ public function selectByName(string $departmentName): self { $result = self::getFieldsWhere([ 'name' => $departmentName, ], [ 'id', ]); if (empty($result)) { throw new Exception('Department not found: ' . $departmentName); } $this->selectId($result[0]['id']); return $this; } public function selectId(int $department_id): self { $this->id = $department_id; $this->getObjectProperties(); return $this; } /** * @return products_o[] An array of all products in the department's categories * @throws Exception */ public function getAllProductInDepartmentCategories(): array { self::requireSelected(); // Get all the categories associated $department_categories = (new department_categories_o())->getDepartmentCategoryObjects($this->id); // Use array_reduce to combine both loops into a single operation $products = array_reduce($department_categories, function ($products, $department_category) { return array_merge($products, $department_category->getCategory()->getProducts()); }, []); // Remove duplicate products return array_unique($products, SORT_REGULAR); } }