Refactor: migrate files

This commit is contained in:
Jepp9350
2025-01-29 14:27:44 +01:00
parent 4c74dd7995
commit 707df910b0
142 changed files with 371 additions and 9 deletions
+3
View File
@@ -0,0 +1,3 @@
FROM mysql:8.0
# Set working directory
WORKDIR /var/lib/mysql
+14
View File
@@ -0,0 +1,14 @@
# NGINX Dockerfile
FROM nginx:1.27.3-alpine
# Import the configuration file for Nginx
COPY nginx.conf /etc/nginx/nginx.conf
# Copy the contents of the /app directory to the /var/www/html directory
COPY /app /var/www/html
# Expose port 80 (HTTP) and 443 (HTTPS)
EXPOSE 80 443
# Start Nginx when the container starts
CMD ["nginx", "-g", "daemon off;"]
+14
View File
@@ -0,0 +1,14 @@
# php -- BEGIN cPanel-generated handler, do not edit
# Set the “ea-php80” package as the default “PHP” programming language.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
<IfModule mime_module>
AddHandler application/x-httpd-ea-php80 .php .php8 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit
@@ -0,0 +1,136 @@
<?php
namespace classes;
use Exception;
use interfaces\authentication_i;
use objects\plate_scanners_o;
use objects\tokens_o;
use objects\users_o;
class authentication implements authentication_i
{
public function authenticate(int $customer_number, string $password): bool
{
// Get the customer from the database
$customer = (new users_o())->getUserByCustomerNumber($customer_number);
// Check if the customer exists
if (!$customer->exists()) {
return false;
}
// Check if the customer has a password
if (!$customer->hasPassword()) {
return false;
}
// Check if the password is correct
if (!$this->match_passwords($password, $customer->getPassword())) {
return false;
}
return true;
}
public function match_passwords($password, $hash): bool
{
// Compare the password with the hash
return password_verify($password, $hash);
}
public function create_token(int $customer_number): string
{
// Create a token
$token = bin2hex(random_bytes(32));
// Get the user id
$user_id = (new users_o())->getUserByCustomerNumber($customer_number)->id;
// Save the token in the database
(new tokens_o())->create($user_id, $token, 'AUTH_TOKEN');
return $token;
}
public function create_employee_token(int $employee_id): string
{
// Create a token
$token = bin2hex(random_bytes(32));
// Save the token in the database
(new tokens_o())->create($employee_id, $token, 'AUTH_TOKEN');
return $token;
}
public function validate_token(string $token): bool
{
// Get the token from the database
$token = (new tokens_o())->getToken($token);
// Check if the token exists
if (!$token->id) {
return false;
}
return true;
}
/**
* @throws Exception
*/
public function get_user(): users_o|false
{
/**
* Get the user from the token
*/
// Get the token from the headers
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
return false;
}
$token = $headers['Authorization'];
// Strip the Bearer prefix
$token = str_replace('Bearer ', '', $token);
// Get the token from the database
$token = (new tokens_o())->getToken($token);
// Check if the token exists
if (!$token->id) {
return false;
}
// Get the user from the database
return (new users_o())->getUserById($token->user_id->value());
}
public function get_plate_scanner(): plate_scanners_o|false
{
// Get the token from the headers
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
return false;
}
$token = $headers['Authorization'];
// Strip the Bearer prefix
$token = str_replace('Bearer ', '', $token);
// Get the token from the database
$token = (new plate_scanners_o())->getPlateScannerByApiKey($token);
// Check if the token exists
if (!isset($token->id)) {
return false;
}
// Get the plate scanner from the database
return $token;
}
public function hash_password($password): string
{
// Hash the password
return password_hash($password, PASSWORD_DEFAULT);
}
public function authenticateEmployee(int $user_id, string $password): bool
{
// Get the employee from the database
$employee = (new users_o())->getUserById($user_id);
// Check if the employee exists
if (!$employee->exists()) {
return false;
}
// Check if the password is correct
if (!$this->match_passwords($password, $employee->getPassword())) {
return false;
}
return true;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace classes;
use Exception;
use mysqli;
class db
{
public mysqli $conn;
private string $host;
private string $user;
private string $password;
private string $database;
public function __construct(array $config)
{
$this->host = $config['host'];
$this->user = $config['user'];
$this->password = $config['password'];
$this->database = $config['database'];
}
public function connect(): void
{
global $response;
try {
$this->conn = new mysqli($this->host, $this->user, $this->password, $this->database);
} catch (Exception $e) {
$response->internal_server_error($e->getMessage());
}
}
public function escape_string(string $string): string
{
return $this->conn->real_escape_string($string);
}
public function close(): void
{
$this->conn->close();
}
public function get(string $table, int $id)
{
$sql = "SELECT * FROM $table WHERE id = $id";
$result = $this->query($sql);
return $this->fetch_assoc($result);
}
public function query(string $sql): \mysqli_result|bool
{
// If the connection is not established, connect
return $this->conn->query($sql);
}
public function fetch_assoc($result)
{
return $result->fetch_assoc();
}
public function list_objects(string $table): array
{
$sql = "SELECT * FROM $table";
$result = $this->query($sql);
return $this->fetch_all($result);
}
public function fetch_all($result)
{
return $result->fetch_all(MYSQLI_ASSOC);
}
public function list_objects_paginated(string $table, int $page, int $limit): array
{
$offset = ($page - 1) * $limit;
$sql = "SELECT * FROM $table LIMIT $limit OFFSET $offset";
$result = $this->query($sql);
return $this->fetch_all($result);
}
public function count_objects(string $table): int
{
$sql = "SELECT COUNT(*) FROM $table";
$result = $this->query($sql);
return $result->fetch_row()[0];
}
public function add_object(string $table, array $data): void
{
$columns = implode(', ', array_keys($data));
$values = implode("', '", array_values($data));
$sql = "INSERT INTO $table ($columns) VALUES ('$values')";
$this->query($sql);
}
public function insert_id(): int
{
return $this->conn->insert_id;
}
public function conn(): mysqli
{
return $this->conn;
}
public function prepare(string $sql): false|\mysqli_stmt
{
return $this->conn->prepare($sql);
}
public function num_rows(\mysqli_result|bool $result): int|string
{
return $result->num_rows;
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace classes;
require_once WD . '/modules/economic/endpoints/economic_orders_endpoint.php';
use endpoints\economic_orders_endpoint;
use interfaces\economic_i;
class economic implements economic_i
{
/**
* Any endpoints reached by the /orders endpoint
* @var economic_orders_endpoint
*/
public economic_orders_endpoint $orders;
public function __construct()
{
$this->orders = new economic_orders_endpoint();
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace classes;
use interfaces\encrypt_i;
class encrypt implements encrypt_i
{
public function encrypt(string $data): string
{
// Debug:
return $data;
// Encrypt data
global $ENCRYPTION_KEY;
// Use AES 256 encryption
$cipher = "aes-256-cbc";
// Use the encryption key
$options = 0;
// Get the initialization vector
$iv_length = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($iv_length);
// Use the first 16 bytes of the initialization vector
$iv = substr($iv, 0, 16);
// Encrypt the data
$encrypted = openssl_encrypt($data, $cipher, $ENCRYPTION_KEY, $options, $iv);
// Save the initialization vector for decryption
return $iv . $encrypted;
}
public function decrypt(string $data): string
{
// Debug:
return $data;
// Decrypt data
global $ENCRYPTION_KEY;
// Use AES 256 encryption
$cipher = "aes-256-cbc";
// Use the encryption key and initialization vector
$options = 0;
// Get the initialization vector
$iv_length = openssl_cipher_iv_length($cipher);
$iv = substr($data, 0, $iv_length);
// Get the encrypted data
$encrypted = substr($data, $iv_length);
// Decrypt the data
return openssl_decrypt($encrypted, $cipher, $ENCRYPTION_KEY, $options, $iv);
}
}
@@ -0,0 +1,58 @@
<?php
namespace classes;
use interfaces\language_pack_i;
class language_packs
{
/**
* The language packs
* @var array
*/
protected array $language_packs = [];
protected string $default_language = 'en_us';
public function __construct()
{
$this->loadLanguagePacks();
}
/**
* Load the language packs
*/
protected function loadLanguagePacks(): void
{
$files = glob('languages/*.php');
foreach ( $files as $file ) {
require_once $file;
$class = 'languages\\' . str_replace('.php', '', basename($file));
$this->language_packs[] = new $class();
}
}
/**
* Get the default language pack
* @return language_pack_i The default language pack
*/
public function getDefaultLanguagePack(): object
{
return $this->getLanguagePack($this->default_language);
}
/**
* Get the language pack for a language
* @param string $language The language code of the language pack
* @return language_pack_i The language pack
*/
public function getLanguagePack(string $language): object
{
foreach ( $this->language_packs as $language_pack ) {
if ($language_pack->getLanguage() === $language) {
return $language_pack;
}
}
return $this->getLanguagePack($this->default_language);
}
}
@@ -0,0 +1,75 @@
<?php
namespace classes;
class object_property
{
public int $id; // The table of the objects in the database (e.g. users)
private string $table; // The id of the object in the database
private string $column; // The column name of the field in the database table (e.g. id, name, email)
private string $type; // The data type of the field in the database table (e.g. int, varchar, text)
private bool $required; // Whether the field is required or not
private mixed $default; // The default value of the field
public function __construct(string $table, int $id, string $column, string $type, bool $required = false, mixed $default = null)
{
$this->table = $table;
$this->id = $id;
$this->column = $column;
$this->type = $type;
$this->required = $required;
$this->default = $default;
}
public function __toString(): string
{
// Return the value of the field in the database table
return $this->value();
}
/**
* Get the value of the field in the database table
* @return mixed The value of the field in the database table
*/
public function value(): mixed
{
// Get the value of the field in the database table
global $db;
$sql = "SELECT $this->column FROM $this->table WHERE id = $this->id";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
return $row[$this->column];
}
/**
* Set the value of the field in the database table
* @param mixed $value The value of the field in the database table
*/
public function set(mixed $value): void
{
// Set the value of the field in the database table
global /** @var db $db */
$db;
// If the value is null, set it to null
if ($value === null) {
$sql = "UPDATE $this->table SET $this->column = NULL WHERE id = $this->id";
} // If the value is a string, escape it
else {
$value = $db->escape_string($value);
$sql = "UPDATE $this->table SET $this->column = '$value' WHERE id = $this->id";
}
$db->query($sql);
}
/**
* Nullify the value of the field in the database table
*/
public function nullify(): void
{
// Set the value of the field in the database table to null
global /** @var db $db */
$db;
$sql = "UPDATE $this->table SET $this->column = NULL WHERE id = $this->id";
$db->query($sql);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace classes;
use interfaces\ratelimit_i;
use objects\ratelimit_o;
class ratelimit implements ratelimit_i
{
private int $limit; // The number of requests allowed in the time period
private int $time; // The time period in seconds
public function __construct(int $defaultLimit, int $defaultTime)
{
$this->limit = $defaultLimit;
$this->time = $defaultTime;
}
public function enforceIP(string $ip): bool
{
global $response;
$ratelimit = (new ratelimit_o())->getOrCreateRateLimitByIp($ip);
if ($ratelimit->count->value() >= $this->limit) {
$response->rate_limit_exceeded();
}
$ratelimit->increment($ratelimit->id, 1);
return true;
}
}
+382
View File
@@ -0,0 +1,382 @@
<?php
namespace classes;
use interfaces\redis_i;
class redis implements redis_i
{
use \traits\redis_t;
public function __construct()
{
global $REDIS_CONFIG;
// Make sure the Redis configuration is set
if (!isset($REDIS_CONFIG)) {
throw new \Exception('Redis configuration is not set');
}
// Apply the Redis configuration
$this->redis_host = $REDIS_CONFIG['host'];
$this->redis_database = $REDIS_CONFIG['database'];
$this->redis_password = $REDIS_CONFIG['password'];
}
/**
* @inheritDoc
*/
public function cache_department_booking_count(int $department_id, int $count): redis_i
{
// Cache the department booking count
$this->set('department_booking_count_' . $department_id, $count);
return $this;
}
/**
* @inheritDoc
*/
public function get_department_booking_count(int $department_id): int|null
{
// Get the department booking count
return $this->get('department_booking_count_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department_booking_count(int $department_id): redis_i
{
// Clear the department booking count
$this->delete('department_booking_count_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department_webhook(int $department_id, string $webhook): self
{
// Cache the department webhook
$this->set('department_webhook_' . $department_id, $webhook);
return $this;
}
/**
* @inheritDoc
*/
public function get_department_webhook(int $department_id): string|null
{
// Get the department webhook
return $this->get('department_webhook_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department_webhook(int $department_id): self
{
// Clear the department webhook
$this->delete('department_webhook_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department_name(int $department_id, string $department_name): self
{
// Cache the department name (If it is not empty or null)
if (empty($department_name)) {
$department_name = 'IS_EMPTY_OR_NULL';
return $this;
}
// Set the department's name in the cache
if ($this->get_department_name($department_id) === null) {
$this->set('department_' . $department_id, json_encode(['name' => $department_name]
));
};
return $this;
}
/**
* @inheritDoc
*/
public function get_department_name(int $department_id): string|null
{
// Get the department name
$tmp_department = $this->get('department_' . $department_id);
if ($tmp_department === 'IS_EMPTY_OR_NULL' || $tmp_department === null) {
return null;
}
// Return the department name
return json_decode($tmp_department, true)->name;
}
/**
* @inheritDoc
*/
public function clear_department_name(int $department_id): self
{
// Clear the department name
$this->delete('department_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_economic_customer_discount_percentage(int $user_id, int $discount_percentage): self
{
// Cache the economic customer discount percentage
$this->set('users_' . $user_id . '_economic_customer_discount_percentage', $discount_percentage);
return $this;
}
/**
* @inheritDoc
*/
public function get_economic_customer_discount_percentage(int $user_id): int|null
{
// Get the economic customer discount percentage
return $this->get('users_' . $user_id . '_economic_customer_discount_percentage');
}
/**
* @inheritDoc
*/
public function clear_economic_customer_discount_percentage(int $user_id): self
{
// Clear the economic customer discount percentage
$this->delete('users_' . $user_id . '_economic_customer_discount_percentage');
return $this;
}
/**
* @inheritDoc
*/
public function cache_customer_notes(int $user_id, array $customer_notes): self
{
// Cache the customer notes
$this->set_array('users_' . $user_id . '_customer_notes', $customer_notes);
return $this;
}
/**
* @inheritDoc
*/
public function get_customer_notes(int $user_id): array|null
{
// Get the customer notes
return $this->get_array('users_' . $user_id . '_customer_notes');
}
/**
* @inheritDoc
*/
public function clear_customer_notes(int $user_id): self
{
// Clear the customer notes
$this->delete('users_' . $user_id . '_customer_notes');
return $this;
}
/**
* @inheritDoc
*/
public function add_log(string $module, string $department, int $type, int $user_id, string $action, string $message): redis_i
{
// Get the current timestamp
$timestamp = strtotime('now');
// Get unique id for the log
$log_id = uniqid();
// Add the log to the cache
$this->set_array('log_' . $log_id, [
'id' => $log_id,
'module' => $module,
'department' => $department,
'type' => $type,
'user_id' => $user_id,
'action' => $action,
'message' => $message,
'timestamp' => $timestamp
]);
return $this;
}
/**
* @inheritDoc
*/
public function get_logs(): array|null
{
// Get all logs from the cache
return $this->get_arrays('log_*');
}
/**
* @inheritDoc
*/
public function clear_logs(): self
{
// Clear all logs from the cache
$keys = $this->get_keys('log_*');
foreach ( $keys as $key ) {
$this->delete($key);
}
return $this;
}
/**
* @inheritDoc
*/
public function get_department(int $department_id): array|null
{
// Get the department
return $this->get_array('department_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department(int $department_id): self
{
// Clear the departments
$this->clear_departments();
return $this;
}
/**
* @inheritDoc
*/
public function clear_departments(): self
{
// Clear all departments from the cache
$this->delete('departments');
$keys = $this->get_keys('department_*');
foreach ( $keys as $key ) {
$this->delete($key);
}
return $this;
}
/**
* @inheritDoc
*/
public function get_departments(): array|null
{
// Get all departments from the cache
return $this->get_array('departments');
}
/**
* @inheritDoc
*/
public function cache_departments(array $departments): self
{
// Cache the departments
foreach ( $departments as $department ) {
$this->cache_department($department['id'], $department);
}
redis->set_array('departments', $departments);
return $this;
}
/**
* @inheritDoc
*/
public function cache_department(int $department_id, array $department): self
{
// Cache the department
$this->set_array('department_' . $department_id, $department);
return $this;
}
/**
* @inheritDoc
*/
public function get_log_count(): int
{
// Get the log count
return $this->count_keys('log_*');
}
/**
* @inheritDoc
*/
public function cache_token(string $token, array $row): self
{
// Cache the token
$this->set_array('token_' . $token, $row);
return $this;
}
/**
* @inheritDoc
*/
public function get_token(string $token): array|null
{
// Get the token
return $this->get_array('token_' . $token);
}
/**
* @inheritDoc
*/
public function clear_token(string $token): self
{
// Clear the token
$this->delete('token_' . $token);
return $this;
}
/**
* @inheritDoc
*/
public function get_last_crond_run(string $task): int|null
{
// Get the last cron task
return $this->get('last_cron_task_' . $task) ?? null;
}
/**
* @inheritDoc
*/
public function set_last_crond_run(string $task, int $time): self
{
// Set the last cron task
$this->set('last_cron_task_' . $task, $time);
return $this;
}
/**
* @inheritDoc
*/
public function cache_user_id_from_customer_number(int $customer_number, int $user_id): self
{
// Cache the user id from customer number
$this->set('user_id_from_customer_number_' . $customer_number, $user_id);
return $this;
}
/**
* @inheritDoc
*/
public function get_user_id_from_customer_number(int $customer_number): int|null
{
// Get the user id from customer number
return $this->get('user_id_from_customer_number_' . $customer_number);
}
/**
* @inheritDoc
*/
public function clear_user_id_from_customer_number(int $customer_number): self
{
// Clear the user id from customer number
$this->delete('user_id_from_customer_number_' . $customer_number);
return $this;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace classes;
class request
{
}
+166
View File
@@ -0,0 +1,166 @@
<?php
namespace classes;
use interfaces\response_i;
use JetBrains\PhpStorm\NoReturn;
class response implements response_i
{
private bool $matching_route_found = false;
private array $data = [];
private array $meta = [];
private array $includes = [];
#[NoReturn] public function success(mixed $data, int $status = null): void
{
$this->response(true, $data, $status);
}
#[NoReturn] public function response(bool $success, mixed $data, int $status = null): void
{
global $DEBUG;
header('Content-Type: application/json');
if ($status) {
http_response_code($status);
} else {
http_response_code($success ? 200 : 400);
}
// If the data isn't an array, convert it to an array
if (!is_array($data)) {
$data = ['message' => $data];
}
// If the debug mode is enabled, add the debug data to the response
if ($DEBUG) {
$this->add_include('debug', [
'memory' => memory_get_usage(),
'time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
'data' => $this->get_data()
]);
}
echo json_encode([
'success' => $success,
'data' => $data,
'meta' => $this->meta,
'includes' => $this->includes
]);
exit;
}
public function add_include(string $string, array $dataArray): void
{
$this->add_included($string, $dataArray);
}
public function add_included(string $key, mixed $value): void
{
$this->includes[$key] = $value;
}
public function get_data(): array
{
return $this->data;
}
#[NoReturn] public function not_found(): void
{
$this->error('Not found', 404);
}
#[NoReturn] public function error(mixed $data, int $status = null): void
{
$this->response(false, $data, $status);
}
#[NoReturn] public function rate_limit_exceeded(): void
{
$this->error('Rate limit exceeded', 429);
}
public function matching_route_found(): void
{
$this->matching_route_found = true;
}
#[NoReturn] public function method_not_allowed(): void
{
$this->error('Method not allowed', 405);
}
#[NoReturn] public function internal_server_error($error): void
{
$this->error('Internal server error' . ($error ? ': ' . $error : ''), 500);
}
public function paginate(int $page, int $per_page, int $total, string $search = null, array $filters = null, array $order = null): void
{
// If the total is 0, return 1 page, 0 total
if ($total === 0) {
$total = 1;
}
$this->add_meta('pagination', [
'page' => $page,
'per_page' => $per_page,
'total' => $total,
'search' => $search,
'filters' => $filters,
'order' => $order
]);
}
public function add_meta(string $key, mixed $value): void
{
$this->meta[$key] = $value;
}
public function is_matching_route_found(): bool
{
return $this->matching_route_found;
}
public function add_debug(mixed $data): void
{
$this->add_data('debug', $data);
}
public function add_data(string $key, mixed $value): void
{
$this->data[$key] = $value;
}
public function getRequestParameter(string $key): string|null
{
// Get the request data if the method is POST, PUT or PATCH
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
$data = json_decode(file_get_contents('php://input'), true);
}
// Get the request data if the method is GET, DELETE or OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$data = $_GET;
}
// Return the data
return $data[$key] ?? null;
}
public function parseFilters(?string $filters): array|null
{
if ($filters) {
$filters = explode(',', $filters);
$temp = [];
foreach ( $filters as $filter ) {
$filter = explode(':', $filter);
$temp[$filter[0]] = $filter[1];
}
$filters = $temp;
}
if ($filters) {
return $filters;
}
return null;
}
public function add_debug_list(string $list_name, int $key, $value): void
{
$this->add_data($list_name, [$key => $value]);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace classes;
class router
{
private string $url;
private string $method;
private array $routes;
private array $routeClasses; // Contains the classes that have the route_t trait
public function __construct()
{
$this->url = $_SERVER['REQUEST_URI'] ?? ($argv[1] ?? '/'); // Get the URL (Or the first argument if it's a CLI request)
$this->method = $_SERVER['REQUEST_METHOD'] ?? ($argv[2] ?? 'GET'); // Get the method (Or the second argument if it's a CLI request)
$this->routes = [];
$this->routeClasses = [];
}
public function add($route, $method, $function): void
{
$this->routes[] = ['route' => $route, 'method' => $method, 'function' => $function];
}
public function auto_load_routes(string $path): void
{
global $response;
$files = scandir($path);
foreach ( $files as $file ) {
if ($file == '.' || $file == '..') {
continue;
}
require_once $path . '/' . $file;
}
// Get all the classes in the files with the route_t trait
$classes = get_declared_classes();
foreach ( $classes as $class ) {
if (in_array('traits\route_t', class_uses($class))) {
$this->routeClasses[] = $class;
}
}
// Try to run the routes, if there is an error, catch it and send an internal server error response
try {
$this->run();
} catch (\Exception $e) {
$response->internal_server_error($e->getMessage());
}
}
public function run(): void
{
foreach ( $this->routeClasses as $class ) {
$route = new $class();
// Add the routes to the router
$route->run();
}
$this->routeRequest();
}
private function routeRequest(): void
{
global $response;
$matching_route_found = false;
foreach ( $this->routes as $route ) {
if ($this->doesRouteMatchCurrent($route['route']) && $route['method'] == $this->method) {
$route['function']();
$matching_route_found = true;
}
}
if ($matching_route_found) {
$response->matching_route_found();
} else {
$response->not_found();
}
}
private function doesRouteMatchCurrent($route): bool
{
// Check if the route matches the current URL or if it matches the regex pattern
// Remove the query string
$this->url = explode('?', $this->url)[0];
// Exact match
if ($route == $this->url) {
return true;
}
// Regex
$route = str_replace('/', '\/', $route);
$route = preg_replace('/{[a-zA-Z0-9]+}/', '([a-zA-Z0-9]+)', $route);
if (preg_match('/^' . $route . '$/', $this->url)) {
return true;
}
// None of the matches were found
return false;
}
public function ERROR_HANDLER($callback): void
{
try {
$callback();
} catch (\Exception $e) {
global $response;
$response->internal_server_error($e->getMessage());
}
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace classes;
use traits\session_t;
class session
{
use session_t;
}
+139
View File
@@ -0,0 +1,139 @@
<?php
namespace classes;
use GuzzleHttp\Client;
use interfaces\notification_i;
use objects\departments_o;
use objects\users_o;
use traits\notification_t;
class slack implements notification_i
{
use notification_t;
/**
* @inheritdoc
* @throws \Exception
*/
public function send_department_booking_notification(int $department_id, $message): self
{
// Get the departments webhook
$webhook = self::get_department_webhook($department_id);
// Check if the webhook is empty
if (empty($webhook)) {
throw new \Exception('Department webhook is empty');
}
// Send the notification to the department
self::add_log(self::send_webhook_message($message, $webhook));
return $this;
}
private function get_department_webhook(int $department_id): string|null
{
// Check if the department webhook is cached
$webhook = redis->get_department_webhook($department_id);
// If the department webhook is not cached, get it from the database
if ($webhook === null) {
$department = new departments_o();
$department->id = $department_id;
$department->getObjectProperties();
$webhook = $department->slack_webhook->value();
// Cache the department webhook (If it is not empty or null)
if (!empty($webhook)) {
redis->cache_department_webhook($department_id, $webhook);
}
return null;
}
return redis->get_department_webhook($department_id);
}
/**
* Send a message to a slack webhook
* @param string $message
* @param string $webhook
* @return string The response from the webhook, unparsed
*/
protected function send_webhook_message(string $message, string $webhook): string
{
global $DEBUG;
try {
// Initialize Guzzle client
$client = new Client();
// Prepare the payload for the webhook
$payload = [
'text' => $message
];
// Send POST request to the webhook
$response = $client->post($webhook, [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode($payload),
'verify' => ($DEBUG === false) // Disable SSL verification (Only in development)
]);
// Return success message
return 'Message sent successfully. Response: ' . $response->getBody();
} catch (\Exception $e) {
// Handle any errors that occur
return 'Failed to send message: ' . $e->getMessage();
}
}
public function format_new_booking($id, $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, int $department, $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status): string
{
// Get the department name
$department_name = (new departments_o())->getDepartmentName($department);
// Get the customer name
$customer_name = (new users_o())->getCustomerName($customer_number);
// Format the message
return "*New booking created* ( ID: " . $id . " )\n"
. "Customer: $customer_name\n"
. "Customer number: $customer_number\n"
. "Wash type: $wash_type\n"
. "Contact email: $contact_email\n"
. "Reference number: $reference_number\n"
. "RegNr Traekker: $regNrTraekker\n"
. "RegNr Trailer: $regNrTrailer\n"
. "Wash certificate email: $washCertificateEmail\n"
. "Date: $date\n"
. "Department: $department_name\n"
. "Pickup: $pickup_bool\n"
. "Notes: $notes\n"
. "Status: $status";
}
public function format_unfulfilled_booking($id, $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, int $department, $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status): string
{
// Get the department name
$department_name = (new departments_o())->getDepartmentName($department);
// Get the customer name
$customer_name = (new users_o())->getCustomerName($customer_number);
// Format the message
return "Unfulfilled booking from " . $date . "\n"
. "ID: $id\n"
. "Customer: $customer_name\n"
. "Customer number: $customer_number\n"
. "Wash type: $wash_type\n"
. "Contact email: $contact_email\n"
. "Reference number: $reference_number\n"
. "RegNr Traekker: $regNrTraekker\n"
. "RegNr Trailer: $regNrTrailer\n"
. "Wash certificate email: $washCertificateEmail\n"
. "Date: $date\n"
. "Department: $department_name\n"
. "Pickup: $pickup_bool\n"
. "Notes: $notes\n"
. "Status: $status";
}
public function send_message(string $string): void
{
global $SLACK_DEFAULT_WEBHOOK;
// Send the message to the slack webhook
self::add_log(self::send_webhook_message($string, $SLACK_DEFAULT_WEBHOOK));
}
}
@@ -0,0 +1,34 @@
<?php
namespace classes;
use interfaces\minio_wash_certificates_i;
use traits\minio_t;
class wash_certificate_store implements minio_wash_certificates_i
{
use minio_t;
public function __construct()
{
self::setBucket('truckwashdev'); // Change this to washcertificates when in production
}
/**
* @inheritDoc
*/
public function washCertificateExists(string $certificate_id): bool
{
// Check if the file exists
$objects = self::getS3Client()->listObjects([
'Bucket' => self::getBucket(),
'Prefix' => 'wash_certificate_' . $certificate_id . '.pdf'
]);
return count($objects['Contents'] ?? []) > 0;
}
public function getWashCertificateDownload(int $id): string
{
return self::getPresignedUrl('wash_certificate_' . $id . '.pdf');
}
}
@@ -0,0 +1,160 @@
<?php
namespace classes;
use interfaces\wordpress_bookings_remote_i;
use traits\wordpress_api_object_t;
class wordpress_bookings_remote implements wordpress_bookings_remote_i
{
use wordpress_api_object_t;
protected array $booking_cache = [];
protected wash_certificate_store $wash_certificate_store;
public function __construct()
{
// Run wordpress_api_object_t constructor
$this->wordpress_api_object_t_construct();
$this->wash_certificate_store = new wash_certificate_store();
}
/**
* @inheritDoc
*/
public function get_all_wash_ids(): array
{
// Get all the wash IDs
return $this->request('get_all_wash_ids');
}
/**
* @inheritDoc
*/
public function get_all_bookings(): array
{
// Get all the bookings
return $this->request('get_all_wash_bookings');
}
/**
* @inheritDoc
*/
public function get_last_wash_id(): int
{
// Get the last wash ID
$result = $this->request('get_last_wash_id');
return $result["success"] ? (int)$result["data"]["last_wash_id"] : 0;
}
/**
* @inheritDoc
*/
public function parse_bookings(array $bookings): array
{
// Parse all the bookings
$parsed_bookings = [];
foreach ( $bookings as $booking ) {
$parsed_booking = $this->parse_booking($booking);
if ($parsed_booking !== null) {
// Check if the booking has a wash certificate
$parsed_bookings[] = $parsed_booking;
}
}
return $parsed_bookings;
}
/**
* @inheritDoc
*/
public function parse_booking(array|int $booking): array|null
{
// Check if the booking is an integer, and get the booking by the ID
if (is_int($booking)) {
$booking = $this->get_booking($booking);
}
// Check if the success is false
if (isset($booking["success"])) {
if (!$booking["success"]) {
return null;
} else {
$booking = $booking["data"]["booking"];
}
} else if (isset($booking["id"])) {
// Check if the booking property is set
} else {
return null;
}
// Parse the booking pickup bool
// 1 = true, 0 = false
// "true" = true, "false" = false
if ($booking["pickup_bool"] === 1 || $booking["pickup_bool"] === "true") {
$booking["pickup_bool"] = true;
} else {
$booking["pickup_bool"] = false;
}
$this->booking_cache[$booking["id"]] = [
"id" => (int)$booking["id"],
"customer_number" => (int)$booking["customer_number"],
"wash_type" => (string)$booking["wash_type"],
"contact_email" => (string)$booking["contact_email"],
"reference_number" => (string)$booking["reference_number"],
"regNrTraekker" => (string)$booking["regNrTraekker"],
"regNrTrailer" => (string)$booking["regNrTrailer"],
"washCertificateEmail" => (string)$booking["washCertificateEmail"],
"date" => (string)$booking["date"],
"department" => (string)$booking["department"],
"pickup_bool" => $booking["pickup_bool"],
"notes" => (string)$booking["notes"],
"washCertificateStatus" => (string)$booking["washCertificateStatus"],
"washCertificateUrl" => (string)$booking["washCertificateUrl"],
"status" => (string)$booking["status"]
];
// If the status is pending, check if the booking has a wash certificate
if ($this->booking_cache[$booking["id"]]["status"] === 'pending') {
$this->booking_cache[$booking["id"]]["status"] = $this->check_booking_has_wash_certificate($booking) ? 'completed' : 'pending';
}
return $this->booking_cache[$booking["id"]];
}
/**
* @inheritDoc
*/
public function get_booking(int $id): array
{
// Get the booking by the ID
return $this->request('get_wash_booking', ['wash_id' => $id]);
}
/**
* @inheritDoc
*/
public function check_booking_has_wash_certificate(array $booking): bool
{
// Check if the booking has a wash certificate
return $this->wash_certificate_store->washCertificateExists($booking['id']);
}
/**
* @inheritDoc
*/
public function check_booking_has_expected_keys(array $booking, array $expected_keys): bool
{
// Check if the booking has the expected keys
return count(array_intersect($expected_keys, array_keys($booking))) === count($expected_keys);
}
/**
* @inheritDoc
*/
public function get_booking_cache(): array
{
// Get the booking cache
return $this->booking_cache;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
global $EMAIL_WASH_CERTIFICATE_TOKEN;
// This script only runs when the program is called from the command line. It is used to run the CLI script.
if (php_sapi_name() !== 'cli' && !isset($_GET['internalCronCall'])) {
exit;
}
// If the program was called from the command line, get the arguments
if (php_sapi_name() === 'cli') {
$args = $argv;
} else {
// If the program was called from the browser, get the arguments from the URL
$args = [];
$args[1] = $_GET['script'];
$args[2] = $_GET['action'];
$auth = $_GET['auth_key'] ?? null;
if ($auth !== $EMAIL_WASH_CERTIFICATE_TOKEN) {
echo json_encode([
'status' => 'error',
'message' => 'Invalid token'
]);
exit;
}
}
// If the first argument is 'run', switch to the second argument
if ($args[1] === 'run') {
switch ($args[2]) {
case 'minio-test':
echo "Running the minio test script";
require_once 'tests/minio/minioTest.php';
break;
case 'redis-test':
echo "Running the redis test script";
require_once 'tests/redis/redisTest.php';
break;
case 'redis-logSync-test':
echo "Running the redis log sync test script";
require_once 'tests/redis/redisLogSyncTest.php';
break;
case 'bookingModule-test':
echo "Running the bookingModule test script";
require_once 'tests/bookingModule/bookingModuleTest.php';
break;
case 'slackModule-test':
echo "Running the slack test script";
require_once 'tests/slackModule/SlackModuleTest.php';
break;
case 'bookingSync-test':
echo "Running the bookingSync test script";
require_once 'tests/bookingModule/bookingSyncTest.php';
break;
case 'bookingSync':
require_once 'cron/SyncBookings.php';
break;
case 'clearAllUsersEconomicCustomerDiscounts':
require_once 'cron/ClearAllUsersEconomicCustomerDiscounts.php';
break;
case 'clearAllUsersEconomicCustomerDetails':
require_once 'cron/ClearAllUsersEconomicCustomerDetails.php';
break;
case 'economicOrderParser-test':
echo "Running the economicOrderParser test script";
require_once 'tests/economicOrderParser/EconomicOrderParserTest.php';
break;
case 'logSync':
require_once 'cron/SyncLogs.php';
break;
case 'cron':
require_once 'cron/Cron.php';
break;
default:
echo "Invalid script name";
break;
}
} else {
echo "Invalid action";
}
+13
View File
@@ -0,0 +1,13 @@
{
"require-dev": {
"rector/rector": "^1.2"
},
"require": {
"ext-mysqli": "*",
"ext-openssl": "*",
"ext-curl": "*",
"ext-json": "*",
"aws/aws-sdk-php": "^3.0",
"predis/predis": "*"
}
}
+1097
View File
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
<?php global $CONFIG_DB, $DEBUG, $ENCRYPTION_KEY, $CORS, $ECONOMIC_API, $WORDPRESS_STATIC_TOKEN, $EMAIL_WASH_CERTIFICATE_TOKEN, $MINIO, $REDIS_CONFIG, $WORDPRESS_API_URL, $SLACK_DEFAULT_WEBHOOK;
/**
* Check if the environment variables are set (If we are running in a Docker container)
*/
if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') {
// Set the configuration from the environment variables
$ENV_VARIABLES = [
'CONFIG_DB_HOST' => 'host',
'CONFIG_DB_USER' => 'user',
'CONFIG_DB_PASSWORD' => 'password',
'CONFIG_DB_DATABASE' => 'database',
'DEBUG' => 'DEBUG',
'ENCRYPTION_KEY' => 'ENCRYPTION_KEY',
'CORS' => 'CORS',
'ECONOMIC_API_APP_ACCESS_GRANT' => 'app_access_grant',
'ECONOMIC_API_APP_ACCESS_GRANT2' => 'app_access_grant2',
'ECONOMIC_API_APP_SECRET_TOKEN' => 'app_secret_token',
'WORDPRESS_STATIC_TOKEN' => 'WORDPRESS_STATIC_TOKEN',
'EMAIL_WASH_CERTIFICATE_TOKEN' => 'EMAIL_WASH_CERTIFICATE_TOKEN',
'WORDPRESS_API_URL' => 'WORDPRESS_API_URL',
'MINIO_ENDPOINT' => 'endpoint',
'MINIO_ACCESS_KEY' => 'access_key',
'MINIO_SECRET_KEY' => 'secret_key',
'SLACK_DEFAULT_WEBHOOK' => 'SLACK_DEFAULT_WEBHOOK',
'REDIS_CONFIG_HOST' => 'host',
'REDIS_CONFIG_DATABASE' => 'database',
'REDIS_CONFIG_PASSWORD' => 'password'
];
/**
* Set the db configuration
*/
$CONFIG_DB = [
'host' => $_ENV['CONFIG_DB_HOST'],
'user' => $_ENV['CONFIG_DB_USER'],
'password' => $_ENV['CONFIG_DB_PASSWORD'],
'database' => $_ENV['CONFIG_DB_DATABASE']
];
/**
* Set the debug configuration
*/
$DEBUG = $_ENV['DEBUG'];
/**
* Set the encryption key
*/
$ENCRYPTION_KEY = $_ENV['ENCRYPTION_KEY'];
/**
* Set the CORS configuration
*/
$CORS = $_ENV['CORS'];
/**
* Set the economic API configuration
*/
$ECONOMIC_API = [
'app_access_grant' => $_ENV['ECONOMIC_API_APP_ACCESS_GRANT'],
'app_access_grant2' => $_ENV['ECONOMIC_API_APP_ACCESS_GRANT2'],
'app_secret_token' => $_ENV['ECONOMIC_API_APP_SECRET_TOKEN']
];
/**
* Set the WordPress static token
*/
$WORDPRESS_STATIC_TOKEN = $_ENV['WORDPRESS_STATIC_TOKEN'];
/**
* Set the email wash certificate token
*/
$EMAIL_WASH_CERTIFICATE_TOKEN = $_ENV['EMAIL_WASH_CERTIFICATE_TOKEN'];
/**
* Set the WordPress API URL
*/
$WORDPRESS_API_URL = $_ENV['WORDPRESS_API_URL'];
/**
* Set the Minio configuration
*/
$MINIO = [
'endpoint' => $_ENV['MINIO_ENDPOINT'],
'access_key' => $_ENV['MINIO_ACCESS_KEY'],
'secret_key' => $_ENV['MINIO_SECRET_KEY']
];
/**
* Set the Slack default webhook
*/
$SLACK_DEFAULT_WEBHOOK = $_ENV['SLACK_DEFAULT_WEBHOOK'];
/**
* Set the Redis configuration
*/
$REDIS_CONFIG = [
'host' => $_ENV['REDIS_CONFIG_HOST'],
'database' => $_ENV['REDIS_CONFIG_DATABASE'],
'password' => $_ENV['REDIS_CONFIG_PASSWORD']
];
// Set the timezone
date_default_timezone_set($_ENV['CONFIG_TIMEZONE']) ?? 'Europe/Copenhagen';
} else {
// Throw an error if the environment variables are not set
throw new Exception('Environment variables are not set');
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/**
* Cron job, ran every minute.
*/
$now = time();
// Log the time of the cron job
file_put_contents(__DIR__ . '/cron.log', date('Y-m-d H:i:s', $now) . ' - Cron job started' . PHP_EOL, FILE_APPEND);
// Include the required files
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/config.php';
// Set the .htaccess file
$htaccess = "
# php -- BEGIN cPanel-generated handler, do not edit
# Set the “ea-php80” package as the default “PHP” programming language.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
<IfModule mime_module>
AddHandler application/x-httpd-ea-php80 .php .php8 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit
";
// Write the .htaccess file (This is not really ideal, but it works for now. This is because the .htaccess file is changed by cPanel, and it's not going to be kept there anyway.)
file_put_contents(__DIR__ . '/.htaccess', $htaccess);
// Log the time of the cron job
file_put_contents(__DIR__ . '/cron.log', date('Y-m-d H:i:s', $now) . ' - Cron job executed in ' . (time() - $now) . ' seconds ( ' . (microtime(true) - $now) . 'ms )' . PHP_EOL, FILE_APPEND);
@@ -0,0 +1,21 @@
<?php
/**
* This script is used to sync bookings from the remote API to the local database.
* It is run as a cron job.
*
* index.php run bookingSync
*/
// prevent direct access
use objects\bookings_o;
if (!defined('WD')) {
exit;
}
// Sync the bookings
$bookings_o = new bookings_o();
// Check if any bookings from yesterday haven't been fulfilled
$bookings_o->checkUnfulfilledBookings();
@@ -0,0 +1,22 @@
<?php
/**
* This script is used to sync bookings from the remote API to the local database.
* It is run as a cron job.
*
* index.php run bookingSync
*/
// prevent direct access
use objects\users_o;
if (!defined('WD')) {
exit;
}
$start = microtime(true);
// Sync the discounts
$users_o = new users_o();
$users_o->clearAllUsersEconomicCustomerDetailsFromCache();
$end = microtime(true);
//$slack = new \classes\slack();
//$slack->send_message("The booking sync script has finished. It took " . round($end - $start, 2) . " seconds to run.");
@@ -0,0 +1,22 @@
<?php
/**
* This script is used to sync bookings from the remote API to the local database.
* It is run as a cron job.
*
* index.php run bookingSync
*/
// prevent direct access
use objects\users_o;
if (!defined('WD')) {
exit;
}
$start = microtime(true);
// Sync the discounts
$users_o = new users_o();
$users_o->clearAllUsersEconomicCustomerDiscountsFromCache();
$end = microtime(true);
//$slack = new \classes\slack();
//$slack->send_message("The booking sync script has finished. It took " . round($end - $start, 2) . " seconds to run.");
+97
View File
@@ -0,0 +1,97 @@
<?php
// prevent direct access
use objects\bookings_o;
use objects\logs_o;
use objects\users_o;
if (!defined('WD')) {
exit;
}
// Define the warning function
function warn($message): void
{
echo "\n\033[33m$message\033[0m\n";
}
$response_cron = [];
// Define the cron tasks
$cron_tasks = [
'CheckUnfulfilledBookings' => [
'interval' => 86400, // 24 hours
'last_run' => 0,
'next_run' => 0,
'time' => '15:00',
'function' => 'checkUnfulfilledBookings',
],
'SyncBookings' => [
'interval' => 60, // 1 minute
'last_run' => 0,
'next_run' => 0,
'function' => 'syncBookings',
],
'SyncLogs' => [
'interval' => 300, // 5 minutes
'last_run' => 0,
'next_run' => 0,
'function' => 'syncLogsToDatabase',
],
'SyncUserEconomicCustomerDiscounts' => [
'interval' => 600, // 10 minutes
'last_run' => 0,
'next_run' => 0,
'function' => 'SyncUserEconomicCustomerDiscounts',
],
'SyncUserEconomicCustomerDetails' => [
'interval' => 43200, // 12 hours
'last_run' => 0,
'next_run' => 0,
'function' => 'SyncUserEconomicCustomerDetails',
],
];
function checkUnfulfilledBookings(): void
{
$bookings_o = new bookings_o();
$bookings_o->checkUnfulfilledBookings();
}
function syncBookings(): void
{
$bookings_o = new bookings_o();
$response_cron[] = $bookings_o->syncBookings();
}
function syncLogsToDatabase(): void
{
$logs_o = new logs_o();
$logs_o->syncLogsToDatabase();
}
function SyncUserEconomicCustomerDiscounts(): void
{
$users_o = new users_o();
$users_o->clearAllUsersEconomicCustomerDiscountsFromCache();
}
function SyncUserEconomicCustomerDetails(): void
{
$users_o = new users_o();
$users_o->clearAllUsersEconomicCustomerDetailsFromCache();
$users_o->syncAllUsersEconomicCustomerDetails();
}
foreach ( $cron_tasks as $task => $data ) {
$lastRun = redis->get_last_crond_run($task) === null ? 0 : redis->get_last_crond_run($task);
$nextRun = $lastRun + $data['interval'];
$cron_tasks[$task]['last_run'] = $lastRun;
$cron_tasks[$task]['next_run'] = $nextRun;
if ($nextRun <= time()) {
$data['function']();
redis->set_last_crond_run($task, time());
} else {
$response_cron[] = $task . ' is not due to run yet, next run is at ' . date('Y-m-d H:i:s', $nextRun) . ' (' . ($nextRun - time()) . ' seconds)';
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
/**
* This script is used to sync bookings from the remote API to the local database.
* It is run as a cron job.
*
* index.php run bookingSync
*/
// prevent direct access
use objects\bookings_o;
if (!defined('WD')) {
exit;
}
$start = microtime(true);
// Sync the bookings
$bookings_o = new bookings_o();
$bookings_o->syncBookings();
$end = microtime(true);
//$slack = new \classes\slack();
//$slack->send_message("The booking sync script has finished. It took " . round($end - $start, 2) . " seconds to run.");
+19
View File
@@ -0,0 +1,19 @@
<?php
/**
* This script is used to sync the logs to the database.
* It is run as a cron job.
*
* index.php run SyncLogs
*/
// prevent direct access
use objects\logs_o;
if (!defined('WD')) {
exit;
}
// Sync the logs to the database
$logs_o = new logs_o();
$logs_o->syncLogsToDatabase();
+113
View File
@@ -0,0 +1,113 @@
<?php global /** @var response $response */
$DEBUG, $CONFIG_DB, $ENCRYPTION_KEY, $CORS, $WD, $db, $response, $request, $router, $redis;
/**
* This is the main entry point to the Truck Wash API.
*/
const WD = __DIR__;
require_once 'config.php';
/** Debug */
if ($DEBUG) {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
/** CORS */
header("Access-Control-Allow-Origin: $CORS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
/** Autoload */
require_once 'vendor/autoload.php';
/** Load all Interfaces */
foreach ( glob(WD . '/interfaces/*.php') as $interface ) {
require_once $interface;
}
/** Load all Traits */
foreach ( glob(WD . '/traits/*.php') as $trait ) {
require_once $trait;
}
/** Load all Classes */
require_once 'classes/encrypt.php';
require_once 'classes/authentication.php';
require_once 'classes/object_property.php';
require_once 'classes/router.php';
require_once 'classes/db.php';
require_once 'classes/response.php';
require_once 'classes/request.php';
require_once 'classes/ratelimit.php';
require_once 'classes/wash_certificate_store.php';
require_once 'classes/redis.php';
require_once 'classes/slack.php';
require_once 'classes/wordpress_bookings_remote.php';
require_once 'classes/language_packs.php';
require_once 'classes/economic.php';
/**
* Modules
* Load all the modules
*/
require_once 'modules/economic/economic_m.php';
require_once 'modules/economic/customers/economicCustomers.php';
require_once 'modules/economic/customers/economic_customer_mo.php';
require_once 'modules/economic/invoices/draft/economicInvoicesDrafts.php';
require_once 'modules/economic/invoices/draft/economic_invoice_draft_mo.php';
use classes\db;
use classes\redis;
use classes\request;
use classes\response;
use classes\router;
// Start the session
$router = new router();
$response = new response();
$request = new request();
$db = new db($CONFIG_DB);
try {
define("redis", (new redis())->connect());
} catch (Exception $e) {
$response->error($e->getMessage(), 500);
}
// Connect to the database, and ensure the connection is successful
try {
$db->connect();
} catch (Exception $e) {
$response->error($e->getMessage(), 500);
}
// Load all the traits
foreach ( glob(WD . '/traits/*.php') as $trait ) {
require_once $trait;
}
// Load all the routes
foreach ( glob(WD . '/routes/*.php') as $route ) {
require_once $route;
}
/** Load all Objects */
foreach ( glob(WD . '/objects/*.php') as $object ) {
try {
require_once $object;
} catch (Exception $e) {
$response->error($e->getMessage(), 500);
}
}
// If the program was called from the command line, run the cli script
if (php_sapi_name() === 'cli' || isset($_GET['internalCronCall'])) {
require_once 'cli.php';
exit;
}
// Autoload all the routes
$router->auto_load_routes(WD . '/routes');
/** If no matching route is found, return a 404 */
if (!$response->is_matching_route_found()) {
// Debug
$response->add_debug(['message' => 'No matching route found']);
$response->not_found();
}
@@ -0,0 +1,11 @@
<?php
namespace interfaces;
interface authentication_i
{
public function authenticate(int $customer_number, string $password): bool;
public function create_token(int $customer_number): string;
public function hash_password(string $password): string;
public function match_passwords(string $password, string $hash): bool;
}
@@ -0,0 +1,7 @@
<?php
namespace interfaces;
interface economic_i
{
}
@@ -0,0 +1,9 @@
<?php
namespace interfaces;
interface encrypt_i
{
public function encrypt(string $data): string;
public function decrypt(string $data): string;
}
@@ -0,0 +1,7 @@
<?php
namespace interfaces;
interface language_pack_i
{
}
@@ -0,0 +1,13 @@
<?php
namespace interfaces;
interface minio_wash_certificates_i
{
/**
* Check if a wash certificate exists
* @param string $certificate_id
* @return bool
*/
public function washCertificateExists(string $certificate_id): bool;
}
@@ -0,0 +1,8 @@
<?php
namespace interfaces;
interface notification_i
{
}
@@ -0,0 +1,8 @@
<?php
namespace interfaces;
interface ratelimit_i
{
public function enforceIP(string $ip): bool;
}
+247
View File
@@ -0,0 +1,247 @@
<?php
namespace interfaces;
interface redis_i
{
/**
* Cache a departments booking count (cached)
* @param int $department_id
* @param int $count
* @return self
*/
public function cache_department_booking_count(int $department_id, int $count): self;
/**
* Get a departments booking count (cached)
* @param int $department_id
* @return int|null
*/
public function get_department_booking_count(int $department_id): int|null;
/**
* Clear a departments booking count (cached)
* @param int $department_id
* @return self
*/
public function clear_department_booking_count(int $department_id): self;
/**
* Cache a department webhook
* @param int $department_id
* @param string $webhook
* @return self
*/
public function cache_department_webhook(int $department_id, string $webhook): self;
/**
* Get a department webhook
* @param int $department_id
* @return string|null
*/
public function get_department_webhook(int $department_id): string|null;
/**
* Clear a department webhook
* @param int $department_id
* @return self
*/
public function clear_department_webhook(int $department_id): self;
/**
* Cache a department name
* @param int $department_id
* @param string $department_name
* @return self
*/
public function cache_department_name(int $department_id, string $department_name): self;
/**
* Get a department name
* @param int $department_id
* @return string|null
*/
public function get_department_name(int $department_id): string|null;
/**
* Clear a department name
* @param int $department_id
* @return self
*/
public function clear_department_name(int $department_id): self;
/**
* Cache an economic customers discount percentage
* @param int $user_id
* @param int $discount_percentage
* @return self
*/
public function cache_economic_customer_discount_percentage(int $user_id, int $discount_percentage): self;
/**
* Get an economic customers discount percentage
* @param int $user_id
* @return int|null
*/
public function get_economic_customer_discount_percentage(int $user_id): int|null;
/**
* Clear an economic customers discount percentage
* @param int $user_id
* @return self
*/
public function clear_economic_customer_discount_percentage(int $user_id): self;
/**
* Cache a customer notes
* @param int $user_id
* @param array $customer_notes
* @return self
*/
public function cache_customer_notes(int $user_id, array $customer_notes): self;
/**
* Get a customer notes
* @param int $user_id
* @return array|null
*/
public function get_customer_notes(int $user_id): array|null;
/**
* Clear a customer notes
* @param int $user_id
* @return self
*/
public function clear_customer_notes(int $user_id): self;
/**
* Add a log to the logs cache
* @param string $module
* @param string $department
* @param int $type
* @param int $user_id
* @param string $action
* @param string $message
* @return self
*/
public function add_log(string $module, string $department, int $type, int $user_id, string $action, string $message): self;
/**
* Get all logs from the logs cache
* @return array|null
*/
public function get_logs(): array|null;
/**
* Clear all logs from the logs cache
* @return self
*/
public function clear_logs(): self;
/**
* Cache a department
* @param int $department_id
* @param array $department
* @return self
*/
public function cache_department(int $department_id, array $department): self;
/**
* Get a department
* @param int $department_id
* @return array|null
*/
public function get_department(int $department_id): array|null;
/**
* Clear a department
* @param int $department_id
* @return self
*/
public function clear_department(int $department_id): self;
/**
* Get departments from the cache
* @return array|null
*/
public function get_departments(): array|null;
/**
* Cache departments
* @param array $departments
* @return self
*/
public function cache_departments(array $departments): self;
/**
* Clear departments from the cache
* @return self
*/
public function clear_departments(): self;
/**
* Get the log count
* @return int
*/
public function get_log_count(): int;
/**
* Cache a token
* @param string $token
* @param array $row
* @return self
*/
public function cache_token(string $token, array $row): self;
/**
* Get a token
* @param string $token
* @return array|null
*/
public function get_token(string $token): array|null;
/**
* Delete a token
* @param string $token
* @return self
*/
public function clear_token(string $token): self;
/**
* Get the last cron task
* @param string $task
* @return int|null
*/
public function get_last_crond_run(string $task): int|null;
/**
* Set the last cron task
* @param string $task
* @param int $time
* @return self
*/
public function set_last_crond_run(string $task, int $time): self;
/**
* Get a users id from customer number from the cache
* @param int $customer_number
* @return int|null
*/
public function get_user_id_from_customer_number(int $customer_number): int|null;
/**
* Cache a users id from customer number
* @param int $customer_number
* @param int $user_id
* @return self
*/
public function cache_user_id_from_customer_number(int $customer_number, int $user_id): self;
/**
* Clear a users id from customer number from the cache
* @param int $customer_number
* @return self
*/
public function clear_user_id_from_customer_number(int $customer_number): self;
}
@@ -0,0 +1,16 @@
<?php
namespace interfaces;
interface response_i
{
public function response(bool $success, array $data, int $status = null): void;
public function success(mixed $data, int $status = null): void;
public function error(mixed $data, int $status = null): void;
public function not_found(): void;
public function matching_route_found(): void;
public function method_not_allowed(): void;
public function internal_server_error($error): void;
public function add_data(string $key, mixed $value): void;
public function add_debug(mixed $data): void;
}
@@ -0,0 +1,65 @@
<?php
namespace interfaces;
interface wordpress_bookings_remote_i
{
/**
* Get booking by ID from the remote API
* @param int $id
* @return array
*/
public function get_booking(int $id): array;
/**
* Parse booking data, and return it as an array
* @param array|int $booking
* @return array|null The parsed booking data, or null if the booking was not found
*/
public function parse_booking(array|int $booking): array|null;
/**
* Get all wash IDs from the remote API
* @return array
*/
public function get_all_wash_ids(): array;
/**
* Get all bookings from the remote API
* @return array
*/
public function get_all_bookings(): array;
/**
* Get the last wash ID from the remote API
* @return int
*/
public function get_last_wash_id(): int;
/**
* Parse multiple bookings
* @param array $bookings
* @return array
*/
public function parse_bookings(array $bookings): array;
/**
* Check if a booking has the expected keys
* @param array $booking
* @param array $expected_keys
* @return bool
*/
public function check_booking_has_expected_keys(array $booking, array $expected_keys): bool;
/**
* Check if a booking has a wash certificate
* @param array $booking
*/
public function check_booking_has_wash_certificate(array $booking): bool;
/**
* Get the booking cache
* @return array
*/
public function get_booking_cache(): array;
}
@@ -0,0 +1,27 @@
<?php
namespace languages;
use interfaces\language_pack_i;
use traits\language_pack_t;
class language_pack_en_us implements language_pack_i
{
use language_pack_t;
public function __construct()
{
$this->setLanguage('en_us');
$this->addTranslations([
'Order ID is required' => 'Order ID is required ( Translated )',
'Product ID is required' => 'Product ID is required ( Translated )',
'Quantity is required' => 'Quantity is required ( Translated )',
'Order items added' => 'Order items added ( Translated )',
]);
}
public function getLanguage(): string
{
return $this->language;
}
}
@@ -0,0 +1,90 @@
<?php
namespace customers;
use economic_m;
use objects\users_o;
class economicCustomers extends economic_m
{
public users_o $users_o;
public function searchCustomers(string|int $search, string $filter, int $limit = 10, int $page = 1): object
{
// Make sure the search string is ready for the API
$search = urlencode($search);
// Search for customers
$url = '/customers?filter=' . $filter . '$like:' . $search . '&pagesize=' . $limit . '&skippages=' . $page - 1;
$response = $this->send_request($url, 'GET', '');
return json_decode($response);
}
public function getCustomerName(int $customer_number): string|null
{
// Check if the customer name is cached
$tmp_user = self::getUsersObject()->getUserByCustomerNumber($customer_number);
$cached = $tmp_user->getCached('economic_customer');
if ($cached) {
return $cached->name;
}
// Get the customer name from the economic system
$tmp_user->getCustomerEcocomicData($customer_number);
$cached = $tmp_user->getCached('economic_customer');
if ($cached) {
return $cached->name;
}
return null;
}
public function getUsersObject(): users_o
{
if (!isset($this->users_o)) {
$this->users_o = new users_o();
}
return $this->users_o;
}
public function getCustomerId(int $customerNumber): object|bool
{
// Check if the customer exists
$url = '/customers/' . $customerNumber;
$response = $this->send_request($url, 'GET', '');
$response = json_decode($response);
return isset($response->customerNumber) ? $response : false;
}
public function getCustomerProduct(int $customer_number, int $product_id): object
{
// Get the customer product
$url = '/customers/' . $customer_number . '/templates/invoiceline/' . $product_id;
$response = $this->send_request($url, 'GET', '');
return json_decode($response);
}
public function getCustomerDiscountPercentage(int $customer_number): int
{
// Get the customer products
$products = $this->getCustomerProducts($customer_number, 1)->collection;
$discount = $this->getCustomerProductDiscount($customer_number, $products[0]->product->productNumber);
// Since the discount is global, we only need to get the discount for one product
return $discount->discountPercentage;
}
public function getCustomerProducts(int $customer_number, int $limit = 10, int $page = 1): object
{
// Get the customer products
$url = '/customers/' . $customer_number . '/templates/invoiceline/?pagesize=' . $limit . '&skippages=' . $page - 1;
$response = $this->send_request($url, 'GET', '');
return json_decode($response);
}
public function getCustomerProductDiscount(int $customer_number, int $product_id)
{
// Get the customer global discount
$url = '/customers/' . $customer_number . '/templates/invoiceline/' . $product_id;
$response = $this->send_request($url, 'GET', '');
$response = json_decode($response);
return $response;
}
}
@@ -0,0 +1,80 @@
<?php
namespace customers;
use classes\response;
use objects\users_o;
class economic_customer_mo
{
public int $customer_number;
public null|string $name;
public null|string $address;
public null|string $city;
public null|string $zip;
public null|string $message;
public null|string $corporateIdentificationNumber;
public null|string $email;
public null|string $mobilePhone;
public null|string $currency;
public null|string $country;
public function getCustomerByCustomerNumber(int $customer_number): static
{
// Get the customer from the economic system
$economicCustomers = new economicCustomers();
$customer = $economicCustomers->getCustomerId($customer_number);
// Check if the customer exists
if ($customer) {
return $this->parseCustomer($customer);
}
return $this;
}
public function parseCustomer($customer): static
{
global /** @var response $response */
$response;
$this->customer_number = $customer->customerNumber;
$this->name = ($customer->name ?? null);
$this->address = ($customer->address ?? null);
$this->city = ($customer->city ?? null);
$this->zip = ($customer->zip ?? null);
$this->corporateIdentificationNumber = ($customer->corporateIdentificationNumber ?? null);
$this->email = ($customer->email ?? null);
$this->mobilePhone = ($customer->mobilePhone ?? null);
$this->currency = ($customer->currency ?? null);
$this->country = ($customer->country ?? null);
$users_o = new users_o();
$user = $users_o->getUserByCustomerNumber($this->customer_number);
// Cache the customer
if ($user->exists()) {
$user->cache('economic_customer', $this);
}
// Add the customer to the debug log
$response->add_debug_list('economic_customer', $this->customer_number, $customer);
return $this;
}
public function asArray(): array
{
// If the customer does not exist, return an empty array
if (!isset($this->customer_number)) {
return [];
}
return [
'customerNumber' => $this->customer_number,
'name' => $this->name,
'address' => $this->address,
'city' => $this->city,
'zip' => $this->zip,
'corporateIdentificationNumber' => $this->corporateIdentificationNumber,
'email' => $this->email,
'mobilePhone' => $this->mobilePhone,
'currency' => $this->currency,
'country' => $this->country,
];
}
}
@@ -0,0 +1,67 @@
<?php
class economic_m
{
/**
* https://secure.e-conomic.com/secure/api1/requestaccess.aspx?appPublicToken=WeG89W2Y63wLjy1lnqfpsHlfa1mBiyNKByqtGmBOoOw&redirectUrl=truckwash.dk
*/
private string $api_url = 'https://restapi.e-conomic.com';
private string $appAccessGrant;
private string $app_token;
private string $appAccessGrant2;
public function __construct()
{
global $ECONOMIC_API;
$this->app_token = $ECONOMIC_API['app_secret_token'];
$this->appAccessGrant = $ECONOMIC_API['app_access_grant'];
$this->appAccessGrant2 = $ECONOMIC_API['app_access_grant2'];
}
/**
* Send a request to the Economic API
* @param string $url
* @param string $method
* @param string $data
* @param bool $authToken2
* @return string
*/
protected function send_request($url, $method, $data = '', bool $authToken2 = false): string
{
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $this->api_url . $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => array(
'X-AppSecretToken: ' . $this->app_token,
'X-AgreementGrantToken: ' . ( $authToken2 ? $this->appAccessGrant2 : $this->appAccessGrant ),
'Content-Type: application/json'
),
));
if ($method === 'POST') {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
/**
* Allowed search filters
* @return array
*/
public function allowed_search_filters_customers(): array
{
return [
'address', 'balance', 'barred', 'city', 'corporateIdentificationNumber', 'country', 'creditLimit', 'currency', 'customerGroup.customerGroupNumber', 'customerNumber', 'ean', 'email', 'lastUpdated', 'mobilePhone', 'name', 'publicEntryNumber', 'telephoneAndFaxNumber', 'vatNumber', 'website', 'zip'
];
}
}
@@ -0,0 +1,23 @@
<?php
namespace endpoints;
require_once WD . '/modules/economic/endpoints/orders/economic_orders_drafts_endpoint.php';
use endpoints\orders\economic_orders_drafts_endpoint;
use traits\economic_endpoint_t;
class economic_orders_endpoint
{
use economic_endpoint_t;
/**
* Any endpoints reached by the /orders/drafts endpoint
* @var economic_orders_drafts_endpoint
*/
public economic_orders_drafts_endpoint $drafts;
public function __construct()
{
$this->drafts = new economic_orders_drafts_endpoint();
}
}
@@ -0,0 +1,22 @@
<?php
namespace endpoints\orders;
use traits\economic_endpoint_t;
class economic_orders_drafts_endpoint
{
use economic_endpoint_t;
/**
* List all draft orders
* @return object {collection: [order], pagination: {maxPageSize: number, skipPages: number, results: number}}
*/
public function get(): object
{
$response = $this->send_request('/orders/drafts', 'GET');
// Return the response as an object
return json_decode($response);
}
}
@@ -0,0 +1,21 @@
<?php
class economicInvoicesDrafts extends economic_m
{
public function createInvoiceDraft(array $data): object
{
// Create a draft invoice
$url = '/invoices/drafts';
$response = $this->send_request($url, 'POST', json_encode($data));
return json_decode($response);
}
public function getLayouts(): object
{
// Get all the layouts
$url = '/layouts';
$response = $this->send_request($url, 'GET');
return json_decode($response);
}
}
@@ -0,0 +1,134 @@
<?php
class economic_invoice_draft_mo extends economicInvoicesDrafts
{
protected int $customer_number;
protected string $date; // Date of the invoice (YYYY-MM-DD)
protected string $currency; // Currency of the invoice (DKK, EUR, USD, etc.)
protected float $layout_number; // Layout number of the invoice
protected float $payment_terms_number; // Payment terms number of the invoice
protected array $recipient; // Recipient of the invoice (Includes name, address, zip, city, and (array)vatZone)
protected array $lines; // Lines of the invoice (Includes product, quantity, unitNetPrice, discountPercentage, and (array)vatRate)
public function addLine(string $productNumber, string $description, float $quantity, float $unitNetPrice, float $discountPercentage, int $economic_department_id, int $dimension): void
{
// We then add a 0 in front of the product number to make sure it's the right one, made by FlexPOS.
// Add a line to the invoice
$line = [
'product' => [
'productNumber' => $productNumber,
],
'quantity' => $quantity,
'unitNetPrice' => $unitNetPrice,
'discountPercentage' => $discountPercentage,
'description' => $description,
];
// If the department is set, add it to the line
if ($economic_department_id) {
$line['departmentalDistribution'] = [
'departmentalDistributionNumber' => $economic_department_id,
'DistributionType' => 'Department',
'dimension' => $dimension
];
}
$this->lines[] = $line;
}
public function addLineTEXT(string $text): void
{
// Add a line to the invoice
$this->lines[] = [
'description' => (string)$text,
];
}
public function createInvoiceDraftExample(): object
{
$data = [
'currency' => 'DKK',
'date' => date('Y-m-d'),
'layout' => [
'layoutNumber' => 1
],
'paymentTerms' => [
'paymentTermsNumber' => 1
],
'recipient' => [
'name' => $this->recipient['name'],
'address' => $this->recipient['address'],
'zip' => $this->recipient['zip'],
'city' => $this->recipient['city'],
'vatZone' => [
'vatZoneNumber' => 1
]
],
'customer' => [
'customerNumber' => (int)$this->customer_number
],
'lines' => $this->lines // Lines added using the addLine method
];
return $this->createInvoiceDraft($data);
}
// Example of a method that uses the createInvoiceDraft method
public function createInvoiceDraft(array $data): object
{
// Create a draft invoice
$url = '/invoices/drafts';
$response = $this->send_request($url, 'POST', json_encode($data));
return json_decode($response);
}
public function setCustomerNumber(int $customer_number): economic_invoice_draft_mo
{
$this->customer_number = $customer_number;
return $this;
}
public function setRecipient(string $name, string $address, string $zip, string $city): economic_invoice_draft_mo
{
$this->recipient = [
'name' => $name,
'address' => $address,
'zip' => $zip,
'city' => $city,
];
return $this;
}
public function deleteInvoiceDraft(int $value): void
{
// Delete the invoice draft
$url = '/invoices/drafts/' . $value;
$this->send_request($url, 'DELETE');
}
public function publishInvoiceDraft(int $invoiceDraftId): object
{
// Publish the invoice draft
$url = '/invoices/booked';
return json_decode($this->send_request($url, 'POST', json_encode(['draftInvoice' => ['draftInvoiceNumber' => $invoiceDraftId]])));
}
public function getInvoicePdf(int $param)
{
// Get the invoice PDF
$url = '/invoices/booked/' . $param . '/pdf';
return $this->send_request($url, 'GET');
}
public function getInvoiceDraft(int $int): object
{
// Get the invoice draft
$url = '/invoices/drafts/' . $int;
return json_decode($this->send_request($url, 'GET'));
}
public function addLinesToInvoiceDraft(int $invoiceDraftId): object
{
// Add lines to the invoice draft
$url = '/invoices/drafts/' . $invoiceDraftId . '/lines';
return json_decode($this->send_request($url, 'POST', json_encode(['lines' => $this->lines])));
}
}
@@ -0,0 +1,401 @@
{
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "Customer collection GET schema",
"description": "A schema for fetching a collection of customer, aka. Debtor.",
"type": "object",
"restdocs": "http://restdocs.e-conomic.com/#get-customers",
"properties": {
"collection": {
"type": "array",
"description": "A collection of customers.",
"items": {
"title": "Customer",
"type": "object",
"properties": {
"address": {
"type": "string",
"maxLength": 510,
"sortable": true,
"filterable": true,
"description": "Address for the customer including street and number."
},
"balance": {
"type": "number",
"readOnly": true,
"sortable": true,
"filterable": true,
"description": "The outstanding amount for this customer."
},
"barred": {
"type": "boolean",
"filterable": true,
"description": "Boolean indication of whether the customer is barred from invoicing."
},
"city": {
"type": "string",
"maxLength": 50,
"sortable": true,
"filterable": true,
"description": "The customer's city."
},
"corporateIdentificationNumber": {
"type": "string",
"maxLength": 40,
"sortable": true,
"filterable": true,
"description": "Corporate Identification Number. For example CVR in Denmark."
},
"pNumber": {
"type": "string",
"minLength": 10,
"maxLength": 10,
"description": "Extension of corporate identification number (CVR). Identifying separate production unit (p-nummer)."
},
"country": {
"type": "string",
"maxLength": 50,
"sortable": true,
"filterable": true,
"description": "The customer's country."
},
"creditLimit": {
"type": "number",
"sortable": true,
"filterable": true,
"description": "A maximum credit for this customer. Once the maximum is reached or passed in connection with an order/quotation/invoice for this customer you see a warning in e-conomic."
},
"currency": {
"type": "string",
"maxLength": 3,
"minLength": 3,
"required": true,
"sortable": true,
"filterable": true,
"description": "Default payment currency."
},
"customerNumber": {
"type": "integer",
"maximum": 999999999,
"minimum": 1,
"sortable": true,
"filterable": true,
"description": "The customer number is a positive unique numerical identifier with a maximum of 9 digits."
},
"dueAmount": {
"type": "number",
"readOnly": true,
"sortable": false,
"filterable": false,
"description": "Due amount that the customer needs to pay."
},
"ean": {
"type": "string",
"maxLength": 13,
"sortable": true,
"filterable": true,
"description": "European Article Number. EAN is used for invoicing the Danish public sector."
},
"email": {
"type": "string",
"maxLength": 255,
"sortable": true,
"filterable": true,
"description": "Customer e-mail address where e-conomic invoices should be emailed. Note: you can specify multiple email addresses in this field, separated by a space. If you need to send a copy of the invoice or write to other e-mail addresses, you can also create one or more customer contacts."
},
"lastUpdated": {
"type": "string",
"format": "full-date",
"pattern": "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z",
"sortable": true,
"filterable": true,
"description": "The date this customer was last updated. The date is formatted according to ISO-8601."
},
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"required": true,
"sortable": true,
"filterable": true,
"description": "The customer name."
},
"publicEntryNumber": {
"type": "string",
"maxLength": 50,
"sortable": true,
"filterable": true,
"description": "The public entry number is used for electronic invoicing, to define the account invoices will be registered on at the customer."
},
"telephoneAndFaxNumber": {
"type": "string",
"maxLength": 255,
"sortable": true,
"filterable": true,
"description": "The customer's telephone and/or fax number."
},
"mobilePhone": {
"type": "string",
"maxLength": 50,
"sortable": true,
"filterable": true,
"description": "The customer's mobile phone number."
},
"eInvoicingDisabledByDefault": {
"type": "boolean",
"readonly": false,
"description": "Boolean indication of whether the default sending method should be email instead of e-invoice. This property is updatable only by using PATCH to /customers/:customerNumber"
},
"vatNumber": {
"type": "string",
"maxLength": 20,
"sortable": true,
"filterable": true,
"description": "The customer's value added tax identification number. This field is only available to agreements in Sweden, UK, Germany, Poland and Finland. Not to be mistaken for the danish CVR number, which is defined on the corporateIdentificationNumber property."
},
"website": {
"type": "string",
"maxLength": 255,
"sortable": true,
"filterable": true,
"description": "Customer website, if applicable."
},
"zip": {
"type": "string",
"maxLength": 30,
"sortable": true,
"filterable": true,
"description": "The customer's postcode."
},
"contacts": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the customer contacts items."
},
"deliveryLocations": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the customer delivery locations items."
},
"defaultDeliveryLocation": {
"type": "object",
"description": "Customers default delivery location.",
"properties": {
"deliveryLocationNumber": {
"type": "integer",
"description": "The unique identifier of the delivery location."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the delivery location.",
"required": true
}
}
},
"attention": {
"type": "object",
"description": "The customer's person of attention.",
"properties": {
"customerContactNumber": {
"type": "integer",
"description": "The unique identifier of the customer employee."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the customer employee item.",
"required": true
}
}
},
"customerContact": {
"type": "object",
"description": "Reference to main contact employee at customer.",
"properties": {
"customerContactNumber": {
"type": "integer",
"description": "The unique identifier of the customer contact."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the customer contact item.",
"required": true
}
}
},
"customerGroup": {
"type": "object",
"required": true,
"description": "Reference to the customer group this customer is attached to.",
"properties": {
"customerGroupNumber": {
"type": "integer",
"filterable": true,
"description": "The unique identifier of the customer group."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the customer group item.",
"required": true
}
}
},
"layout": {
"type": "object",
"description": "Layout to be applied for invoices and other documents for this customer.",
"properties": {
"layoutNumber": {
"type": "integer",
"description": "The unique identifier of the layout."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the layout item.",
"required": true
}
}
},
"paymentTerms": {
"type": "object",
"required": true,
"description": "The default payment terms for the customer.",
"properties": {
"paymentTermsNumber": {
"type": "integer",
"description": "The unique identifier of the payment terms."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the payment terms item.",
"required": true
}
}
},
"salesPerson": {
"type": "object",
"description": "Reference to the employee responsible for contact with this customer.",
"properties": {
"employeeNumber": {
"type": "integer",
"description": "The unique identifier of the employee."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the employee resource.",
"required": true
}
}
},
"vatZone": {
"type": "object",
"required": true,
"description": "Indicates in which VAT-zone the customer is located (e.g.: domestically, in Europe or elsewhere abroad).",
"properties": {
"vatZoneNumber": {
"type": "integer",
"description": "The unique identifier of the VAT-zone."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the VAT-zone item.",
"required": true
}
}
},
"templates": {
"type": "object",
"description": "",
"properties": {
"invoice": {
"type": "string",
"format": "uri",
"description": "The unique reference to the invoice template."
},
"invoiceLine": {
"type": "string",
"format": "uri",
"description": "The unique reference to the invoiceLine template."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the templates resource.",
"required": true
}
}
},
"totals": {
"type": "object",
"description": "",
"properties": {
"drafts": {
"type": "string",
"format": "uri",
"description": "The unique reference to the draft invoice totals for this customer."
},
"booked": {
"type": "string",
"format": "uri",
"description": "The unique reference to the booked invoice totals for this customer."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the totals resource for this customer.",
"required": true
}
}
},
"invoices": {
"type": "object",
"description": "",
"properties": {
"drafts": {
"type": "string",
"format": "uri",
"description": "The unique reference to the draft invoices for this customer."
},
"booked": {
"type": "string",
"format": "uri",
"description": "The unique reference to the booked invoices for this customer."
},
"self": {
"type": "string",
"format": "uri",
"description": "A unique link reference to the invoices resource for this customer.",
"required": true
}
}
},
"self": {
"type": "string",
"format": "uri",
"description": "The unique self reference of the customer resource.",
"required": true
}
}
}
},
"metaData": {
"type": "object",
"description": "Information about possible actions, endpoints and resource paths related to the endpoint."
},
"pagination": {
"type": "object",
"description": "Information about the pagination."
},
"self": {
"type": "string",
"format": "uri",
"description": "The unique self reference of the customer collection resource.",
"required": true
}
}
}
@@ -0,0 +1,37 @@
<?php
/**
* This is the processor that handles the certificate download requests
* It is here to avoid direct access to the certificate download API
*/
require_once '../../../config.php';
global $WORDPRESS_STATIC_TOKEN;
// Male sure the config file is loaded and configured
if (!isset($WORDPRESS_STATIC_TOKEN)) {
header('HTTP/1.0 500 Internal Server Error');
return;
}
// Get the certificate ID from the query string
$certificate_id = $_GET['certificate_id'] ?? '';
// Check if the certificate ID is a number
if (!is_numeric($certificate_id) || $certificate_id < 1) {
header('HTTP/1.0 403 Bad Request');
return;
}
// TODO: Add authentication here
// Check if the certificate exists in the /output/certificates folder
if (!file_exists("../output/certificates/wash_certificate_" . $certificate_id . ".pdf")) {
header('HTTP/1.0 403 Bad Request');
return;
}
// Set the headers
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="wash certificate ' . $certificate_id . '.pdf"');
// Output the certificate
readfile("../output/certificates/wash_certificate_" . $certificate_id . ".pdf");
exit;
@@ -0,0 +1,23 @@
{
"repositories": [
{
"type": "composer",
"url": "http://packagist.org",
"options": {
"ssl": {
"verify_peer": false
}
}
}
],
"require": {
"phpoffice/phpspreadsheet": "^3.4",
"mpdf/mpdf": "^8.2",
"dompdf/dompdf": "^3.0",
"tecnickcom/tcpdf": "^6.7",
"mitoteam/jpgraph": "^10.4",
"setasign/fpdi": "^2.6",
"setasign/fpdf": "^1.8",
"ext-mysqli": "*"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
<?php
$type = 'Core';
$name = 'Courier';
$up = -100;
$ut = 50;
for ( $i = 0; $i <= 255; $i++ )
$cw[chr($i)] = 600;
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,10 @@
<?php
$type = 'Core';
$name = 'Courier-Bold';
$up = -100;
$ut = 50;
for ( $i = 0; $i <= 255; $i++ )
$cw[chr($i)] = 600;
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,10 @@
<?php
$type = 'Core';
$name = 'Courier-BoldOblique';
$up = -100;
$ut = 50;
for ( $i = 0; $i <= 255; $i++ )
$cw[chr($i)] = 600;
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,10 @@
<?php
$type = 'Core';
$name = 'Courier-Oblique';
$up = -100;
$ut = 50;
for ( $i = 0; $i <= 255; $i++ )
$cw[chr($i)] = 600;
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Helvetica';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278,
chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 278, '"' => 355, '#' => 556, '$' => 556, '%' => 889, '&' => 667, '\'' => 191, '(' => 333, ')' => 333, '*' => 389, '+' => 584,
',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 278, ';' => 278, '<' => 584, '=' => 584, '>' => 584, '?' => 556, '@' => 1015, 'A' => 667,
'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 500, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944,
'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 278, '\\' => 278, ']' => 278, '^' => 469, '_' => 556, '`' => 333, 'a' => 556, 'b' => 556, 'c' => 500, 'd' => 556, 'e' => 556, 'f' => 278, 'g' => 556, 'h' => 556, 'i' => 222, 'j' => 222, 'k' => 500, 'l' => 222, 'm' => 833,
'n' => 556, 'o' => 556, 'p' => 556, 'q' => 556, 'r' => 333, 's' => 500, 't' => 278, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 500, '{' => 334, '|' => 260, '}' => 334, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 222, chr(131) => 556,
chr(132) => 333, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 222, chr(146) => 222, chr(147) => 333, chr(148) => 333, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 500, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 260, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333,
chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 556, chr(182) => 537, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667,
chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 500, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 556, chr(241) => 556,
chr(242) => 556, chr(243) => 556, chr(244) => 556, chr(245) => 556, chr(246) => 556, chr(247) => 584, chr(248) => 611, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Helvetica-Bold';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278,
chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 333, '"' => 474, '#' => 556, '$' => 556, '%' => 889, '&' => 722, '\'' => 238, '(' => 333, ')' => 333, '*' => 389, '+' => 584,
',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 333, ';' => 333, '<' => 584, '=' => 584, '>' => 584, '?' => 611, '@' => 975, 'A' => 722,
'B' => 722, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 556, 'K' => 722, 'L' => 611, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944,
'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 584, '_' => 556, '`' => 333, 'a' => 556, 'b' => 611, 'c' => 556, 'd' => 611, 'e' => 556, 'f' => 333, 'g' => 611, 'h' => 611, 'i' => 278, 'j' => 278, 'k' => 556, 'l' => 278, 'm' => 889,
'n' => 611, 'o' => 611, 'p' => 611, 'q' => 611, 'r' => 389, 's' => 556, 't' => 333, 'u' => 611, 'v' => 556, 'w' => 778, 'x' => 556, 'y' => 556, 'z' => 500, '{' => 389, '|' => 280, '}' => 389, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 278, chr(131) => 556,
chr(132) => 500, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 278, chr(146) => 278, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 556, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 280, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333,
chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 611, chr(182) => 556, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722,
chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 556, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 611, chr(241) => 611,
chr(242) => 611, chr(243) => 611, chr(244) => 611, chr(245) => 611, chr(246) => 611, chr(247) => 584, chr(248) => 611, chr(249) => 611, chr(250) => 611, chr(251) => 611, chr(252) => 611, chr(253) => 556, chr(254) => 611, chr(255) => 556);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Helvetica-BoldOblique';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278,
chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 333, '"' => 474, '#' => 556, '$' => 556, '%' => 889, '&' => 722, '\'' => 238, '(' => 333, ')' => 333, '*' => 389, '+' => 584,
',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 333, ';' => 333, '<' => 584, '=' => 584, '>' => 584, '?' => 611, '@' => 975, 'A' => 722,
'B' => 722, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 556, 'K' => 722, 'L' => 611, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944,
'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 584, '_' => 556, '`' => 333, 'a' => 556, 'b' => 611, 'c' => 556, 'd' => 611, 'e' => 556, 'f' => 333, 'g' => 611, 'h' => 611, 'i' => 278, 'j' => 278, 'k' => 556, 'l' => 278, 'm' => 889,
'n' => 611, 'o' => 611, 'p' => 611, 'q' => 611, 'r' => 389, 's' => 556, 't' => 333, 'u' => 611, 'v' => 556, 'w' => 778, 'x' => 556, 'y' => 556, 'z' => 500, '{' => 389, '|' => 280, '}' => 389, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 278, chr(131) => 556,
chr(132) => 500, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 278, chr(146) => 278, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 556, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 280, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333,
chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 611, chr(182) => 556, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722,
chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 556, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 611, chr(241) => 611,
chr(242) => 611, chr(243) => 611, chr(244) => 611, chr(245) => 611, chr(246) => 611, chr(247) => 584, chr(248) => 611, chr(249) => 611, chr(250) => 611, chr(251) => 611, chr(252) => 611, chr(253) => 556, chr(254) => 611, chr(255) => 556);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Helvetica-Oblique';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278,
chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 278, '"' => 355, '#' => 556, '$' => 556, '%' => 889, '&' => 667, '\'' => 191, '(' => 333, ')' => 333, '*' => 389, '+' => 584,
',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 278, ';' => 278, '<' => 584, '=' => 584, '>' => 584, '?' => 556, '@' => 1015, 'A' => 667,
'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 500, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944,
'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 278, '\\' => 278, ']' => 278, '^' => 469, '_' => 556, '`' => 333, 'a' => 556, 'b' => 556, 'c' => 500, 'd' => 556, 'e' => 556, 'f' => 278, 'g' => 556, 'h' => 556, 'i' => 222, 'j' => 222, 'k' => 500, 'l' => 222, 'm' => 833,
'n' => 556, 'o' => 556, 'p' => 556, 'q' => 556, 'r' => 333, 's' => 500, 't' => 278, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 500, '{' => 334, '|' => 260, '}' => 334, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 222, chr(131) => 556,
chr(132) => 333, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 222, chr(146) => 222, chr(147) => 333, chr(148) => 333, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 500, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 260, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333,
chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 556, chr(182) => 537, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667,
chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 500, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 556, chr(241) => 556,
chr(242) => 556, chr(243) => 556, chr(244) => 556, chr(245) => 556, chr(246) => 556, chr(247) => 584, chr(248) => 611, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,20 @@
<?php
$type = 'Core';
$name = 'Symbol';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250,
chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 713, '#' => 500, '$' => 549, '%' => 833, '&' => 778, '\'' => 439, '(' => 333, ')' => 333, '*' => 500, '+' => 549,
',' => 250, '-' => 549, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 278, ';' => 278, '<' => 549, '=' => 549, '>' => 549, '?' => 444, '@' => 549, 'A' => 722,
'B' => 667, 'C' => 722, 'D' => 612, 'E' => 611, 'F' => 763, 'G' => 603, 'H' => 722, 'I' => 333, 'J' => 631, 'K' => 722, 'L' => 686, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 768, 'Q' => 741, 'R' => 556, 'S' => 592, 'T' => 611, 'U' => 690, 'V' => 439, 'W' => 768,
'X' => 645, 'Y' => 795, 'Z' => 611, '[' => 333, '\\' => 863, ']' => 333, '^' => 658, '_' => 500, '`' => 500, 'a' => 631, 'b' => 549, 'c' => 549, 'd' => 494, 'e' => 439, 'f' => 521, 'g' => 411, 'h' => 603, 'i' => 329, 'j' => 603, 'k' => 549, 'l' => 549, 'm' => 576,
'n' => 521, 'o' => 549, 'p' => 549, 'q' => 521, 'r' => 549, 's' => 603, 't' => 439, 'u' => 576, 'v' => 713, 'w' => 686, 'x' => 493, 'y' => 686, 'z' => 494, '{' => 480, '|' => 200, '}' => 480, '~' => 549, chr(127) => 0, chr(128) => 0, chr(129) => 0, chr(130) => 0, chr(131) => 0,
chr(132) => 0, chr(133) => 0, chr(134) => 0, chr(135) => 0, chr(136) => 0, chr(137) => 0, chr(138) => 0, chr(139) => 0, chr(140) => 0, chr(141) => 0, chr(142) => 0, chr(143) => 0, chr(144) => 0, chr(145) => 0, chr(146) => 0, chr(147) => 0, chr(148) => 0, chr(149) => 0, chr(150) => 0, chr(151) => 0, chr(152) => 0, chr(153) => 0,
chr(154) => 0, chr(155) => 0, chr(156) => 0, chr(157) => 0, chr(158) => 0, chr(159) => 0, chr(160) => 750, chr(161) => 620, chr(162) => 247, chr(163) => 549, chr(164) => 167, chr(165) => 713, chr(166) => 500, chr(167) => 753, chr(168) => 753, chr(169) => 753, chr(170) => 753, chr(171) => 1042, chr(172) => 987, chr(173) => 603, chr(174) => 987, chr(175) => 603,
chr(176) => 400, chr(177) => 549, chr(178) => 411, chr(179) => 549, chr(180) => 549, chr(181) => 713, chr(182) => 494, chr(183) => 460, chr(184) => 549, chr(185) => 549, chr(186) => 549, chr(187) => 549, chr(188) => 1000, chr(189) => 603, chr(190) => 1000, chr(191) => 658, chr(192) => 823, chr(193) => 686, chr(194) => 795, chr(195) => 987, chr(196) => 768, chr(197) => 768,
chr(198) => 823, chr(199) => 768, chr(200) => 768, chr(201) => 713, chr(202) => 713, chr(203) => 713, chr(204) => 713, chr(205) => 713, chr(206) => 713, chr(207) => 713, chr(208) => 768, chr(209) => 713, chr(210) => 790, chr(211) => 790, chr(212) => 890, chr(213) => 823, chr(214) => 549, chr(215) => 250, chr(216) => 713, chr(217) => 603, chr(218) => 603, chr(219) => 1042,
chr(220) => 987, chr(221) => 603, chr(222) => 987, chr(223) => 603, chr(224) => 494, chr(225) => 329, chr(226) => 790, chr(227) => 790, chr(228) => 786, chr(229) => 713, chr(230) => 384, chr(231) => 384, chr(232) => 384, chr(233) => 384, chr(234) => 384, chr(235) => 384, chr(236) => 494, chr(237) => 494, chr(238) => 494, chr(239) => 494, chr(240) => 0, chr(241) => 329,
chr(242) => 274, chr(243) => 686, chr(244) => 686, chr(245) => 686, chr(246) => 384, chr(247) => 384, chr(248) => 384, chr(249) => 384, chr(250) => 384, chr(251) => 384, chr(252) => 494, chr(253) => 494, chr(254) => 494, chr(255) => 0);
$uv = array(32 => 160, 33 => 33, 34 => 8704, 35 => 35, 36 => 8707, 37 => array(37, 2), 39 => 8715, 40 => array(40, 2), 42 => 8727, 43 => array(43, 2), 45 => 8722, 46 => array(46, 18), 64 => 8773, 65 => array(913, 2), 67 => 935, 68 => array(916, 2), 70 => 934, 71 => 915, 72 => 919, 73 => 921, 74 => 977, 75 => array(922, 4), 79 => array(927, 2), 81 => 920, 82 => 929, 83 => array(931, 3), 86 => 962, 87 => 937, 88 => 926, 89 => 936, 90 => 918, 91 => 91, 92 => 8756, 93 => 93, 94 => 8869, 95 => 95, 96 => 63717, 97 => array(945, 2), 99 => 967, 100 => array(948, 2), 102 => 966, 103 => 947, 104 => 951, 105 => 953, 106 => 981, 107 => array(954, 4), 111 => array(959, 2), 113 => 952, 114 => 961, 115 => array(963, 3), 118 => 982, 119 => 969, 120 => 958, 121 => 968, 122 => 950, 123 => array(123, 3), 126 => 8764, 160 => 8364, 161 => 978, 162 => 8242, 163 => 8804, 164 => 8725, 165 => 8734, 166 => 402, 167 => 9827, 168 => 9830, 169 => 9829, 170 => 9824, 171 => 8596, 172 => array(8592, 4), 176 => array(176, 2), 178 => 8243, 179 => 8805, 180 => 215, 181 => 8733, 182 => 8706, 183 => 8226, 184 => 247, 185 => array(8800, 2), 187 => 8776, 188 => 8230, 189 => array(63718, 2), 191 => 8629, 192 => 8501, 193 => 8465, 194 => 8476, 195 => 8472, 196 => 8855, 197 => 8853, 198 => 8709, 199 => array(8745, 2), 201 => 8835, 202 => 8839, 203 => 8836, 204 => 8834, 205 => 8838, 206 => array(8712, 2), 208 => 8736, 209 => 8711, 210 => 63194, 211 => 63193, 212 => 63195, 213 => 8719, 214 => 8730, 215 => 8901, 216 => 172, 217 => array(8743, 2), 219 => 8660, 220 => array(8656, 4), 224 => 9674, 225 => 9001, 226 => array(63720, 3), 229 => 8721, 230 => array(63723, 10), 241 => 9002, 242 => 8747, 243 => 8992, 244 => 63733, 245 => 8993, 246 => array(63734, 9));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Times-Roman';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250,
chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 408, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 180, '(' => 333, ')' => 333, '*' => 500, '+' => 564,
',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 278, ';' => 278, '<' => 564, '=' => 564, '>' => 564, '?' => 444, '@' => 921, 'A' => 722,
'B' => 667, 'C' => 667, 'D' => 722, 'E' => 611, 'F' => 556, 'G' => 722, 'H' => 722, 'I' => 333, 'J' => 389, 'K' => 722, 'L' => 611, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 556, 'Q' => 722, 'R' => 667, 'S' => 556, 'T' => 611, 'U' => 722, 'V' => 722, 'W' => 944,
'X' => 722, 'Y' => 722, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 469, '_' => 500, '`' => 333, 'a' => 444, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 500, 'i' => 278, 'j' => 278, 'k' => 500, 'l' => 278, 'm' => 778,
'n' => 500, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 333, 's' => 389, 't' => 278, 'u' => 500, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 444, '{' => 480, '|' => 200, '}' => 480, '~' => 541, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500,
chr(132) => 444, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 889, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 444, chr(148) => 444, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 980,
chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 444, chr(159) => 722, chr(160) => 250, chr(161) => 333, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 200, chr(167) => 500, chr(168) => 333, chr(169) => 760, chr(170) => 276, chr(171) => 500, chr(172) => 564, chr(173) => 333, chr(174) => 760, chr(175) => 333,
chr(176) => 400, chr(177) => 564, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 500, chr(182) => 453, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 310, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 444, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722,
chr(198) => 889, chr(199) => 667, chr(200) => 611, chr(201) => 611, chr(202) => 611, chr(203) => 611, chr(204) => 333, chr(205) => 333, chr(206) => 333, chr(207) => 333, chr(208) => 722, chr(209) => 722, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 564, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 722, chr(222) => 556, chr(223) => 500, chr(224) => 444, chr(225) => 444, chr(226) => 444, chr(227) => 444, chr(228) => 444, chr(229) => 444, chr(230) => 667, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 500,
chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 564, chr(248) => 500, chr(249) => 500, chr(250) => 500, chr(251) => 500, chr(252) => 500, chr(253) => 500, chr(254) => 500, chr(255) => 500);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Times-Bold';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250,
chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 555, '#' => 500, '$' => 500, '%' => 1000, '&' => 833, '\'' => 278, '(' => 333, ')' => 333, '*' => 500, '+' => 570,
',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 570, '=' => 570, '>' => 570, '?' => 500, '@' => 930, 'A' => 722,
'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 778, 'I' => 389, 'J' => 500, 'K' => 778, 'L' => 667, 'M' => 944, 'N' => 722, 'O' => 778, 'P' => 611, 'Q' => 778, 'R' => 722, 'S' => 556, 'T' => 667, 'U' => 722, 'V' => 722, 'W' => 1000,
'X' => 722, 'Y' => 722, 'Z' => 667, '[' => 333, '\\' => 278, ']' => 333, '^' => 581, '_' => 500, '`' => 333, 'a' => 500, 'b' => 556, 'c' => 444, 'd' => 556, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 556, 'i' => 278, 'j' => 333, 'k' => 556, 'l' => 278, 'm' => 833,
'n' => 556, 'o' => 500, 'p' => 556, 'q' => 556, 'r' => 444, 's' => 389, 't' => 333, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 444, '{' => 394, '|' => 220, '}' => 394, '~' => 520, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500,
chr(132) => 500, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 667, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 444, chr(159) => 722, chr(160) => 250, chr(161) => 333, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 220, chr(167) => 500, chr(168) => 333, chr(169) => 747, chr(170) => 300, chr(171) => 500, chr(172) => 570, chr(173) => 333, chr(174) => 747, chr(175) => 333,
chr(176) => 400, chr(177) => 570, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 556, chr(182) => 540, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 330, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722,
chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 389, chr(205) => 389, chr(206) => 389, chr(207) => 389, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 570, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 722, chr(222) => 611, chr(223) => 556, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 722, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 556,
chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 570, chr(248) => 500, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Times-BoldItalic';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250,
chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 389, '"' => 555, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 278, '(' => 333, ')' => 333, '*' => 500, '+' => 570,
',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 570, '=' => 570, '>' => 570, '?' => 500, '@' => 832, 'A' => 667,
'B' => 667, 'C' => 667, 'D' => 722, 'E' => 667, 'F' => 667, 'G' => 722, 'H' => 778, 'I' => 389, 'J' => 500, 'K' => 667, 'L' => 611, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 611, 'Q' => 722, 'R' => 667, 'S' => 556, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 889,
'X' => 667, 'Y' => 611, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 570, '_' => 500, '`' => 333, 'a' => 500, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 556, 'i' => 278, 'j' => 278, 'k' => 500, 'l' => 278, 'm' => 778,
'n' => 556, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 389, 's' => 389, 't' => 278, 'u' => 556, 'v' => 444, 'w' => 667, 'x' => 500, 'y' => 444, 'z' => 389, '{' => 348, '|' => 220, '}' => 348, '~' => 570, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500,
chr(132) => 500, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 944, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 1000,
chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 389, chr(159) => 611, chr(160) => 250, chr(161) => 389, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 220, chr(167) => 500, chr(168) => 333, chr(169) => 747, chr(170) => 266, chr(171) => 500, chr(172) => 606, chr(173) => 333, chr(174) => 747, chr(175) => 333,
chr(176) => 400, chr(177) => 570, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 576, chr(182) => 500, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 300, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667,
chr(198) => 944, chr(199) => 667, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 389, chr(205) => 389, chr(206) => 389, chr(207) => 389, chr(208) => 722, chr(209) => 722, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 570, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 611, chr(222) => 611, chr(223) => 500, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 722, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 556,
chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 570, chr(248) => 500, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 444, chr(254) => 500, chr(255) => 444);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,21 @@
<?php
$type = 'Core';
$name = 'Times-Italic';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250,
chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 420, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 214, '(' => 333, ')' => 333, '*' => 500, '+' => 675,
',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 675, '=' => 675, '>' => 675, '?' => 500, '@' => 920, 'A' => 611,
'B' => 611, 'C' => 667, 'D' => 722, 'E' => 611, 'F' => 611, 'G' => 722, 'H' => 722, 'I' => 333, 'J' => 444, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 667, 'O' => 722, 'P' => 611, 'Q' => 722, 'R' => 611, 'S' => 500, 'T' => 556, 'U' => 722, 'V' => 611, 'W' => 833,
'X' => 611, 'Y' => 556, 'Z' => 556, '[' => 389, '\\' => 278, ']' => 389, '^' => 422, '_' => 500, '`' => 333, 'a' => 500, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 278, 'g' => 500, 'h' => 500, 'i' => 278, 'j' => 278, 'k' => 444, 'l' => 278, 'm' => 722,
'n' => 500, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 389, 's' => 389, 't' => 278, 'u' => 500, 'v' => 444, 'w' => 667, 'x' => 444, 'y' => 444, 'z' => 389, '{' => 400, '|' => 275, '}' => 400, '~' => 541, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500,
chr(132) => 556, chr(133) => 889, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 500, chr(139) => 333, chr(140) => 944, chr(141) => 350, chr(142) => 556, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 556, chr(148) => 556, chr(149) => 350, chr(150) => 500, chr(151) => 889, chr(152) => 333, chr(153) => 980,
chr(154) => 389, chr(155) => 333, chr(156) => 667, chr(157) => 350, chr(158) => 389, chr(159) => 556, chr(160) => 250, chr(161) => 389, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 275, chr(167) => 500, chr(168) => 333, chr(169) => 760, chr(170) => 276, chr(171) => 500, chr(172) => 675, chr(173) => 333, chr(174) => 760, chr(175) => 333,
chr(176) => 400, chr(177) => 675, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 500, chr(182) => 523, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 310, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 611, chr(193) => 611, chr(194) => 611, chr(195) => 611, chr(196) => 611, chr(197) => 611,
chr(198) => 889, chr(199) => 667, chr(200) => 611, chr(201) => 611, chr(202) => 611, chr(203) => 611, chr(204) => 333, chr(205) => 333, chr(206) => 333, chr(207) => 333, chr(208) => 722, chr(209) => 667, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 675, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722,
chr(220) => 722, chr(221) => 556, chr(222) => 611, chr(223) => 500, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 667, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 500,
chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 675, chr(248) => 500, chr(249) => 500, chr(250) => 500, chr(251) => 500, chr(252) => 500, chr(253) => 444, chr(254) => 500, chr(255) => 444);
$enc = 'cp1252';
$uv = array(0 => array(0, 128), 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => array(8224, 2), 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => array(8216, 2), 147 => array(8220, 2), 149 => 8226, 150 => array(8211, 2), 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => array(160, 96));
?>
@@ -0,0 +1,20 @@
<?php
$type = 'Core';
$name = 'ZapfDingbats';
$up = -100;
$ut = 50;
$cw = array(
chr(0) => 0, chr(1) => 0, chr(2) => 0, chr(3) => 0, chr(4) => 0, chr(5) => 0, chr(6) => 0, chr(7) => 0, chr(8) => 0, chr(9) => 0, chr(10) => 0, chr(11) => 0, chr(12) => 0, chr(13) => 0, chr(14) => 0, chr(15) => 0, chr(16) => 0, chr(17) => 0, chr(18) => 0, chr(19) => 0, chr(20) => 0, chr(21) => 0,
chr(22) => 0, chr(23) => 0, chr(24) => 0, chr(25) => 0, chr(26) => 0, chr(27) => 0, chr(28) => 0, chr(29) => 0, chr(30) => 0, chr(31) => 0, ' ' => 278, '!' => 974, '"' => 961, '#' => 974, '$' => 980, '%' => 719, '&' => 789, '\'' => 790, '(' => 791, ')' => 690, '*' => 960, '+' => 939,
',' => 549, '-' => 855, '.' => 911, '/' => 933, '0' => 911, '1' => 945, '2' => 974, '3' => 755, '4' => 846, '5' => 762, '6' => 761, '7' => 571, '8' => 677, '9' => 763, ':' => 760, ';' => 759, '<' => 754, '=' => 494, '>' => 552, '?' => 537, '@' => 577, 'A' => 692,
'B' => 786, 'C' => 788, 'D' => 788, 'E' => 790, 'F' => 793, 'G' => 794, 'H' => 816, 'I' => 823, 'J' => 789, 'K' => 841, 'L' => 823, 'M' => 833, 'N' => 816, 'O' => 831, 'P' => 923, 'Q' => 744, 'R' => 723, 'S' => 749, 'T' => 790, 'U' => 792, 'V' => 695, 'W' => 776,
'X' => 768, 'Y' => 792, 'Z' => 759, '[' => 707, '\\' => 708, ']' => 682, '^' => 701, '_' => 826, '`' => 815, 'a' => 789, 'b' => 789, 'c' => 707, 'd' => 687, 'e' => 696, 'f' => 689, 'g' => 786, 'h' => 787, 'i' => 713, 'j' => 791, 'k' => 785, 'l' => 791, 'm' => 873,
'n' => 761, 'o' => 762, 'p' => 762, 'q' => 759, 'r' => 759, 's' => 892, 't' => 892, 'u' => 788, 'v' => 784, 'w' => 438, 'x' => 138, 'y' => 277, 'z' => 415, '{' => 392, '|' => 392, '}' => 668, '~' => 668, chr(127) => 0, chr(128) => 390, chr(129) => 390, chr(130) => 317, chr(131) => 317,
chr(132) => 276, chr(133) => 276, chr(134) => 509, chr(135) => 509, chr(136) => 410, chr(137) => 410, chr(138) => 234, chr(139) => 234, chr(140) => 334, chr(141) => 334, chr(142) => 0, chr(143) => 0, chr(144) => 0, chr(145) => 0, chr(146) => 0, chr(147) => 0, chr(148) => 0, chr(149) => 0, chr(150) => 0, chr(151) => 0, chr(152) => 0, chr(153) => 0,
chr(154) => 0, chr(155) => 0, chr(156) => 0, chr(157) => 0, chr(158) => 0, chr(159) => 0, chr(160) => 0, chr(161) => 732, chr(162) => 544, chr(163) => 544, chr(164) => 910, chr(165) => 667, chr(166) => 760, chr(167) => 760, chr(168) => 776, chr(169) => 595, chr(170) => 694, chr(171) => 626, chr(172) => 788, chr(173) => 788, chr(174) => 788, chr(175) => 788,
chr(176) => 788, chr(177) => 788, chr(178) => 788, chr(179) => 788, chr(180) => 788, chr(181) => 788, chr(182) => 788, chr(183) => 788, chr(184) => 788, chr(185) => 788, chr(186) => 788, chr(187) => 788, chr(188) => 788, chr(189) => 788, chr(190) => 788, chr(191) => 788, chr(192) => 788, chr(193) => 788, chr(194) => 788, chr(195) => 788, chr(196) => 788, chr(197) => 788,
chr(198) => 788, chr(199) => 788, chr(200) => 788, chr(201) => 788, chr(202) => 788, chr(203) => 788, chr(204) => 788, chr(205) => 788, chr(206) => 788, chr(207) => 788, chr(208) => 788, chr(209) => 788, chr(210) => 788, chr(211) => 788, chr(212) => 894, chr(213) => 838, chr(214) => 1016, chr(215) => 458, chr(216) => 748, chr(217) => 924, chr(218) => 748, chr(219) => 918,
chr(220) => 927, chr(221) => 928, chr(222) => 928, chr(223) => 834, chr(224) => 873, chr(225) => 828, chr(226) => 924, chr(227) => 924, chr(228) => 917, chr(229) => 930, chr(230) => 931, chr(231) => 463, chr(232) => 883, chr(233) => 836, chr(234) => 836, chr(235) => 867, chr(236) => 867, chr(237) => 696, chr(238) => 696, chr(239) => 874, chr(240) => 0, chr(241) => 874,
chr(242) => 760, chr(243) => 946, chr(244) => 771, chr(245) => 865, chr(246) => 771, chr(247) => 888, chr(248) => 967, chr(249) => 888, chr(250) => 831, chr(251) => 873, chr(252) => 927, chr(253) => 970, chr(254) => 918, chr(255) => 0);
$uv = array(32 => 32, 33 => array(9985, 4), 37 => 9742, 38 => array(9990, 4), 42 => 9755, 43 => 9758, 44 => array(9996, 28), 72 => 9733, 73 => array(10025, 35), 108 => 9679, 109 => 10061, 110 => 9632, 111 => array(10063, 4), 115 => 9650, 116 => 9660, 117 => 9670, 118 => 10070, 119 => 9687, 120 => array(10072, 7), 128 => array(10088, 14), 161 => array(10081, 7), 168 => 9827, 169 => 9830, 170 => 9829, 171 => 9824, 172 => array(9312, 10), 182 => array(10102, 31), 213 => 8594, 214 => array(8596, 2), 216 => array(10136, 24), 241 => array(10161, 14));
?>
Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

@@ -0,0 +1,143 @@
<?php
/**
* This is the generator for the wash certificates (Used by the WordPress plugin)
* The reason behind this being outside the WordPress plugin is that the PHPExcel library is not compatible with the WordPress plugin, and it is easier to maintain the generator here.
*/
// Load the config file
require_once '../../config.php';
require_once '../../vendor/autoload.php';
/** Load the relevant classes */
require_once '../../interfaces/minio_wash_certificates_i.php';
require_once '../../traits/minio_t.php';
require_once '../../classes/wash_certificate_store.php';
use classes\wash_certificate_store;
use objects\bookings_o;
global $WORDPRESS_STATIC_TOKEN;
// Validate the token was loaded from the config file
if (!isset($WORDPRESS_STATIC_TOKEN) || $WORDPRESS_STATIC_TOKEN === '') {
echo 'Missing token in config file';
exit;
}
require 'vendor/autoload.php';
require_once 'twc_spreadsheet_class.php';
// Set CORS headers
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: Content-Type");
// Set the timezone
date_default_timezone_set('Europe/Copenhagen');
// Set the time format
setlocale(LC_TIME, 'da_DK');
// Create the output directory if it does not exist
if (!file_exists('output/certificates')) {
mkdir('output/certificates', 0777, true);
}
// Create the output/certificates directory if it does not exist
if (!file_exists('output/certificates')) {
mkdir('output/certificates', 0777, true);
}
// If the request is from the command line
if (php_sapi_name() === 'cli') {
// Set the $_GET variable to default values
$_GET['secret_token'] = $WORDPRESS_STATIC_TOKEN;
$_GET['sealOrPlumber'] = '12345';
$_GET['performedBy'] = 'JH';
$_GET['bookingId'] = '12345';
$_GET['regNumber'] = 'AB12345';
$_GET['regNumberTrailer'] = 'AB12345';
$_GET['department'] = 'taastrup';
}
// Require the $_GET variable secret_token to be set
if (!isset($_GET['secret_token'])) {
echo 'Invalid request';
exit;
}
// make sure the secret token is correct
if ($_GET['secret_token'] !== $WORDPRESS_STATIC_TOKEN) {
echo 'Invalid secret token';
exit;
}
// Check if the $_GET variable justDownload is set
if (isset($_GET['justDownload'])) {
// Require the $_GET variable bookingId to be set
if (!isset($_GET['bookingId'])) {
echo 'Missing required fields';
exit;
}
// Usage example
$wash_certificate_store = new wash_certificate_store();
// Return the certificate download URL
echo $wash_certificate_store->getWashCertificateDownload($_GET['bookingId']);
// Exit the script
exit;
}
// Require the $_GET variables sealOrPlumber, safetySeal, performedBy, and bookingId, regNumber, and regNumberTrailer to be set
if (!isset($_GET['sealOrPlumber']) || !isset($_GET['performedBy']) || !isset($_GET['bookingId']) || !isset($_GET['regNumber']) || !isset($_GET['regNumberTrailer']) || !isset($_GET['department'])) {
// We are missing some required fields in the query string
echo 'Missing required fields';
exit;
}
// Check if the certificate already exists in the bucket
$wash_certificate_store = new wash_certificate_store();
if ($wash_certificate_store->washCertificateExists($_GET['bookingId'])) {
// Return the certificate url
echo $wash_certificate_store->getWashCertificateDownload($_GET['bookingId']);
// Exit the script
exit;
} else {
// Check if the certificate exists in the filesystem (legacy system)
if (file_exists(dirname(__FILE__) . "/output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf")) {
// Upload the certificate to the bucket
$success = $wash_certificate_store->uploadFile("wash_certificate_" . $_GET['bookingId'] . ".pdf", dirname(__FILE__) . "/output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf");
// If the certificate was uploaded successfully, delete the local copy
if ($success) {
//unlink(dirname(__FILE__) . "/output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf");
// Return the certificate url
echo $wash_certificate_store->getWashCertificateDownload($_GET['bookingId']);
exit;
}
// If the certificate was not uploaded successfully, return an error
echo 'Failed to upload the certificate';
// Exit the script
exit;
}
}
// Usage example
$template = "templates/template2024julv3.xlsx";
$generator = new WashCertificateGenerator($template, $_GET['department']);
$generator->generateCertificate(dirname(__FILE__) . "/output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf", $_GET['sealOrPlumber'], $_GET['regNumber'], $_GET['regNumberTrailer'], $_GET['performedBy']);
// Determine the generated certificate name
$generatedCertificateName = "wash_certificate_" . $_GET['bookingId'] . ".pdf";
// Return the generated certificate path
$generatedCertificatePath = "output/certificates/wash_certificate_" . $_GET['bookingId'] . ".pdf";
// Upload the certificate to the bucket
$wash_certificate_store->uploadFile($generatedCertificateName, dirname(__FILE__) . '/' . $generatedCertificatePath);
// Delete the local copy of the certificate
//unlink(dirname(__FILE__) . '/' . $generatedCertificatePath);
// Set the status of the booking to completed
$booking = new bookings_o();
$booking->id = $_GET['bookingId'];
$booking->getObjectProperties();
$booking->status->set('completed');
$booking->washCertificateUrl->set('Protected URL');
$booking->washCertificateStatus->set('completed');
// Return the generated certificate object download URL
echo $wash_certificate_store->getWashCertificateDownload($_GET['bookingId']);
// Exit the script
exit;
@@ -0,0 +1,146 @@
<?php
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use setasign\Fpdi\Fpdi;
class WashCertificateGenerator
{
private string $template;
private string $department;
private string $color;
private string $colorBackground;
private array $signature;
private array $logo;
private Spreadsheet $spreadsheet;
private array $washPerformedAt;
public function __construct(string $template, string $department)
{
$this->template = $template;
$this->department = $department;
$this->color = '1488bc'; // Hex color code (Without the #)
$this->colorBackground = 'd9d9d9'; // Hex color code (Without the #)
$this->washPerformedAt = [
'hvidovre' => ['column' => 'A', 'row' => 21],
'taastrup' => ['column' => 'B', 'row' => 21],
'glostrup' => ['column' => 'C', 'row' => 21],
'køge' => ['column' => 'D', 'row' => 21],
'roskilde' => ['column' => 'E', 'row' => 21],
'aarhusC' => ['column' => 'F', 'row' => 21],
];
$this->signature = [
'path' => 'images/truckwash-underskrift.png',
'x' => 0,
'y' => 140,
'is-centered' => true,
'width' => 70,
];
$this->logo = [
'path' => 'images/truckwash-banner-png.png',
'x' => 0,
'y' => 20,
'is-centered' => true,
'width' => 80,
];
}
public function generateCertificate(string $outputPath, string $sealOrPlumNumber, string $regNumber, string $regNumberTrailer, string $carriedOutBy): void
{
$data = [
'sealOrPlumNumber' => $sealOrPlumNumber,
'regNumber' => $regNumber,
'regNumberTrailer' => $regNumberTrailer,
'carriedOutBy' => $carriedOutBy,
];
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(false);
$reader->setIncludeCharts(true);
$this->spreadsheet = $reader->load($this->template);
$activeWorksheet = $this->spreadsheet->getActiveSheet();
$activeWorksheet->setTitle('Wash Certificate');
$this->setCellValues($activeWorksheet, $data);
$this->setCellStyles($activeWorksheet);
$this->setPageSetup($activeWorksheet);
$this->exportToPdf($outputPath);
$this->addImagesToPdf($outputPath);
$this->spreadsheet->disconnectWorksheets();
unset($this->spreadsheet);
}
private function setCellValues($activeWorksheet, array $data): void
{
$activeWorksheet->setCellValue('A9', $data['sealOrPlumNumber']);
$activeWorksheet->setCellValue('B17', $data['regNumber']);
$activeWorksheet->setCellValue('B18', $data['regNumberTrailer']);
$activeWorksheet->setCellValue('F18', $data['carriedOutBy']);
$activeWorksheet->setCellValue($this->washPerformedAt[$this->department]['column'] . $this->washPerformedAt[$this->department]['row'], 'X');
}
private function setCellStyles($activeWorksheet): void
{
$activeWorksheet->getStyle($this->washPerformedAt[$this->department]['column'] . ($this->washPerformedAt[$this->department]['row'] + 1))->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->color);
$activeWorksheet->getStyle('A9')->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->colorBackground);
$activeWorksheet->getStyle('B17')->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->colorBackground);
$activeWorksheet->getStyle('B18')->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->colorBackground);
$activeWorksheet->getStyle('F18')->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->colorBackground);
foreach ( $this->washPerformedAt as $key => $value ) {
if ($key !== $this->department) {
$activeWorksheet->getStyle($value['column'] . ($value['row'] + 1))->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB($this->colorBackground);
}
}
// Make sure the cells 32A:32F has the font size 11
$activeWorksheet->getStyle('A32:F32')->getFont()->setSize(11);
// Make sure the cells 35A:35F has the font size 11
$activeWorksheet->getStyle('A35:F35')->getFont()->setSize(11);
}
private function setPageSetup($activeWorksheet): void
{
$activeWorksheet->getPageSetup()->setPaperSize(PageSetup::PAPERSIZE_A4);
$activeWorksheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT);
$activeWorksheet->getPageSetup()->setFitToWidth(1);
$activeWorksheet->getPageSetup()->setFitToHeight(0);
$activeWorksheet->getPageMargins()->setTop(0.75);
$activeWorksheet->getPageMargins()->setRight(0.75);
$activeWorksheet->getPageMargins()->setLeft(0.75);
$activeWorksheet->getPageMargins()->setBottom(0.75);
$activeWorksheet->getPageMargins()->setHeader(0.3);
$activeWorksheet->getPageMargins()->setFooter(0.3);
}
private function exportToPdf(string $outputPath): void
{
$writer = IOFactory::createWriter($this->spreadsheet, 'Mpdf');
$writer->setIncludeCharts(false);
$writer->setPreCalculateFormulas(true);
$writer->save($outputPath);
}
private function addImagesToPdf(string $outputPath): void
{
$logo = $this->logo;
$signature = $this->signature;
// Center the logo and signature
$logo['x'] = $logo['is-centered'] ? (210 - $logo['width']) / 2 : $logo['x'];
$signature['x'] = $signature['is-centered'] ? (210 - $signature['width']) / 2 : $signature['x'];
$pdf = new Fpdi();
$pdf->AddPage();
$pdf->setSourceFile($outputPath);
$tplIdx = $pdf->importPage(1);
$pdf->useTemplate($tplIdx, 0, 0, 210);
$pdf->Image($logo['path'], $logo['x'], $logo['y'], $logo['width']);
$pdf->Image($signature['path'], $signature['x'], $signature['y'], $signature['width']);
$pdf->Output($outputPath, 'F');
}
}
+317
View File
@@ -0,0 +1,317 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use classes\response;
use classes\slack;
use classes\wash_certificate_store;
use classes\wordpress_bookings_remote;
use traits\db_object_t;
class bookings_o extends db
{
use db_object_t;
public object_property $customer_number;
public object_property $wash_type;
public object_property $contact_email;
public object_property $reference_number;
public object_property $regNrTraekker;
public object_property $regNrTrailer;
public object_property $washCertificateEmail;
public object_property $date;
public object_property $department;
public object_property $pickup_bool;
public object_property $notes;
public object_property $washCertificateStatus;
public object_property $washCertificateUrl;
public object_property $status;
public function structure(): void
{
$this->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);
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class cron_o extends db
{
use db_object_t;
public object_property $name;
public object_property $last_run;
public object_property $times_ran;
public function structure(): void
{
$this->setTable('cron');
}
public function getObjectProperties(): void
{
$this->name = new object_property($this->table, $this->id, 'name', 'string', true);
$this->last_run = new object_property($this->table, $this->id, 'last_run', 'datetime', true);
$this->times_ran = new object_property($this->table, $this->id, 'times_ran', 'int', true);
}
public function getByName(string $name): cron_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE name = '$name'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
}
return $this;
}
public function incrementTimesRan(int $id): void
{
global $db;
$this->id = $id;
// Update the record in the database
$sql = "UPDATE $this->table SET times_ran = times_ran + 1 WHERE id = $this->id";
$db->query($sql);
}
public function updateLastRun(int $id): void
{
global $db;
$this->id = $id;
// Update the record in the database
$sql = "UPDATE $this->table SET last_run = NOW() WHERE id = $this->id";
$db->query($sql);
}
public function getCronCreateIfNotExists(string $name): cron_o
{
global $db;
// Avoid SQL injection
$name = $db->escape_string($name);
// Check if the record exists
$sql = "SELECT id FROM $this->table WHERE name = '$name'";
$result = $db->query($sql);
if ($result->num_rows == 0) {
// Create a new record in the database
$sql = "INSERT INTO $this->table (name, last_run, times_ran) VALUES ('$name', NOW(), 0)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
}
return $this;
}
}
@@ -0,0 +1,82 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class customer_codes_o extends db
{
use db_object_t;
public object_property $user_id;
public object_property $code;
public function structure(): void
{
$this->setTable('customer_codes');
}
public function getObjectProperties(): void
{
$this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', true);
$this->code = new object_property($this->table, $this->id, 'code', 'string', true);
}
public function set(int $user_id, string|null $code): customer_codes_o
{
global $db, $response;
try {
// Avoid SQL injection
if (!is_null($code)) {
$code = $db->escape_string($code);
}
// Create a new record in the database ( Replace the code, if an entry already exists )
$sql = "INSERT INTO $this->table (user_id, code) VALUES ($user_id, '$code') ON DUPLICATE KEY UPDATE code = '$code'";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
return $this;
}
public function getCode(int $user_id): customer_codes_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE user_id = $user_id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $user_id;
$this->getObjectProperties();
} else {
$this->set($user_id, null);
}
return $this;
}
public function setCode(int $id, mixed $code): customer_codes_o
{
global $db, $response;
try {
// Avoid SQL injection
$code = $db->escape_string($code);
// Update the record in the database
$sql = "UPDATE $this->table SET code = '$code' WHERE user_id = $id";
$db->query($sql);
// Set the values of the object properties
$this->id = $id;
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
return $this;
}
}
@@ -0,0 +1,123 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class customer_notes_o extends db
{
use db_object_t;
public object_property $customer_id;
public object_property $note;
public object_property $cashier_id;
public object_property $deleted_at;
public function structure(): void
{
$this->setTable('customer_notes');
}
public function add(int $customer_id, string $note, int $cashier_id): customer_notes_o
{
global $db, $response;
try {
// Avoid SQL injection
$note = $db->escape_string($note);
// Create a new record in the database
$sql = "INSERT INTO $this->table (customer_id, note, cashier_id) VALUES ($customer_id, '$note', $cashier_id)";
$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_customer_notes($customer_id);
} catch (\Exception $e) {
$response->error($e->getMessage());
}
return $this;
}
public function getObjectProperties(): void
{
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
$this->note = new object_property($this->table, $this->id, 'note', 'string', true);
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function getCustomerNotesAsArray(int $user_id): array
{
global $response, $db;
// Check if the result is cached
$cached = redis->get_customer_notes($user_id);
if ($cached) {
$response->add_meta('cached', true);
return $cached;
}
$sql = "SELECT * FROM $this->table WHERE customer_id = $user_id AND deleted_at IS NULL";
$result = $db->query($sql);
$customer_notes = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$customer_notes[] = $row;
}
}
// Cache the result
redis->cache_customer_notes($user_id, $customer_notes);
return $customer_notes;
}
public function getCustomerNoteById(int $id): customer_notes_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function delete(int $id): void
{
global $db;
$this->id = $id;
$sql = "UPDATE $this->table SET deleted_at = NOW() WHERE id = $this->id";
$db->query($sql);
// Clear the cache
$customer_id = $this->getCustomerByNoteId($this->id);
redis->clear_customer_notes($customer_id);
}
public function getCustomerByNoteId(int $id): int
{
global $db;
$sql = "SELECT customer_id FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
return $row['customer_id'];
}
return 0;
}
public function restore(int $id): void
{
global $db;
$this->id = $id;
$sql = "UPDATE $this->table SET deleted_at = NULL WHERE id = $this->id";
$db->query($sql);
// Clear the cache
$customer_id = $this->getCustomerByNoteId($this->id);
redis->clear_customer_notes($customer_id);
}
}
@@ -0,0 +1,124 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use classes\response;
use traits\db_object_t;
class customer_vehicles_o extends db
{
use db_object_t;
public object_property $customer_id; // The id of the customer
public object_property $type; // The type of the vehicle
public object_property $reg; // The registration number of the vehicle
public object_property $notes; // The notes of the vehicle
public object_property $deleted_at; // The timestamp of when the record was "deleted"
public function structure(): void
{
$this->setTable('customer_vehicles');
}
public function getObjectProperties(): void
{
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
$this->type = new object_property($this->table, $this->id, 'type', 'string', true);
$this->reg = new object_property($this->table, $this->id, 'reg', 'string', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', true);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function add(int $customer_id, string $type, string $reg, string $notes): customer_vehicles_o
{
global $db, $response;
try {
// Avoid SQL injection
$type = $db->escape_string($type);
$reg = $db->escape_string($reg);
$notes = $db->escape_string($notes);
// Create a new record in the database
$sql = "INSERT INTO $this->table (customer_id, type, reg, notes) VALUES ($customer_id, '$type', '$reg', '$notes')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
return $this;
}
public function getCustomerVehiclesAsArray(int $customer_id): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE customer_id = $customer_id AND deleted_at IS NULL";
$result = $db->query($sql);
$customer_notes = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$customer_notes[] = $row;
}
}
return $customer_notes;
}
public function getCustomerVehiclesPaginated($customer_id, $page = 1 , $limit = 10): array
{
global /** @var response $response */
$db, $response;
$sql = "SELECT * FROM $this->table WHERE customer_id = $customer_id AND deleted_at IS NULL LIMIT $limit OFFSET " . ($page - 1) * $limit;
$result = $db->query($sql);
$array = $db->fetch_all($result);
$sql = "SELECT COUNT(*) as count FROM $this->table WHERE customer_id = $customer_id AND deleted_at IS NULL";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
$total = $row['count'];
$response->paginate($page, $limit, $total);
return $array;
}
public function getCustomerVehicleById(int $id): customer_vehicles_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function delete(int $id): void
{
global $db;
$this->id = $id;
$sql = "UPDATE $this->table SET deleted_at = NOW() WHERE id = $this->id";
$db->query($sql);
}
public function restore(int $id): void
{
global $db;
$this->id = $id;
$sql = "UPDATE $this->table SET deleted_at = NULL WHERE id = $this->id";
$db->query($sql);
}
public function getArrayByObjectProperties(): array
{
return [
'id' => $this->id,
'customer_id' => $this->customer_id->value(),
'type' => $this->type->value(),
'reg' => $this->reg->value(),
'notes' => $this->notes->value(),
'deleted_at' => $this->deleted_at->value()
];
}
}
@@ -0,0 +1,197 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class departments_o extends db
{
use db_object_t;
public object_property $name;
public object_property $description;
public object_property $economic_department_id; // The id of the department in the economic system (Can be null)
public object_property $slack_webhook; // The slack webhook for the department
public function structure(): void
{
$this->setTable('departments');
}
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();
}
public function getObjectProperties(): void
{
$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);
}
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) {
$departments = array_map(function ($department) {
return [
'name' => $department['name'],
'description' => $department['description'],
'id' => $department['id']
];
}, $departments);
}
return $departments;
}
public function getDepartmentById(int $id): 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) {
$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 selectId(int $department_id): self
{
$this->id = $department_id;
$this->getObjectProperties();
return $this;
}
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;
}
}
@@ -0,0 +1,75 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class economic_module_orders extends db
{
use db_object_t;
public object_property $economic_invoice_draft_id;
public object_property $economic_invoice_id;
public function structure(): void
{
$this->setTable('economic_module_orders');
}
public function getByOrderId(int $orderId): economic_module_orders
{
global $db;
// Create a new record in the database, if it does not exist
$sql = "SELECT * FROM $this->table WHERE id = $orderId";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $orderId;
$this->getObjectProperties();
} else {
$this->add($orderId);
}
return $this;
}
public function getObjectProperties(): void
{
$this->economic_invoice_draft_id = new object_property($this->table, $this->id, 'invoice_draft_id', 'int', true);
$this->economic_invoice_id = new object_property($this->table, $this->id, 'invoice_id', 'int', true);
}
public function add(int $orderId): void
{
global $db, $response;
try {
// Create a new record in the database
$sql = "INSERT INTO $this->table (id) VALUES ($orderId)";
$db->query($sql);
// Get the id of the new record
$this->id = $orderId;
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function unlinkAllOrdersFromDraft(int $draftId): void
{
global $db;
$sql = "UPDATE $this->table SET invoice_draft_id = NULL WHERE invoice_draft_id = $draftId";
$db->query($sql);
}
public function asArray(): array
{
return [
'id' => $this->id,
'invoice_draft_id' => $this->economic_invoice_draft_id->value(),
'invoice_id' => $this->economic_invoice_id->value(),
];
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class logs_o extends db
{
use db_object_t;
public object_property $module;
public object_property $department;
public object_property $type;
public object_property $user_id;
public object_property $action;
public object_property $message;
public function structure(): void
{
$this->setTable('logs');
}
public function add(string $module, string $department, int $type, int $user_id, string $action, string $message): void
{
// Add to the cache instead of the database, to make it faster
redis->add_log($module, $department, $type, $user_id, $action, $message);
}
public function getObjectProperties(): void
{
$this->module = new object_property($this->table, $this->id, 'module', 'string', true);
$this->department = new object_property($this->table, $this->id, 'department', 'string', false);
$this->type = new object_property($this->table, $this->id, 'type', 'int', true);
$this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', false);
$this->action = new object_property($this->table, $this->id, 'action', 'string', true);
$this->message = new object_property($this->table, $this->id, 'message', 'string', false);
}
public function syncLogsToDatabase($display_progress = false): void
{
function progress($display_progress, $message): void
{
if ($display_progress) {
echo "\n\033[33m$message\033[0m\n";
}
}
function progress_bar($display_progress, int $current, int $total): void
{
// Calculate the percentage
$percentage = ($current / $total) * 100;
// Round the percentage
$percentage = round($percentage);
// Display the progress bar
if ($display_progress) {
echo "\r\033[33m" . str_repeat('=', $percentage) . str_repeat(' ', 100 - $percentage) . "\033[0m " . number_format($percentage, 2) . '%';
}
// Show the percentage, completed and total
if ($display_progress) {
echo "\r\033[33m" . number_format($percentage, 2) . '% (' . $current . '/' . $total . ")\033[0m";
// If the current is equal to the total, add a new line
if ($current === $total) {
echo "\n";
}
}
}
global /** @var db $db */
$db;
// Get all logs from the cache
progress($display_progress, 'Counting logs in cache');
$total = redis->get_log_count();
progress($display_progress, 'Downloading ' . $total . ' logs from cache');
$logs = redis->get_logs();
progress($display_progress, 'Retrieved ' . count($logs) . ' logs from cache');
$current = 0;
// Insert the logs into the database
if ($logs) {
progress($display_progress, 'Preparing to insert logs into the database');
$stmt = $db->prepare("INSERT INTO $this->table (module, department, type, user_id, action, message, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
progress($display_progress, 'Inserting logs into the database');
foreach ( $logs as $log ) {
$current++;
$timestamp = date('Y-m-d H:i:s', $log['timestamp']);
$stmt->bind_param('ssiiisss', $log['module'], $log['department'], $log['type'], $log['user_id'], $log['action'], $log['message'], $timestamp, $timestamp);
$stmt->execute();
// Remove the log from the cache
redis->delete('log_' . $log['id']);
progress_bar($display_progress, $current, $total);
}
$stmt->close();
}
}
}
@@ -0,0 +1,205 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class order_items_o extends db
{
use db_object_t;
/**
* The associated order id
* @var object_property
*/
public object_property $order_id;
/**
* The associated product id
* @var object_property
*/
public object_property $product_id;
/**
* The text reference assigned to the product at the time of the order
* @var object_property
*/
public object_property $reference;
/**
* The notes added to the order item
* @var object_property
*/
public object_property $notes;
/**
* The cashier (id) who added the item to the order
* @var object_property
*/
public object_property $cashier_id;
/**
* The price of the product at the time of the order
* @var object_property
*/
public object_property $price;
/**
* The quantity of the product ordered
* @var object_property
*/
public object_property $quantity;
public function structure(): void
{
$this->setTable('order_items');
}
public function getOrderItemById(int $id): order_items_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function getObjectProperties(): void
{
$this->order_id = new object_property($this->table, $this->id, 'order_id', 'int', true);
$this->product_id = new object_property($this->table, $this->id, 'product_id', 'int', true);
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
$this->price = new object_property($this->table, $this->id, 'price', 'int', true);
$this->quantity = new object_property($this->table, $this->id, 'quantity', 'int', true);
}
public function add(int $order_id, int $product_id, string $reference, string $notes, int $cashier_id, int $price, int $quantity): void
{
global $db, $response;
try {
// Avoid SQL injection
$reference = $db->escape_string($reference);
$notes = $db->escape_string($notes);
// Create a new record in the database
$sql = "INSERT INTO $this->table (order_id, product_id, reference, notes, cashier_id, price, quantity) VALUES ($order_id, $product_id, '$reference', '$notes', $cashier_id, $price, $quantity)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function edit(int $id, int $order_id, int $product_id, string $reference, string $notes, int $cashier_id, int $price, int $quantity): void
{
global $db, $response;
$this->id = $id;
try {
// Avoid SQL injection
$reference = $db->escape_string($reference);
$notes = $db->escape_string($notes);
// Update the record in the database
$sql = "UPDATE $this->table SET order_id = $order_id, product_id = $product_id, reference = '$reference', notes = '$notes', cashier_id = $cashier_id, price = $price, quantity = $quantity WHERE id = $this->id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function addItemToOrder(int $order_id, int $product_id, int $cashier_id, int $quantity): void
{
global $db, $response;
try {
// Get the order
$order = (new orders_o())->getOrderById($order_id);
// Get the product price
$price = (new products_o())->getProductById($product_id)->getDepartmentPrice((int)$order->department_id->value());
// Check if the user has a discount on the product, or category
$customer = (new orders_o())->getOrderCustomer($order_id);
$discount = $customer->getCustomPrice($product_id, false);
if ($discount) {
$price = $price - ($price * $discount / 100);
}
// Create a new record in the database
$sql = "INSERT INTO $this->table (order_id, product_id, price, cashier_id, quantity) VALUES ($order_id, $product_id, $price, $cashier_id, $quantity)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function removeOrderItem(int $id): void
{
// TODO: Implement delete() method instead
global $db;
$this->id = $id;
$sql = "DELETE FROM $this->table WHERE id = $this->id";
$db->query($sql);
}
public function getItemAsArray(): array
{
return [
'id' => (int)$this->id,
'order_id' => (int)$this->order_id->value(),
'product_id' => (int)$this->product_id->value(),
'reference' => (string)$this->reference->value(),
'notes' => (string)$this->notes->value(),
'cashier_id' => (int)$this->cashier_id->value(),
'price' => (int)$this->price->value(),
'quantity' => (int)$this->quantity->value(),
'product' => (array)(new products_o())->getProductById($this->product_id->value())->asArray(),
'cashier' => (array)(new users_o())->getUserById($this->cashier_id->value())->asArray()
];
}
public function getAllItemsAsArray(int $orderId): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE order_id = $orderId AND deleted_at IS NULL";
$result = $db->query($sql);
// Circumvent the repeated instantiation of the object, by just selecting the fields
$items = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$items[] = $row;
}
}
return $items;
}
public function updateOrderItem(int $id, int $price, string $notes, string $reference, int $quantity): void
{
global $db, $response;
$this->id = $id;
try {
// Avoid SQL injection
$price = $db->escape_string($price);
$notes = $db->escape_string($notes);
$reference = $db->escape_string($reference);
// Update the record in the database
$sql = "UPDATE $this->table SET price = $price, notes = '$notes', reference = '$reference', quantity = $quantity WHERE id = $this->id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
}
+292
View File
@@ -0,0 +1,292 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use classes\response;
use traits\db_object_t;
class orders_o extends db
{
use db_object_t;
public object_property $customer_id;
public object_property $cashier_id;
public object_property $reference;
public object_property $notes;
public object_property $department_id;
public object_property $reg_1;
public object_property $reg_2;
public object_property $reg_3;
public economic_module_orders $economic_module_orders;
public object_property $created_at;
public object_property $deleted_at;
public function structure(): void
{
$this->setTable('orders');
}
public function add(int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id, string $reg_1 = '', string $reg_2 = '', string $reg_3 = ''): orders_o
{
global $db, $response;
try {
// Avoid SQL injection
$reference = $db->escape_string($reference);
$notes = $db->escape_string($notes);
$reg_1 = $db->escape_string($reg_1);
$reg_2 = $db->escape_string($reg_2);
$reg_3 = $db->escape_string($reg_3);
// Create a new record in the database
$sql = "INSERT INTO $this->table (customer_id, cashier_id, reference, notes, department_id, reg_1, reg_2, reg_3) VALUES ($customer_id, $cashier_id, '$reference', '$notes', $department_id, '$reg_1', '$reg_2', '$reg_3')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
return $this;
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function getObjectProperties(): void
{
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true);
$this->cashier_id = new object_property($this->table, $this->id, 'cashier_id', 'int', true);
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', true);
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
$this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function edit(int $id, int $customer_id, int $cashier_id, string $reference, string $notes, int $department_id): void
{
global $db, $response;
$this->id = $id;
try {
// Avoid SQL injection
$reference = $db->escape_string($reference);
$notes = $db->escape_string($notes);
// Update the record in the database
$sql = "UPDATE $this->table SET customer_id = $customer_id, cashier_id = $cashier_id, reference = '$reference', notes = '$notes', department_id = $department_id WHERE id = $id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function includeIncludes(): orders_o
{
global /** @var response $response */
$response;
$includeEverything = $response->getRequestParameter('include_all') === 'true';
/** orderItems */
if ($response->getRequestParameter('includeOrderItems') || $includeEverything) {
$response->add_include('orderItems', $this->applyDepartmentPrices($this->getOrderItems($this->id), $this->department_id->value()));
}
/** customer */
if ($response->getRequestParameter('includeCustomer') || $includeEverything) {
$customer = new users_o();
$response->add_include('customer', $customer->getCustomerByIdOrCustomerNumber($this->customer_id->value())->includeIncludes()->asArray());
}
/** cashier */
if ($response->getRequestParameter('includeCashier') || $includeEverything) {
$cashier = new users_o();
$response->add_include('cashier', $cashier->getUserById($this->cashier_id->value())->asArray());
}
/**
* economicModuleOrders
*/
if ($response->getRequestParameter('includeEconomicModuleOrders') || $includeEverything) {
$response->add_include('economicModuleOrders', $this->economic_module_orders->getByOrderId($this->id)->asArray());
}
return $this;
}
/**
* Apply department pricing to a list of products
* @param array $order_items
* @param int $department_id
* @return array
*/
public function applyDepartmentPrices(array $order_items, int $department_id): array
{
global $response;
$department = new departments_o();
$department->getDepartmentById($department_id);
$department->getDepartmentProductPrices($department_id);
foreach ( $order_items as $key => $order_item ) {
$product = new products_o();
$product->getProductById($order_item['product_id']);
$order_items[$key]['product']['price'] = $product->getDepartmentPrice($department_id);
}
return $order_items;
}
public function getOrderItems(int $order_id): array
{
global $db;
$sql = "SELECT * FROM order_items WHERE order_id = $order_id";
$result = $db->query($sql);
$order_items = [];
if ($result->num_rows > 0 && $result) {
while ($row = $result->fetch_assoc()) {
$order_item = new order_items_o();
$order_items[] = $order_item->getOrderItemById($row['id'])->getItemAsArray();
}
}
return $order_items;
}
public function asArray(): array
{
return [
'id' => $this->id,
'customer_id' => $this->customer_id->value(),
'cashier_id' => $this->cashier_id->value(),
'reference' => $this->reference->value(),
'notes' => $this->notes->value(),
'department_id' => $this->department_id->value(),
'reg_1' => $this->reg_1->value(),
'reg_2' => $this->reg_2->value(),
'reg_3' => $this->reg_3->value(),
'created_at' => $this->created_at->value(),
'deleted_at' => $this->deleted_at->value(),
];
}
public function getCustomerByOrderId(?string $order_id): users_o
{
global $db;
$sql = "SELECT customer_id FROM orders WHERE id = $order_id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
return (new users_o())->getCustomerByIdOrCustomerNumber($row['customer_id']);
}
return new users_o();
}
public function getCustomerOrdersPaginated(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_id'] = $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 getDepartmentByOrderId($order_id): array
{
return (new departments_o())->getDepartmentById($this->department_id->value());
}
public function delete(): void
{
// Set the deleted_at property to the current timestamp
$this->deleted_at->set(date('Y-m-d H:i:s'));
// Save the object
}
public function restore(): void
{
// Set the deleted_at property to null
$this->deleted_at->set(null);
// Save the object
}
/**
* @param string $plate The vehicle plate
* @param int $entries The number of last entries to return (default 10)
* @return array The orders for the vehicle plate
*/
public function getOrderHistoryByVehiclePlate(string $plate, int $entries = 10): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' ORDER BY id DESC LIMIT $entries";
$result = $db->query($sql);
return $db->fetch_all($result);
}
public function unlinkOrdersFromInvoiceDraft(int $draftInvoiceNumber): void
{
$this->economic_module_orders->unlinkAllOrdersFromDraft($draftInvoiceNumber);
}
public function getOrderCustomer(int $orderId): users_o
{
$order = new orders_o();
$order->getOrderById($orderId);
return (new users_o())->getCustomerByIdOrCustomerNumber($order->customer_id->value());
}
public function getOrderById(int $id): orders_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
/**
* This function is used when an order is updated
* It takes the request data and updates the order accordingly.
* @return void
* @example {"id":410,"field":"reg_1","value":"REG12"}
*/
public function updateRequest(): void
{
global $response;
// If the permission check is not skipped, require the user to be logged in
$data = json_decode(file_get_contents('php://input'), true);
$this->id = $data['id'];
$this->getObjectProperties();
// Throw an error if the order does not exist
if (!$this->exists()) {
$response->error('Order not found, or already deleted', 400);
}
// Validate the field value
if (!isset($data['field']) || !isset($data['value'])) {
$response->error('Field and value are required', 400);
}
// Check if the value is null
if ($data['value'] === 'null' || $data['value'] === 'NULL') {
$this->{$data['field']}->nullify();
}
$this->{$data['field']}->set($data['value']);
}
public function exists(): bool
{
// Check if the id is greater than 0, and that the deleted_at property is null
if ($this->id > 0) {
$this->getObjectProperties();
return $this->deleted_at->value() === null;
}
return false;
}
}
@@ -0,0 +1,99 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class plate_scanners_o extends db
{
use db_object_t;
public object_property $department_id;
public object_property $name;
public object_property $notes;
public object_property $api_key;
public function structure(): void
{
$this->setTable('plate_scanners');
}
public function getObjectProperties(): void
{
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int');
$this->name = new object_property($this->table, $this->id, 'name', 'string');
$this->notes = new object_property($this->table, $this->id, 'notes', 'string');
$this->api_key = new object_property($this->table, $this->id, 'api_key', 'string');
}
public function getPlateScannerById(int $id): plate_scanners_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function add(int $department_id, string $name, string $notes): void
{
global $db, $response;
try {
// Generate an API key
$api_key = bin2hex(random_bytes(32));
// Avoid SQL injection
$name = $db->escape_string($name);
$notes = $db->escape_string($notes);
// Create a new record in the database
$sql = "INSERT INTO $this->table (department_id, name, notes, api_key) VALUES ($department_id, '$name', '$notes', '$api_key')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function edit(int $id, int $department_id, string $name, string $notes): void
{
global $db, $response;
$this->id = $id;
try {
// Avoid SQL injection
$name = $db->escape_string($name);
$notes = $db->escape_string($notes);
// Update the record in the database
$sql = "UPDATE $this->table SET department_id = $department_id, name = '$name', notes = '$notes' WHERE id = $id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function getPlateScannerByApiKey(mixed $token): plate_scanners_o
{
global $db;
// Avoid SQL injection
$token = $db->escape_string($token);
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE api_key = '$token'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
}
return $this;
}
}
@@ -0,0 +1,84 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class plate_scans_o extends db
{
use db_object_t;
public object_property $plate_scanner_id;
public object_property $plate;
public function structure(): void
{
$this->setTable('plate_scans');
}
public function getObjectProperties(): void
{
$this->plate_scanner_id = new object_property($this->table, $this->id, 'plate_scanner_id', 'int');
$this->plate = new object_property($this->table, $this->id, 'plate', 'string');
}
public function getPlateScanById(int $id): plate_scans_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function add(int $plate_scanner_id, string $plate): void
{
global $db, $response;
try {
// Avoid SQL injection
$plate = $db->escape_string($plate);
// Get the department id from the plate scanner id
$sql = "SELECT department_id FROM plate_scanners WHERE id = $plate_scanner_id";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
$department_id = $row['department_id'];
// Create a new record in the database
$sql = "INSERT INTO $this->table (plate_scanner_id, plate, department_id) VALUES ($plate_scanner_id, '$plate', $department_id)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function getPlateScansByDepartment(int $department_id, int $page, int $limit): array
{
global $db;
// Avoid SQL injection
$department_id = $db->escape_string($department_id);
$page = $db->escape_string($page);
$limit = $db->escape_string($limit);
// Calculate the offset
$offset = ($page - 1) * $limit;
$sql = "SELECT * FROM $this->table WHERE department_id = $department_id ORDER BY id DESC LIMIT $limit OFFSET $offset";
$result = $db->query($sql);
$plate_scans = [];
if ($result->num_rows > 0) {
// Return the records as an array raw data
while ($row = $result->fetch_assoc()) {
$plate_scans[] = $row;
}
}
return $plate_scans;
}
}
+197
View File
@@ -0,0 +1,197 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class products_o extends db
{
use db_object_t;
/**
* The name of the product
* @var object_property
*/
public object_property $name;
/**
* The description of the product
* @var object_property
*/
public object_property $description;
/**
* The price of the product
* @var object_property
*/
public object_property $price;
/**
* The category the product belongs to
* @var object_property
*/
public object_property $category;
/**
* The path to the piktogram image
* @var object_property
*/
public object_property $piktogram;
/**
* The id of the product in the economic system
* @var object_property
*/
public object_property $economic_product_id;
/**
* Whether the category discount should be applied to this product
* @var object_property
*/
public object_property $apply_category_discount;
public function structure(): void
{
$this->setTable('products');
}
public function getProductById(int $id): products_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function getObjectProperties(): void
{
$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->price = new object_property($this->table, $this->id, 'price', 'int', true);
$this->category = new object_property($this->table, $this->id, 'category', 'string', true);
$this->piktogram = new object_property($this->table, $this->id, 'piktogram', 'string', false);
$this->economic_product_id = new object_property($this->table, $this->id, 'economic_product_id', 'int', false);
$this->apply_category_discount = new object_property($this->table, $this->id, 'apply_category_discount', 'bool', false);
}
public function add(string $name, string $description, int $price, string|bool $category = false, string|bool $piktogram = false, string|bool $economicProductId = false): void
{
global $db, $response;
try {
// Avoid SQL injection
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$category = $category ? $db->escape_string($category) : null;
$piktogram = $piktogram ? $db->escape_string($piktogram) : '';
$economicProductId = $economicProductId ? $db->escape_string($economicProductId) : null;
// Create a new record in the database
$sql = "INSERT INTO $this->table (name, description, price, category, piktogram, economic_product_id) VALUES ('$name', '$description', $price, '$category', '$piktogram', '$economicProductId')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
/**
* Edit a product
* @param mixed $id
* @param mixed $name
* @param mixed $description
* @param mixed $price
* @param mixed $category
* @param mixed $piktogram
* @param mixed $economicProductId
* @param bool $applyCategoryDiscount
* @return void
*/
public function edit(mixed $id, mixed $name, mixed $description, mixed $price, mixed $category = false, mixed $piktogram = false, mixed $economicProductId = false, bool $applyCategoryDiscount = true): void
{
global $db, $response;
$this->id = $id;
try {
// Avoid SQL injection
$name = $db->escape_string($name);
$description = $db->escape_string($description);
$category = $category ? $db->escape_string($category) : null;
$piktogram = $piktogram ? $db->escape_string($piktogram) : '';
$economicProductId = $economicProductId ? $db->escape_string($economicProductId) : null;
// Update the record in the database
$sql = "UPDATE $this->table SET name = '$name', description = '$description', price = $price, category = '$category', piktogram = '$piktogram', economic_product_id = '$economicProductId', apply_category_discount = " . (int)$applyCategoryDiscount . " WHERE id = $id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$response->error($e->getMessage());
}
}
public function listObjectsByCategory(string $category): array
{
global $db;
$category = $db->escape_string($category);
$sql = "SELECT * FROM $this->table WHERE category = '$category'";
$result = $db->query($sql);
return $db->fetch_all($result);
}
public function asArray(): array
{
return [
'id' => (int)$this->id,
'name' => (string)$this->name->value(),
'description' => (string)$this->description->value(),
'price' => (int)$this->price->value(),
'category' => $this->category->value(),
'piktogram' => $this->piktogram->value(),
'economic_product_id' => $this->economic_product_id->value(),
'apply_category_discount' => (bool)$this->apply_category_discount->value()
];
}
/**
* Apply department pricing to a list of products
* @param array $products
* @param int $department_id
* @return array
*/
public function applyDepartmentPricing(array $products, int $department_id): array
{
global $db;
$department_id = $db->escape_string($department_id);
$sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id";
$result = $db->query($sql);
$prices = $db->fetch_all($result);
foreach ( $products as $key => $product ) {
foreach ( $prices as $price ) {
if ($product['id'] === $price['product_id']) {
$products[$key]['price'] = $price['price'];
}
}
}
return $products;
}
public function getDepartmentPrice(int $department_id): int
{
global $db;
$department_id = $db->escape_string($department_id);
$sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id AND product_id = $this->id";
$result = $db->query($sql);
$prices = $db->fetch_all($result);
// Check if the product has a department price
if (count($prices) > 0) {
return $prices[0]['price'];
}
// Return the default price
return $this->price->value();
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class ratelimit_o extends db
{
use db_object_t;
public object_property $ip;
public object_property $count;
public object_property $total_count;
public function structure(): void
{
$this->setTable('ratelimit');
}
public function getObjectProperties(): void
{
$this->ip = new object_property($this->table, $this->id, 'ip', 'string', true);
$this->count = new object_property($this->table, $this->id, 'count', 'int', true);
$this->total_count = new object_property($this->table, $this->id, 'total_count', 'int', true);
}
public function getRateLimitByIp(string $ip): ratelimit_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE ip = '$ip'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
}
return $this;
}
public function getRateLimitById(int $id): ratelimit_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function increment(int $id, int $count): void
{
global $db;
$this->id = $id;
// Update the record in the database
$sql = "UPDATE $this->table SET count = count + $count, total_count = total_count + $count WHERE id = $this->id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
}
public function reset(int $id): void
{
global $db;
$this->id = $id;
// Update the record in the database
$sql = "UPDATE $this->table SET count = 0 WHERE id = $this->id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
}
public function create(string $ip): void
{
global $db;
// Avoid SQL injection
$ip = $db->escape_string($ip);
// Create a new record in the database
$sql = "INSERT INTO $this->table (ip, count, total_count) VALUES ('$ip', 1, 1)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
}
public function getOrCreateRateLimitByIp(string $ip): ratelimit_o
{
$ratelimit = $this->getRateLimitByIp($ip);
if (!$ratelimit->id) {
$this->create($ip);
return $this;
}
return $ratelimit;
}
public function resetAll(): void
{
global $db;
// Reset all the ratelimits
$sql = "UPDATE $this->table SET count = 0";
$db->query($sql);
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class tokens_o extends db
{
use db_object_t;
public object_property $user_id;
public object_property $type; // AUTH_TOKEN, RESET_PASSWORD
public object_property $token;
public function structure(): void
{
$this->setTable('tokens');
}
public function create(int $user_id, string $token, string $type = 'AUTH_TOKEN'): void
{
global $db;
// Avoid SQL injection
$token = $db->escape_string($token);
// Create a new record in the database
$sql = "INSERT INTO $this->table (user_id, token, type) VALUES ($user_id, '$token', '$type')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
}
public function getObjectProperties(): void
{
$this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', true);
$this->type = new object_property($this->table, $this->id, 'type', 'string', true);
$this->token = new object_property($this->table, $this->id, 'token', 'string', true);
}
/**
* @throws Exception
*/
public function getToken(string $token): tokens_o
{
global $db;
// Check if the token is cached
$cached = redis->get_token($token);
if ($cached) {
$this->id = $cached['id'];
$this->getObjectProperties();
return $this;
}
// Avoid SQL injection
$token = $db->escape_string($token);
// Prepare the SQL statement
$sql = "SELECT id FROM $this->table WHERE token = '$token'";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
if (!$row) {
throw new Exception("Token not found " . $token);
}
// Cache the token
redis->cache_token($token, $row);
$this->id = $row['id'];
$this->getObjectProperties();
return $this;
}
public function delete(string $token): void
{
global $db;
// Avoid SQL injection
$token = $db->escape_string($token);
// Prepare the SQL statement
$sql = "DELETE FROM $this->table WHERE token = '$token'";
$db->query($sql);
// Clear the token from the cache
redis->clear_token($token);
}
}
@@ -0,0 +1,77 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class user_key_value_pairs_o extends db
{
use db_object_t;
public int $user_id;
public object_property $var;
public object_property $val;
public function structure(): void
{
$this->setTable('user_key_value_pairs');
}
public function getObjectProperties(): void
{
$this->var = new object_property($this->table, $this->id, 'var', 'string', true);
$this->val = new object_property($this->table, $this->id, 'val', 'mixed', true);
}
public function setUser($user_id): user_key_value_pairs_o
{
$this->user_id = $user_id;
return $this;
}
public function setValue($var, $val): user_key_value_pairs_o
{
global $db;
// Avoid SQL injection
$var = $db->escape_string($var);
$val = $db->escape_string($val);
// Check if the value exists in the database
$exists = $this->getValue($var);
// Create a new record in the database if it doesn't exist
if ($exists !== null) {
$sql = "UPDATE $this->table SET val = '$val' WHERE user_id = $this->user_id AND var = '$var'";
} else {
$sql = "INSERT INTO $this->table (user_id, var, val) VALUES ($this->user_id, '$var', '$val')";
}
$db->query($sql);
return $this;
}
public function getValue($var): mixed
{
global $db;
// Avoid SQL injection
$var = $db->escape_string($var);
// Get the record from the database
$sql = "SELECT val FROM $this->table WHERE user_id = $this->user_id AND var = '$var'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
return $db->fetch_assoc($result)['val'];
}
return null;
}
public function deleteValue($var): user_key_value_pairs_o
{
global $db;
// Avoid SQL injection
$var = $db->escape_string($var);
// Create a new record in the database
$sql = "DELETE FROM $this->table WHERE user_id = $this->user_id AND var = '$var'";
$db->query($sql);
return $this;
}
}
@@ -0,0 +1,162 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use traits\db_object_t;
class user_price_overrides_o extends db
{
use db_object_t;
public int $user_id;
public object_property $is_category;
public object_property $product_or_category_id;
public object_property $percentage;
public function structure(): void
{
$this->setTable('price_overrides');
}
public function getObjectProperties(): void
{
$this->is_category = new object_property($this->table, $this->id, 'is_category', 'bool', true);
$this->product_or_category_id = new object_property($this->table, $this->id, 'product_or_category_id', 'int', true);
$this->percentage = new object_property($this->table, $this->id, 'percentage', 'int', true);
}
public function setUser($user_id): user_price_overrides_o
{
$this->user_id = $user_id;
return $this;
}
/**
* Set a price override for the user
* @param bool $is_category
* @param int|string $product_or_category_id
* @param int $percentage
* @return $this
*/
public function setPrice(bool $is_category, int|string $product_or_category_id, int $percentage): user_price_overrides_o
{
global $db;
// If the user is not set, return the object
if (!isset($this->user_id)) {
return $this;
}
// Check if the record already exists
$this->removePriceIfExist($is_category, $product_or_category_id);
// If the percentage is 0, return the object
if ($percentage === 0) {
return $this;
}
// Create a new record in the database
$sql = "INSERT INTO $this->table (user_id, is_category, product_or_category_id, percentage) VALUES ($this->user_id, " . (int)$is_category . ", '$product_or_category_id', $percentage)";
$db->query($sql);
return $this;
}
private function removePriceIfExist(bool $is_category, int|string $product_or_category_id): void
{
global $db;
// Get the price override from the database
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = '$product_or_category_id'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
// Remove the record from the database
$sql = "DELETE FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = '$product_or_category_id'";
$db->query($sql);
}
}
/**
* Get the price override (PERCENTAGE) for the user
* @param bool $is_category
* @param int|string $product_or_category_id
* @return int
*/
public function getPrice(bool $is_category, int|string $product_or_category_id): int
{
global /** @var db $db */
$db;
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = " . (int)$is_category . " AND product_or_category_id = ";
// If the is_category is false, the product_or_category_id is a product_id
if (!$is_category) {
$product_or_category_id = (int)$product_or_category_id;
$sql .= $product_or_category_id;
} else {
// If the is_category is true, the product_or_category_id is a category_id
$product_or_category_id = $db->escape_string($product_or_category_id);
$sql .= "'$product_or_category_id'";
}
// Get the price override from the database
$result = $db->query($sql);
$percentage = 0;
if ($result->num_rows > 0) {
// Return the percentage
$percentage = $result->fetch_assoc()['percentage'];
}
// If the object is a product, check if the category has a discount set
if (!$is_category) {
$product = (new products_o())->getProductById($product_or_category_id);
if ($product->apply_category_discount->value()) {
// Get the economic user discount
$economic_user_global_discount = (new users_o())->getUserById($this->user_id)->getEconomicCustomerDiscountPercentage();
// Get the category discount
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id . " AND is_category = 1 AND product_or_category_id = '" . $product->category->value() . "'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$category_discount = $result->fetch_assoc()['percentage'];
// If the category discount is higher than the product discount, return the category discount
if ((int)$category_discount > (int)$percentage && (int)$category_discount > (int)$economic_user_global_discount) {
return $category_discount;
}
}
// If the economic user discount is higher than the product discount, return the economic user discount
if ((int)$economic_user_global_discount > (int)$percentage) {
return $economic_user_global_discount;
}
}
}
return $percentage;
}
/**
* Get all the price overrides for the user
* @return array
*/
public function getAllPrices(): array
{
global $db;
// Get the customers global discount
$economic_user_global_discount = (new users_o())->getUserById($this->user_id)->getEconomicCustomerDiscountPercentage();
// Get all the price overrides for the user
$sql = "SELECT * FROM $this->table WHERE user_id = " . $this->user_id;
$result = $db->query($sql);
$prices = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$prices[] = $row;
}
}
// If the user has a global discount, add it to the list
if ($economic_user_global_discount > 0) {
$prices[] = [
'id' => "999999",
'user_id' => "" . $this->user_id,
'is_category' => "1",
'product_or_category_id' => "global",
'percentage' => "" . $economic_user_global_discount,
'created_at' => "2021-01-01 00:00:00",
'updated_at' => "2021-01-01 00:00:00"
];
}
return $prices;
}
}
+803
View File
@@ -0,0 +1,803 @@
<?php
namespace objects;
use classes\db;
use classes\language_packs;
use classes\object_property;
use classes\response;
use customers\economic_customer_mo;
use customers\economicCustomers;
use languages\language_pack_en_us;
use traits\db_object_t;
class users_o extends db
{
use db_object_t;
public object_property $customer_number;
public object_property $display_name;
public object_property $group_id;
public economic_customer_mo $economic_customer;
public object_property $created_at;
public object_property $updated_at;
public array $permissions;
public array $attributes;
public array $discounts;
public customer_codes_o $customer_codes;
public user_key_value_pairs_o $keys;
public user_price_overrides_o $price_overrides;
public language_pack_en_us $language_pack;
protected object_property $password;
public function structure(): void
{
$this->setTable('users');
}
public function edit(int $id, string $customer_number, string|null $role, string|null $password, string|null $display_name): void
{
global $db;
$this->id = $id;
// Avoid SQL injection
$customer_number = $db->escape_string($customer_number);
if ($password !== null) {
// Hash the password
$password = password_hash($password, PASSWORD_DEFAULT);
$password = $db->escape_string($password);
}
if ($role !== null) {
$role = $db->escape_string($role);
}
if ($display_name !== null) {
$display_name = $db->escape_string($display_name);
}
// Update the record in the database
$sql = "UPDATE $this->table SET customer_number = '$customer_number'";
if ($password !== null) {
$sql .= ", password = '$password'";
}
if ($role !== null) {
$sql .= ", group_id = '$role'";
}
if ($display_name !== null) {
$sql .= ", display_name = '$display_name'";
}
$sql .= " WHERE id = $this->id";
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
}
public function getObjectProperties(): void
{
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'string', true);
$this->display_name = new object_property($this->table, $this->id, 'display_name', 'string', false);
$this->password = new object_property($this->table, $this->id, 'password', 'string', true);
$this->group_id = new object_property($this->table, $this->id, 'group_id', 'int', true);
$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);
$this->keys = (new user_key_value_pairs_o())->setUser($this->id);
$this->price_overrides = (new user_price_overrides_o())->setUser($this->id);
$this->renderLanguagePack();
}
/**
* Render the language pack of the user
* @return self
*/
private function renderLanguagePack(): self
{
global /** @var response $response */
$response;
// Check if the user has a key called 'language_pack'
if ($this->keys->getValue('language_pack') !== null) {
// Get the language pack of the user
$language_pack = $this->keys->getValue('language_pack');
$this->language_pack = (new language_packs())->getLanguagePack($language_pack);
} else {
// Get the default language pack
$this->language_pack = (new language_packs())->getDefaultLanguagePack();
$response->add_debug(['message' => 'No language pack found for user, using default language pack']);
};
$response->add_meta('language_pack', $this->language_pack);
return $this;
}
public function getLanguagePack(): object
{
// Get the language pack of the user
return new $this->language_pack();
}
public function getCustomerByIdOrCustomerNumber(int $idOrCustomerNumber): users_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $idOrCustomerNumber OR customer_number = '$idOrCustomerNumber'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
} else {
// Import the customer
$this->importCustomerFromExternalSource($idOrCustomerNumber);
}
return $this;
}
private function importCustomerFromExternalSource(int $customer_number): object|bool
{
global $db;
// Get the customer data from the external source
$economic = new economicCustomers();
$customer_data = $economic->getCustomerId($customer_number);
// DEBUG: Return the customer data
// Check if the customer exists
if ($customer_data) {
// Avoid SQL injection
$customer_number = $db->escape_string($customer_data->customerNumber);
// Double check if the customer exists
$sql = "SELECT * FROM $this->table WHERE customer_number = '$customer_number'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
} else {
// Import the customer
$this->add($customer_number, '', 0);
// Nullify the password
$this->password->nullify();
}
}
// Else return false
return false;
}
public function add(string $customer_number, mixed $password, int $role = 0): void
{
global $db;
// Avoid SQL injection
$customer_number = $db->escape_string($customer_number);
$role = $db->escape_string($role);
// Hash the password
$password = password_hash($password, PASSWORD_DEFAULT);
$password = $db->escape_string($password);
// Create a new record in the database
$sql = "INSERT INTO $this->table (customer_number, password, group_id) VALUES ('$customer_number', '$password', $role)";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
}
public function automaticGetTargetUserFromRequest(): users_o
{
// Get the data from the request
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE') {
$data = $_GET;
} else {
$data = json_decode(file_get_contents('php://input'), true);
}
// Check if the user id is set in the request
if (isset($data['user_id'])) {
return $this->getUserById((int)$data['user_id']);
} elseif (isset($data['customer_number'])) {
return $this->getUserByCustomerNumber($data['customer_number']);
} else {
return $this;
}
}
public function getUserById(int $id): users_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $id;
$this->getObjectProperties();
}
return $this;
}
public function getUserByCustomerNumber(int $customer_number): users_o
{
global $db;
// Get the record from the database
$sql = "SELECT * FROM $this->table WHERE customer_number = '$customer_number'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
} else {
// Import the customer
$this->importCustomerFromExternalSource($customer_number);
}
return $this;
}
public function asArray(): array
{
$array = [
'id' => (int)$this->id,
'customer_number' => (int)$this->customer_number->value(),
'customer_name' => $this->getCustomerName($this->customer_number->value()),
'display_name' => $this->display_name->value(),
'group_id' => (int)$this->group_id->value(),
'created_at' => $this->created_at->value(),
'updated_at' => $this->updated_at->value(),
];
// If the economic customer data is set, add it to the array
if (isset($this->economic_customer)) {
$array['economic_customer'] = $this->economic_customer->asArray();
}
// If the permissions are set, add them to the array
if (isset($this->permissions)) {
$array['permissions'] = $this->permissions;
}
// If the attributes are set, add them to the array
if (isset($this->attributes)) {
$array['attributes'] = $this->attributes;
}
// If the discounts are set, add them to the array
if (isset($this->discounts)) {
$array['discounts'] = $this->discounts;
}
return $array;
}
public function getCustomerName(int $customer_number): string|null
{
// Get the customer from the customer object
if ($customer_number === 0) {
return null;
}
// Create a temporary user object
$tmp_user = new users_o();
$tmp_user->getUserByCustomerNumber($customer_number);
// Get the customer name (Check cache first)
$cached = $tmp_user->getCached('economic_customer');
if (!$cached) {
$tmp_user->getCustomerEcocomicData($customer_number);
$cached = $tmp_user->getCached('economic_customer');
}
if ($cached) {
return $cached->name;
}
return null;
}
public function getCustomerEcocomicData(int $customer_number = null): users_o
{
// Get the customer data from the external source
$economic = new economicCustomers();
// Check if the customer number is set
if (!isset($this->customer_number) && $customer_number === null) {
return $this;
}
$customer_number = $customer_number ?? $this->customer_number->value();
$this->economic_customer = (new economic_customer_mo())->getCustomerByCustomerNumber($customer_number);
return $this;
}
public function getNotes(): array
{
global $db;
// Create the customer notes object
$customer_notes = new customer_notes_o();
// Get the customer notes
return $customer_notes->getCustomerNotesAsArray($this->id);
}
public function addNote($customer_id, $note, $cashier_id): void
{
global $db;
// Create the customer notes object
$customer_notes = new customer_notes_o();
// Add the note
$customer_notes->add($customer_id, $note, $cashier_id);
}
public function deleteNote(int $note_id): void
{
global $db;
// Create the customer notes object
$customer_notes = new customer_notes_o();
// Delete the note
$customer_notes->delete($note_id);
}
public function getOrImportCustomerByCustomerNumber(int $customer_number): object|bool
{
global $db;
// Check if the customer exists
$sql = "SELECT * FROM $this->table WHERE customer_number = $customer_number";
$result = $db->query($sql);
if ($result->num_rows > 0) {
$this->id = $result->fetch_assoc()['id'];
$this->getObjectProperties();
} else {
// Import the customer
return $this->importCustomerFromExternalSource($customer_number);
}
return $this;
}
public function addAttribute(string $attribute, int $user_id = null): void
{
global $db;
if ($user_id === null) {
$user_id = $this->id;
}
$attribute = $db->escape_string($attribute);
// To prevent two attributes with the same name for the same user, we will delete the old one if it exists
$this->deleteAttribute($attribute, $user_id);
$sql = "INSERT INTO maintenancemode_dbtest.customer_attributes (user_id, attribute) VALUES ($user_id, '$attribute')";
$db->query($sql);
}
public function deleteAttribute(string $attribute, int $user_id = null): void
{
global $db;
if ($user_id === null) {
$user_id = $this->id;
}
$attribute = $db->escape_string($attribute);
// Make sure the attribute exists
if (!$this->doesUserHaveAttribute((string)$attribute, (int)$user_id)) {
return;
}
// If the attribute exists, delete it, if it does not exist, nothing will happen
$sql = "DELETE FROM maintenancemode_dbtest.customer_attributes WHERE user_id = $user_id AND attribute = '$attribute' LIMIT 1";
$db->query($sql);
}
public function doesUserHaveAttribute(string $attribute, int $user_id = null): bool
{
global $db;
if ($user_id === null) {
$user_id = $this->id;
}
$attribute = $db->escape_string($attribute);
$sql = "SELECT * FROM maintenancemode_dbtest.customer_attributes WHERE user_id = $user_id AND attribute = '$attribute' LIMIT 1";
$result = $db->query($sql);
if ($result->num_rows > 0) {
return true;
}
return false;
}
/**
* If the customer requires a reference number for each order
* @return bool
*/
public function requiresReference(): bool
{
return $this->doesUserHaveAttribute('requiresReferenceNumber');
}
/**
* If the customer should have an invoice per order (Otherwise, they will have a monthly invoice for all orders)
* @return bool
*/
public function invoicePerOrder(): bool
{
return $this->doesUserHaveAttribute('invoiceAllOrdersIndividually');
}
/**
* If the customer is NOT allowed to purchase spot free washes
* @return bool
*/
public function restrictSpotFree(): bool
{
return $this->doesUserHaveAttribute('restrictSpotFree');
}
/**
* If the customer is NOT allowed to purchase interior cleaning
* @return bool
*/
public function restrictInteriorCleaning(): bool
{
return $this->doesUserHaveAttribute('restrictInteriorCleaning');
}
/**
* If the customer is NOT allowed to purchase tank cleaning
* @return bool
*/
public function restrictTankCleaning(): bool
{
return $this->doesUserHaveAttribute('restrictTankCleaning');
}
public function includeIncludes(array $includes = []): users_o
{
global /** @var response $response */
$response;
$includeEverything = $response->getRequestParameter('include_all') === 'true' || in_array('all', $includes);
/**
* economicCustomer
*/
if ($includeEverything || $response->getRequestParameter('includeEconomicCustomer') === 'true' || in_array('economicCustomer', $includes)) {
$this->getCustomerEcocomicData();
}
/**
* permissions
*/
if ($includeEverything || $response->getRequestParameter('includePermissions') === 'true' || in_array('permissions', $includes)) {
$this->getPermissions();
}
/**
* Attributes
*/
if ($includeEverything || $response->getRequestParameter('includeAttributes') === 'true' || in_array('attributes', $includes)) {
$this->getUserAttributes();
}
/**
* Discounts
*/
if ($includeEverything || $response->getRequestParameter('includeDiscounts') === 'true' || in_array('discounts', $includes)) {
$this->getAllDiscounts();
}
return $this;
}
private function getPermissions(): void
{
global $db;
$sql = "SELECT permission FROM groups_permissions WHERE group_id = " . $this->group_id->value();
$result = $db->query($sql);
$perms = [];
while ($row = $result->fetch_assoc()) {
$perms[] = $row['permission'];
}
$this->permissions = $perms;
}
public function getUserAttributes(int $user_id = null): array
{
global $db;
if ($user_id === null) {
$user_id = $this->id;
}
$sql = "SELECT * FROM maintenancemode_dbtest.customer_attributes WHERE user_id = $user_id";
$result = $db->query($sql);
$array = $db->fetch_all($result);
$this->attributes = $array;
return $this->attributes;
}
/**
* Get all discounts for the user (Include)
* @return void
*/
public function getAllDiscounts(): void
{
// Get all discounts (key = 'custom_price')
$this->discounts = $this->price_overrides->setUser($this->id)->getAllPrices();
}
public function getCode(): string|null
{
// Get the customer code
$this->customer_codes = new customer_codes_o();
$this->customer_codes->getCode($this->id);
return $this->customer_codes->code->value();
}
public function setCode(mixed $code): customer_codes_o
{
// Set the customer code
$this->customer_codes = new customer_codes_o();
return $this->customer_codes->setCode($this->id, $code);
}
/**
* Check if the user has access to the order
* @param int $order_id
* @return bool
*/
public function hasAccessToOrder(int $order_id): bool
{
global $db;
// Check if the user has access to all orders
if ($this->hasPermission('fetch_all_orders')) {
return true;
}
// Get the record from the database
$sql = "SELECT * FROM orders WHERE id = $order_id AND customer_id = " . $this->customer_number->value();
$result = $db->query($sql);
if ($result->num_rows > 0) {
return true;
}
return false;
}
public function hasPermission(string $permission): bool
{
global $db;
// Get the user's group id
$group_id = $this->group_id->value();
// If the users is an admin, they have all permissions
if ((int)$group_id === 1) {
return true;
}
// Get the record from the database
$sql = "SELECT * FROM groups_permissions WHERE group_id = $group_id AND permission = '$permission'";
$result = $db->query($sql);
if ($result->num_rows > 0) {
return true;
}
return false;
}
public function setOpenInvoiceDraft(int $draftInvoiceNumber): void
{
// Set the open invoice draft (key = 'open_invoice_draft')
$this->keys->setUser($this->id)->setValue('open_invoice_draft', $draftInvoiceNumber);
}
public function deleteOpenInvoiceDraft(): void
{
// Check if the user has an open invoice draft
if ($this->hasOpenInvoiceDraft()) {
// Remove all links to the draft invoice
$order = new economic_module_orders();
$order->unlinkAllOrdersFromDraft($this->getOpenInvoiceDraft());
// Unset the open invoice draft
$this->unsetOpenInvoiceDraft();
}
}
public function hasOpenInvoiceDraft(): bool
{
// Check if the user has an open invoice draft (key = 'open_invoice_draft')
return $this->keys->setUser($this->id)->getValue('open_invoice_draft') !== null;
}
public function getOpenInvoiceDraft(): int|null
{
// Get the open invoice draft (key = 'open_invoice_draft')
return (int)$this->keys->setUser($this->id)->getValue('open_invoice_draft');
}
public function unsetOpenInvoiceDraft(): void
{
// Unset the open invoice draft (key = 'open_invoice_draft')
$this->keys->setUser($this->id)->deleteValue('open_invoice_draft');
}
/**
* Get the custom price (DISCOUNT) for the user
* @param int $object_id The ID of the object
* @param bool $is_category If the object is a category
* @return int|null The discount percentage
*/
public function getCustomPrice(int $object_id, bool $is_category = false): int|null
{
// Get the custom price for the product
$discount = $this->price_overrides->setUser($this->id)->getPrice($is_category, $object_id);
// If the is_category is false, check if there is a custom price for the category that the product belongs to
if ($discount === 0 && !$is_category) {
$product = new products_o();
// Get the product by ID
$product->getProductById($object_id);
// Check if the product allows category inheritance of discounts
if ($product->apply_category_discount->value()) {
// Get the custom price for the category
$discount = $this->price_overrides->setUser($this->id)->getPrice(true, $product->category->value());
}
}
return $discount;
}
/**
* Get all custom prices (DISCOUNT) for the user
* @return array The custom prices
*/
public function getCustomPrices(): array
{
// Get the custom prices (key = 'custom_price')
return $this->price_overrides->setUser($this->id)->getAllPrices();
}
/**
* Get all users in a group
* @param int $group_id
* @return array
*/
public function getUsersInGroup(int $group_id): array
{
global $db;
$sql = "SELECT id FROM $this->table WHERE group_id = $group_id";
$result = $db->query($sql);
// Create an array of user objects
$users = [];
while ($row = $result->fetch_assoc()) {
$user = new users_o();
$user->id = $row['id'];
$user->getObjectProperties();
$users[] = $user;
}
return $users;
}
/**
* Get the employee data, that's public to the customers
* @return array
*/
public function listPublicEmployeeData(): array
{
return [
'id' => (int)$this->id,
'display_name' => $this->display_name->value(),
];
}
public function getUsersWithPermission(string $permission): array
{
global $db;
$sql = "SELECT id FROM $this->table WHERE group_id IN (SELECT group_id FROM groups_permissions WHERE permission = '$permission')";
$result = $db->query($sql);
// Create an array of user objects
$users = [];
while ($row = $result->fetch_assoc()) {
$user = new users_o();
$user->id = $row['id'];
$user->getObjectProperties();
$users[] = $user;
}
return $users;
}
/**
* Does the user have access to the booking?
* @param int $id
* @return bool
*/
public function hasAccessToBooking(int $id): bool
{
global $db;
// Check if the user has access to all bookings
if ($this->hasPermission('list_bookings')) {
return true;
}
// Get the record from the database
$sql = "SELECT * FROM bookings WHERE id = $id AND customer_number = " . $this->customer_number->value();
$result = $db->query($sql);
if ($result->num_rows > 0) {
return true;
}
return false;
}
public function parseUsers(array $listObjectsWithPaginationIfSet, $variables = []): array
{
$tmp = self::parseCustomerNumbers($listObjectsWithPaginationIfSet);
// Check if the user has a password, and change it to a boolean instead of a string
foreach ( $tmp as $key => $value ) {
if ($value['password'] === '' || $value['password'] === null) {
$tmp[$key]['password'] = false;
} else {
$tmp[$key]['password'] = true;
}
}
return $tmp;
}
public function parseCustomerNumbers(array $listObjectsWithPaginationIfSet): array
{
foreach ( $listObjectsWithPaginationIfSet as $key => $value ) {
$listObjectsWithPaginationIfSet[$key]['customer_name'] = $this->getCustomerNameById($value['id']);
}
return $listObjectsWithPaginationIfSet;
}
private function getCustomerNameById(int $id): ?string
{
// Check if the name is cached
$cached_name = $this->getCached('economic_customer', $id);
if (!$cached_name) {
// No cached name, get the name from the external source
// Get the name from the external source
$tmp_user = new users_o();
$tmp_user->getUserById($id);
$tmp_user->getCustomerEcocomicData();
$cached_name = $tmp_user->getCached('economic_customer');
if (!$cached_name) {
// Cache the NULL value
$this->cache('economic_customer', 'NULL_OR_EMPTY', $id);
return null;
}
}
return $cached_name->name ?? null;
}
public function hasPassword(): bool
{
return $this->password->value() !== null;
}
public function getPassword(): string|null
{
return $this->password->value();
}
public function clearAllUsersEconomicCustomerDiscountsFromCache(): void
{
// Get all the cached results matching the pattern 'users_*_economic_customer_discount_percentage'
$cached_results = redis->get_keys('users_*_economic_customer_discount_percentage');
// Loop through the cached results
foreach ( $cached_results as $key ) {
// Clear the cached discount percentage
redis->delete($key);
}
}
public function clearAllUsersEconomicCustomerDetailsFromCache(): void
{
// Get all the cached results matching the pattern 'users_*_economic_customer'
$cached_results = redis->get_keys('users_*_economic_customer');
// Loop through the cached results
foreach ( $cached_results as $key ) {
// Clear the cached economic customer details
redis->delete($key);
}
}
public function getEconomicCustomerDiscountPercentage(): int
{
// Check if the discount percentage is cached
$cached_discount_percentage = redis->get_economic_customer_discount_percentage($this->id);
if ($cached_discount_percentage !== null) {
return $cached_discount_percentage;
}
// Get the discount percentage from the external source
$economic = new economicCustomers();
$discount_percentage = $economic->getCustomerDiscountPercentage($this->customer_number->value());
// Cache the discount percentage
redis->cache_economic_customer_discount_percentage($this->id, $discount_percentage);
return $discount_percentage;
}
/**
* Set a custom price (DISCOUNT) for the user
* @param int $user_id
* @param int $object_id The ID of the object
* @param int $discount_percentage The discount percentage
* @param bool $is_category If the object is a category
* @return void
*/
public function setCustomPrice(int $user_id, int|string $object_id, int $discount_percentage, bool $is_category = false): void
{
$this->id = $user_id;
// Get the user object properties
$this->getObjectProperties();
// Set the custom price (key = 'custom_price')
$this->price_overrides->setUser($this->id)->setPrice($is_category, $object_id, $discount_percentage);
}
public function syncAllUsersEconomicCustomerDetails(): void
{
// Get all users
$users = $this->getFields(['id', 'customer_number']);
// Loop through the users
foreach ( $users as $user ) {
// Check if the customer number is set (Or if it is 0)
if ($user['customer_number'] === 0) {
continue;
}
// Get the customer data from the external source
$this->getCustomerEcocomicData($user['customer_number']);
}
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
namespace routes;
use classes\authentication;
use objects\logs_o;
use objects\tokens_o;
use traits\route_t;
class authRoute
{
use route_t;
public function run(): void
{
$this->post('/auth/login', function () {
// Get the post data
global $response;
$data = json_decode(file_get_contents('php://input'), true);
// Check if the customer number, and password are set
if (!isset($data['customer_number']) || empty($data['customer_number']) || !is_numeric($data['customer_number']) || $data['customer_number'] < 1) {
$response->error('Customer number is required', 400);
}
if (!isset($data['password']) || empty($data['password']) || strlen($data['password']) < 1) {
$response->error('Password is required', 400);
}
// Try to log the user in
$isCredentialsValid = (new authentication())->authenticate($data['customer_number'], $data['password']);
// Log the incident
if ($isCredentialsValid) {
(new logs_o())->add('auth', 'global', 1, 0, 'AUTH_SUCCESS', 'Customer number: ' . $data['customer_number']);
} else {
(new logs_o())->add('auth', 'global', 1, 0, 'AUTH_FAILURE', 'Customer number: ' . $data['customer_number']);
$response->error('Invalid credentials', 401);
}
// If the credentials are valid, create a token
$token = (new authentication())->create_token($data['customer_number']);
// Return the token
$response->success(['token' => $token]);
});
$this->get('/auth/logout', function () {
// Get the token from the headers
global $response;
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
// Remove the Bearer prefix
$token = str_replace('Bearer ', '', $token);
// Check if the token is valid
if (!(new authentication())->validate_token($token)) {
$response->error('Invalid token', 401);
}
// Delete the token
(new tokens_o())->delete($token);
// Return a success message
$response->success(['message' => 'Logged out']);
});
$this->get('/auth/session', function () {
// Get the token from the headers
global $response;
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; // Default to empty string if not set
// Remove the Bearer prefix
$token = str_replace('Bearer ', '', $token);
// Check if the token is valid
if (!(new authentication())->validate_token($token)) {
$response->error('Invalid token', 401);
}
// Get the user object
$user = (new authentication())->get_user();
// Check if the user exists
if (!$user) {
$response->error('User not found', 400);
}
// Return the (session) user object
$response->success(
($user->includeIncludes(['economicCustomer', 'permissions'])->asArray())
);
});
$this->post('/auth/employee/login', function () {
// Get the post data
global $response;
$data = json_decode(file_get_contents('php://input'), true);
// Check if the employee number, and password are set
if (!isset($data['user_id'])) {
$response->error('Employee number is required', 400);
}
if (!isset($data['password'])) {
$response->error('Password is required', 400);
}
// Try to log the user in
$isCredentialsValid = (new authentication())->authenticateEmployee($data['user_id'], $data['password']);
// Log the incident
if ($isCredentialsValid) {
(new logs_o())->add('auth', 'global', 1, 0, 'AUTH_SUCCESS', 'Employee number: ' . $data['user_id']);
} else {
(new logs_o())->add('auth', 'global', 1, 0, 'AUTH_FAILURE', 'Employee number: ' . $data['user_id']);
$response->error('Invalid credentials', 401);
}
// If the credentials are valid, create a token
$token = (new authentication())->create_employee_token($data['user_id']);
// Return the token
$response->success(['token' => $token]);
});
}
}
+285
View File
@@ -0,0 +1,285 @@
<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\wash_certificate_store;
use objects\bookings_o;
use objects\departments_o;
use objects\logs_o;
use traits\route_t;
class bookingsRoute
{
use route_t;
public function run(): void
{
/** All bookings */
$this->get('/bookings', function () {
// Require the user to be logged in
global
/** @var response $response */
$EMAIL_WASH_CERTIFICATE_TOKEN,
$response;
$this->requirePermission('list_bookings');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_BOOKINGS', 'Successfully listed bookings');
// If the user has the permission to issue wash certificates, add the wash certificate key to the response
if ($user->hasPermission('issue_wash_certificates')) {
$response->add_meta('wash_certificate_token', $EMAIL_WASH_CERTIFICATE_TOKEN);
}
$bookings_o = new bookings_o();
// Return the list of bookings
$response->success(
$bookings_o->parseBookings($bookings_o->listObjectsWithPaginationIfSet())
);
} else {
// Log the incident
(new logs_o())->add('bookings', 'global', 1, 0, 'LIST_BOOKINGS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
/** Own bookings */
$this->get('/user/bookings', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('list_own_bookings');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_OWN_BOOKINGS', 'Successfully listed own bookings');
// Return the list of departments
$bookings_o = new bookings_o();
$response->success(
$bookings_o->parseBookings($bookings_o->getCustomerBookingsPaginated(
$user->customer_number->value(),
($this->fromRequest('page') ?? 1),
($this->fromRequest('limit') ?? 10),
['id' => 'DESC'],
$this->fromRequest('search') === null ? '' : $this->fromRequest('search'),
$this->fromRequest('filters') === null ? [] :
$response->parseFilters($this->fromRequest('filters')) ?? []
))
);
} else {
// Log the incident
(new logs_o())->add('bookings', 'global', 1, 0, 'LIST_OWN_BOOKINGS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
// Synchronize booking from the external system
$this->post('/admin/bookings/sync', function () {
// Require the user to be logged in
global $response;
if ($this->fromRequest('auth_key') !== 'earm8BX4MFTgS6JCNQdqW5EzHUutv2Vx')
$this->requirePermission('sync_bookings');
// Check if the request was successful
$booking = [
'id' => $this->fromRequest('id'),
'customer_number' => $this->fromRequest('customer_number'),
'wash_type' => $this->fromRequest('wash_type'),
'contact_email' => $this->fromRequest('contact_email'),
'reference_number' => $this->fromRequest('reference_number'),
'regNrTraekker' => $this->fromRequest('regNrTraekker'),
'regNrTrailer' => $this->fromRequest('regNrTrailer'),
'washCertificateEmail' => $this->fromRequest('washCertificateEmail'),
'date' => $this->fromRequest('date'),
'department' => $this->fromRequest('department'),
'pickup_bool' => $this->fromRequest('pickup_bool'),
'notes' => $this->fromRequest('notes'),
'washCertificateStatus' => $this->fromRequest('washCertificateStatus'),
'washCertificateUrl' => $this->fromRequest('washCertificateUrl'),
'status' => $this->fromRequest('status'),
];
// Log the incident
(new logs_o())->add('bookings', 'global', 1, 0, 'SYNC_BOOKINGS', 'Successfully synced bookings');
// Add the booking, if it doesn't exist, update it if it does
(new bookings_o())->addOrUpdate(
(int)$booking['id'],
(int)$booking['customer_number'],
(string)$booking['wash_type'],
(string)$booking['contact_email'],
(string)$booking['reference_number'],
(string)$booking['regNrTraekker'],
(string)$booking['regNrTrailer'],
(string)$booking['washCertificateEmail'],
(string)$booking['date'],
(string)$booking['department'],
(string)$booking['pickup_bool'],
(string)$booking['notes'],
(string)$booking['washCertificateStatus'],
(string)$booking['washCertificateUrl'],
(string)$booking['status']
);
$response->success(
['message' => 'Successfully synced booking']
);
});
// Get a departments unfulfilled bookings (count) for the day
$this->get('/admin/bookings/department/count', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('list_department_bookings_count');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the department_id is set
if ($this->fromRequest('department_id') === null) {
$response->error('Department ID is required', 400);
}
// Check if the department id is a valid number
if (!is_numeric($this->fromRequest('department_id'))) {
$response->error('Department ID must be a number', 400);
}
// Check if the result is cached, if so, we don't need to query the database
if (redis->get_department_booking_count((int)$this->fromRequest('department_id'))) {
$response->add_meta('cached', true);
$response->success(
redis->get_department_booking_count((int)$this->fromRequest('department_id'))
);
}
// Check if the department exists
if (!(new departments_o())->selectId((int)$this->fromRequest('department_id'))->exists()) {
$response->error('Department not found', 404);
}
// Log the incident
(new logs_o())->add('bookings', 'global', 1, $user->id, 'LIST_DEPARTMENT_BOOKINGS_COUNT', 'Successfully listed department bookings');
// Return the list of departments
$response->success(
(new bookings_o())->getDepartmentBookingsUnfulfilledCount((int)$this->fromRequest('department_id'))
);
} else {
// Log the incident
(new logs_o())->add('bookings', 'global', 1, 0, 'LIST_DEPARTMENT_BOOKINGS_COUNT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/user/bookings/washcertificate/download', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('download_own_wash_certificate');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if (!$user->exists()) {
$response->error('User not found', 400);
}
// Check if the required fields are set
$id = $response->getRequestParameter('id');
// Make sure the id is a number
if (!is_numeric($id)) {
$response->error('id parameter must be a number got: ' . $id, 400);
}
// Make sure the user is allowed to download the wash certificate
if (!$user->hasAccessToBooking($id)) {
$response->error('You are not allowed to download this wash certificate', 400);
}
// Create the connection
$wash_certificate_store = new wash_certificate_store();
// Check if the wash certificate exists.
if (!$wash_certificate_store->washCertificateExists($id)) {
$response->error('The wash certificate does not exist. id: ' . $id, 404);
}
// Generate the download link
$response->success(
["link" => $wash_certificate_store->getWashCertificateDownload($id)]
);
});
$this->post('/admin/bookings/delete', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('delete_booking');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if (!$user->exists()) {
$response->error('User not found', 400);
}
// Check if the required fields are set
$id = $response->getRequestParameter('id');
// Make sure the id is a number
if (!is_numeric($id)) {
$response->error('id parameter must be a number got: ' . $id, 400);
}
// Make sure the user is allowed to delete the booking
if (!$user->hasAccessToBooking($id)) {
$response->error('You are not allowed to delete this booking', 400);
}
// Delete the booking
(new bookings_o())->delete($id);
// Return success
$response->success(
["message" => "Booking deleted"]
);
});
$this->post('/superuser/bookings/sync/all', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('sync_all_bookings');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if (!$user->exists()) {
$response->error('User not found', 400);
}
// Log the incident
(new logs_o())->add('bookings', 'global', 1, $user->id, 'SYNC_ALL_BOOKINGS', 'Successfully synced all bookings');
// Sync all bookings
(new bookings_o())->syncBookings();
// Return success
$response->success(
["message" => "All bookings synced"]
);
});
$this->post('/admin/bookings/completeWashWithoutWashCertificate', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
$this->requirePermission('complete_wash_without_wash_certificate');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if (!$user->exists()) {
$response->error('User not found', 400);
}
// Check if the required fields are set
$id = $response->getRequestParameter('id');
// Make sure the id is a number
if (!is_numeric($id)) {
$response->error('id parameter must be a number got: ' . $id, 400);
}
// Make sure the user is allowed to complete the wash without a wash certificate
if (!$user->hasAccessToBooking($id)) {
$response->error('You are not allowed to complete this wash without a wash certificate', 400);
}
// Complete the wash without a wash certificate
(new bookings_o())->completeWashWithoutWashCertificate($id);
// Return success
$response->success(
["message" => "Wash completed without wash certificate"]
);
});
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace routes;
use classes\authentication;
use objects\logs_o;
use traits\route_t;
class cronRoute
{
use route_t;
public function run(): void
{
$this->post('/superuser/cron', function () {
// Get the post data
global $response;
// Make sure the user has the SUPERUSER_RUN_CRON permission
if (!$this->requirePermission('SUPERUSER_RUN_CRON')) {
$response->error('Permission denied', 403);
}
// Get the user object
$user = (new authentication())->get_user();
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if a specific cron job is requested
if (isset($data['job'])) {
// Check if the cron job exists
if (!file_exists(WD . '/cron/' . $data['job'] . '.php')) {
$response->error('Cron job not found', 404);
}
// Include the cron job
require_once WD . '/cron/' . $data['job'] . '.php';
// Log the incident
(new logs_o())->add('cron', 'global', 1, $user->id, 'CRON_JOB_RUN', 'Ran cron job: ' . $data['job']);
$response->success('Cron job ran successfully');
}
// If no specific cron job is requested, run all cron jobs (through the cron.php file)
require_once WD . '/cron/Cron.php';
// Log the incident
(new logs_o())->add('cron', 'global', 1, $user->id, 'CRON_RUN', 'Ran all cron jobs');
$response->success([
'message' => 'All cron jobs ran successfully',
'data' => $response_cron ?? []
]);
});
}
}
@@ -0,0 +1,118 @@
<?php
namespace routes;
use classes\authentication;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
class customerAttributes
{
use route_t;
public function run(): void
{
$this->get('/customer/attributes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_customer_attributes');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the query parameters from the URL
$data = $_GET;
// Check if the required fields are set
if (!isset($data['customer_number']) && !isset($data['user_id'])) {
$response->error('User ID or Customer Number is required', 400);
}
// Validate that the number is a number
if (isset($data['customer_number']) && !is_numeric($data['customer_number'])) {
$response->error('Customer Number must be a number', 400);
}
// Check if the user exists
if (!(new users_o())->automaticGetTargetUserFromRequest()->exists()) {
$response->error('Customer not found', 400);
}
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, $user->id, 'LIST_CUSTOMER_ATTRIBUTES', 'Successfully listed customer attributes');
// Return the list of customer notes
$response->success(
(new users_o())->automaticGetTargetUserFromRequest()->getUserAttributes()
);
} else {
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, 0, 'LIST_CUSTOMER_ATTRIBUTES', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/customer/attributes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_customer_attribute');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['user_id']) && !isset($data['customer_number'])) {
$response->error('User ID or Customer Number is required', 400);
}
if (!isset($data['attribute'])) {
$response->error('Attribute is required', 400);
}
// Add the note to the customer
(new users_o())->automaticGetTargetUserFromRequest()->addAttribute((string)$data['attribute']);
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, $user->id, 'ADD_CUSTOMER_ATTRIBUTE', 'Successfully added a customer attribute');
// Return a success message
$response->success(['message' => 'Customer attribute added']);
} else {
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, 0, 'ADD_CUSTOMER_ATTRIBUTE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->delete('/customer/attributes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_customer_attribute');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the query parameters from the URL
$data = $_GET;
// Check if the required fields are set
if (!isset($data['user_id']) && !isset($data['customer_number'])) {
$response->error('User ID or Customer Number is required', 400);
}
if (!isset($data['attribute'])) {
$response->error('Attribute is required', 400);
}
// Check if the user exists
if (!(new users_o())->automaticGetTargetUserFromRequest()->exists()) {
$response->error('Customer not found', 400);
}
// Add the note to the customer
(new users_o())->automaticGetTargetUserFromRequest()->deleteAttribute($data['attribute']);
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, $user->id, 'DELETE_CUSTOMER_ATTRIBUTE', 'Successfully deleted a customer attribute');
// Return a success message
$response->success(['message' => 'Customer attribute deleted']);
} else {
// Log the incident
(new logs_o())->add('customer_attributes', 'global', 1, 0, 'DELETE_CUSTOMER_ATTRIBUTE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}
@@ -0,0 +1,77 @@
<?php
namespace routes;
use classes\authentication;
use objects\customer_notes_o;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
class customerCodeDepartmentRoute
{
use route_t;
public function run(): void
{
$this->get('/admin/customer/code', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('get_customer_code');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the query parameters from the URL
$data = $_GET;
// Check if the required fields are set
if (!isset($data['customer_number']) && !isset($data['user_id'])) { $response->error('User ID or Customer Number is required', 400); }
// Log the incident
(new logs_o())->add('customer_codes', 'global', 1, $user->id, 'GET_CUSTOMER_CODE', 'Successfully retrieved customer code');
// Check if the user exists
if (!(new users_o())->automaticGetTargetUserFromRequest()->exists()) {
$response->error('Customer not found', 400);
}
// Return the customer code
$response->success(
['code' => (new users_o())->automaticGetTargetUserFromRequest()->getCode()]
);
} else {
// Log the incident
(new logs_o())->add('customer_codes', 'global', 1, 0, 'GET_CUSTOMER_CODE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/admin/customer/code', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_customer_code');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['customer_number']) && !isset($data['user_id']) && !isset($data['code'])) { $response->error('User ID, Customer Number, and Code are required', 400); }
// Log the incident
(new logs_o())->add('customer_codes', 'global', 1, $user->id, 'ADD_CUSTOMER_CODE', 'Successfully added customer code');
// Check if the user exists
if (!(new users_o())->automaticGetTargetUserFromRequest()->exists()) {
$response->error('Customer not found', 400);
}
// Add the customer code
(new users_o())->automaticGetTargetUserFromRequest()->setCode($data['code']);
// Return a success message
$response->success(['message' => 'Customer code added']);
} else {
// Log the incident
(new logs_o())->add('customer_codes', 'global', 1, 0, 'ADD_CUSTOMER_CODE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace routes;
use classes\authentication;
use objects\customer_notes_o;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
class customerNotes
{
use route_t;
public function run(): void
{
$this->get('/customer/notes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_customer_notes');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the query parameters from the URL
$data = $_GET;
// Check if the required fields are set
if (!isset($data['customer_number']) && !isset($data['user_id'])) {
$response->error('User ID or Customer Number is required', 400);
}
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, $user->id, 'LIST_CUSTOMER_NOTES', 'Successfully listed customer notes');
// Check if the user exists
$targetUser = (new users_o())->automaticGetTargetUserFromRequest();
if (!$targetUser->exists()) {
$response->error('Customer not found', 400);
}
// Return the list of customer notes
$response->success(
($targetUser->getNotes())
);
} else {
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, 0, 'LIST_CUSTOMER_NOTES', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/customer/notes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_customer_note');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['user_id']) && !isset($data['customer_number'])) {
$response->error('User ID or Customer Number is required', 400);
}
if (!isset($data['note'])) {
$response->error('Note is required', 400);
}
// Add the note to the customer
$customer = (new users_o())->automaticGetTargetUserFromRequest();
// Check if the customer exists
if (!$customer->exists()) {
$response->error('Customer not found', 400);
}
$customer->addNote((int)$customer->id, (string)$data['note'], (int)$user->id);
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, $user->id, 'ADD_CUSTOMER_NOTE', 'Successfully added a customer note');
// Return a success message
$response->success(['message' => 'Customer note added']);
} else {
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, 0, 'ADD_CUSTOMER_NOTE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->delete('/customer/notes', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_customer_note');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the query parameters from the URL
$data = $_GET;
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('Note ID is required', 400);
}
// Delete the note from the customer
(new customer_notes_o())->delete((int)$data['id']);
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, $user->id, 'DELETE_CUSTOMER_NOTE', 'Successfully deleted a customer note');
// Return a success message
$response->success(['message' => 'Customer note deleted']);
} else {
// Log the incident
(new logs_o())->add('customer_notes', 'global', 1, 0, 'DELETE_CUSTOMER_NOTE', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}
@@ -0,0 +1,53 @@
<?php
namespace routes;
use classes\authentication;
use customers\economicCustomers;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
class customerSearchRoute
{
use route_t;
public function run(): void
{
$this->post('/customers/search', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('search_customers');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the pagination, search and sort parameters are set
if (!isset($data['search'])) {
$response->error('Missing required body parameter search', 400);
}
if (!isset($data['filter'])) {
$response->error('Missing required body parameter filter', 400);
}
// Check if the search parameter is valid
$allowedSearchFilters = (new economicCustomers())->allowed_search_filters_customers();
if (!in_array($data['filter'], $allowedSearchFilters)) {
$response->error('Invalid search parameter', 400);
}
(new logs_o())->add('customers', 'global', 1, $user->id, 'SEARCH_CUSTOMERS', 'Successfully retrieved customers meeting search criteria');
// Return the list of users
$response->success(
(array)(new economicCustomers())->searchCustomers((string)$data['search'], (string)$data['filter'])
);
} else {
// Log the incident
(new logs_o())->add('customers', 'global', 1, 0, 'SEARCH_CUSTOMERS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}
@@ -0,0 +1,107 @@
<?php
namespace routes;
use classes\authentication;
use objects\departments_o;
use objects\logs_o;
use traits\route_t;
class departmentsRoute
{
use route_t;
public function run(): void
{
$this->get('/departments', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_departments');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENTS', 'Successfully listed departments');
// Return the list of departments
$response->success(
(new departments_o())->list()
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->post('/departments', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('add_department');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['name'])) {
$response->error('Name is required', 400);
}
if (!isset($data['description'])) {
$response->error('Description is required', 400);
}
// Add the department
(new departments_o())->create($data['name'], $data['description']);
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'ADD_DEPARTMENT', 'Successfully added a department ' . $data['name']);
// Return a success message
$response->success(['message' => 'Department added successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'ADD_DEPARTMENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
$this->put('/departments', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('edit_department');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = json_decode(file_get_contents('php://input'), true);
// Check if the required fields are set
if (!isset($data['id'])) {
$response->error('ID is required', 400);
}
if (!isset($data['name'])) {
$response->error('Name is required', 400);
}
if (!isset($data['description'])) {
$response->error('Description is required', 400);
}
if (!isset($data['economic_department_id'])) {
$response->error('Economic department ID is required', 400);
}
// Update the department
(new departments_o())->edit($data['id'], $data['name'], $data['description'], (int)$data['economic_department_id']);
// Log the incident
(new logs_o())->add('departments', $data['id'], 1, $user->id, 'EDIT_DEPARTMENT', 'Successfully updated a department ' . $data['name']);
// Return a success message
$response->success(['message' => 'Department updated successfully']);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'EDIT_DEPARTMENT', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}
}
@@ -0,0 +1,343 @@
<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\router;
use economic_invoice_draft_mo;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\users_o;
use traits\route_t;
class economicInvoiceRoute
{
use route_t;
public function run(): void
{
global /** @var response $response */
/** @var router $router */
$router, $response;
$this->post('/economic/invoice/draft/export', function () {
global $response;
$this->requirePermission('economic_invoice_draft_export');
$user = (new authentication())->get_user();
if ($user) {
$order_id = $response->getRequestParameter('order_id');
if (!isset($order_id)) {
$response->error('Order ID is required', 400);
}
// Validate the order ID is a number
if (!is_numeric($order_id)) {
$response->error('Order ID must be a number', 400);
}
$order = (new orders_o())->getOrderById($order_id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 404);
}
// Get the order items
$order_items = (new orders_o())->getOrderItems($order_id);
// Apply the department pricing
$order_items = (new orders_o())->applyDepartmentPrices($order_items, $order->department_id->value());
// Get the customer
$customer = (new orders_o())->getCustomerByOrderId($order_id);
// Check if the customer exists
if (!$customer->exists()) {
$response->error('Customer not found', 404);
}
// Get the customer economic number
$customer_economic = $customer->getCustomerEcocomicData()->economic_customer;
$economic_invoice_draft = (new economic_invoice_draft_mo());
// Set the customer number
$economic_invoice_draft->setCustomerNumber((int)$customer_economic->customer_number);
// Set the recipient
$economic_invoice_draft->setRecipient(
$customer_economic->name ?? 'Ukendt',
$customer_economic->address ?? 'Ukendt',
$customer_economic->zip ?? 'Ukendt',
$customer_economic->city ?? 'Ukendt'
);
// Make sure there are order items
if (count($order_items) === 0) {
$response->error('No order items found', 404);
}
// Get the department
$department = (new departments_o())->getDepartmentById($order->department_id->value());
// Add the department, date, reference
$this->addTheDepartmentDateReference($economic_invoice_draft, $department['name'], $order);
// Add the lines to the invoice
foreach ( $order_items as $order_item ) {
// Add the order item to the invoice draft
$this->addOrderItemToInvoice(
$customer,
$order,
$order_item,
$economic_invoice_draft,
$order_item['quantity'] ?? 1);
}
// Check if the user has an open invoice draft
$hasOpenInvoiceDraft = $customer->hasOpenInvoiceDraft();
if ($hasOpenInvoiceDraft) {
// Get the open invoice draft
$openInvoiceDraft = $customer->getOpenInvoiceDraft();
// Add the order to the invoice draft
$result = $this->addOrderToInvoiceDraft($openInvoiceDraft, $order, $customer, $order_items);
}
// If the customer doesn't want to be billed per order, or if there's no open invoice draft, we'll create a new one
// Create the invoice draft
if (!isset($result)) {
$result = $economic_invoice_draft->createInvoiceDraftExample();
}
// Check if the invoice draft was created, or if the lines were added (lines is an array, and should not be empty)
if (!isset($result->draftInvoiceNumber) && !isset($result->lines[0])) {
// Log the error
(new logs_o())->add('economic_invoice_draft', 'global', 3, $user->id, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'Failed to create economic invoice draft');
// Check if we can get the errors from the response
if (isset($result->errors)) {
$response->add_meta('economic_errors', $result->errors);
}
$response->add_meta('economic_result', $result);
// Try to parse the error message
$response->error($result->message ?? 'Failed to create economic invoice draft', 500);
}
// Add the economic invoice draft to the order
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
// Remove the existing invoice draft (if any)
if ($economic_module_orders->economic_invoice_draft_id->value() > 0) {
$economic_invoice_draft->deleteInvoiceDraft($economic_module_orders->economic_invoice_draft_id->value());
}
$economic_module_orders->economic_invoice_draft_id->set($result->draftInvoiceNumber ?? $customer->getOpenInvoiceDraft());
if (!$customer->doesUserHaveAttribute('invoicePerOrder')) {
// If the customer wants to be billed per order, we'll add the order to the invoice draft
$customer->setOpenInvoiceDraft($result->draftInvoiceNumber ?? $customer->getOpenInvoiceDraft());
}
// Return the response
(new logs_o())->add('economic_invoice_draft', 'global', 1, $user->id, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'Successfully exported an economic invoice draft');
$response->success($economic_module_orders->getArray());
} else {
(new logs_o())->add('economic_invoice_draft', 'global', 1, 0, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
$this->delete('/economic/invoice/draft/delete', function () {
global /** @var response $response */
$response;
$this->requirePermission('economic_invoice_draft_delete');
$user = (new authentication())->get_user();
if ($user) {
$order_id = $response->getRequestParameter('order_id');
if (!isset($order_id)) {
$response->error('Order ID is required', 400);
}
$order = (new orders_o())->getOrderById($order_id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 404);
}
// Make sure the order has an economic invoice draft
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
if ($economic_module_orders->economic_invoice_draft_id->value() === 0) {
$response->error('No economic invoice draft found', 404);
}
$economic_invoice_draft = (new economic_invoice_draft_mo());
// Get the invoice draft number
$invoiceDraftId = $economic_module_orders->economic_invoice_draft_id->value();
$customer = (new orders_o())->getCustomerByOrderId($order_id);
if (!$customer->doesUserHaveAttribute('invoicePerOrder')) {
// Check if the customer has an open invoice draft
if ($customer->hasOpenInvoiceDraft()) {
// Check if it's the same as the order's invoice draft
if ((int)$customer->getOpenInvoiceDraft() === (int)$invoiceDraftId) {
// Remove the open invoice draft from the customer
$customer->deleteOpenInvoiceDraft();
}
}
}
// Delete the invoice draft
$economic_invoice_draft->deleteInvoiceDraft($invoiceDraftId);
// Remove the economic invoice draft from the order
$economic_module_orders->economic_invoice_draft_id->set(null);
// Return the response
(new logs_o())->add('economic_invoice_draft', 'global', 1, $user->id, 'ECONOMIC_INVOICE_DRAFT_DELETE', 'Successfully deleted an economic invoice draft');
$response->success($economic_module_orders->getArray());
} else {
(new logs_o())->add('economic_invoice_draft', 'global', 1, 0, 'ECONOMIC_INVOICE_DRAFT_DELETE', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
$this->post('/economic/invoice/export', function () {
global $response;
$this->requirePermission('economic_invoice_export');
$user = (new authentication())->get_user();
if ($user) {
$order_id = $response->getRequestParameter('order_id');
if (!isset($order_id)) {
$response->error('Order ID is required', 400);
}
$order = (new orders_o())->getOrderById($order_id);
// Check if the order exists
if (!$order->exists()) {
$response->error('Order not found', 404);
}
// Get the customer
$customer = (new orders_o())->getCustomerByOrderId($order_id);
// Check if the customer exists
if (!$customer->exists()) {
$response->error('Customer not found', 404);
}
// Make sure the order has an economic invoice draft
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
if ($economic_module_orders->economic_invoice_draft_id->value() === 0) {
$response->error('No economic invoice draft found', 404);
}
$economic_invoice_draft = (new economic_invoice_draft_mo());
// Get the invoice draft number
$invoiceDraftId = $economic_module_orders->economic_invoice_draft_id->value();
// Make sure there's not already an invoice created
$invoiceId = $economic_module_orders->economic_invoice_id->value();
if ($invoiceId > 0) {
$response->error('An invoice has already been created, invoice ID: ' . $invoiceId, 400);
}
// Publish the invoice draft
$result = $economic_invoice_draft->publishInvoiceDraft((int)$invoiceDraftId);
// Check if the invoice was created
if (!isset($result->bookedInvoiceNumber)) {
// Log the error
(new logs_o())->add('economic_invoice', 'global', 3, $user->id, 'ECONOMIC_INVOICE_EXPORT', 'Failed to create economic invoice from draft: ' . $invoiceDraftId);
// Check if we can get the errors from the response
if (isset($result->errors)) {
$response->add_meta('economic_errors', $result->errors);
}
// Try to parse the error message
$response->error($result->message ?? 'Failed to create economic invoice', 500);
}
// Add the economic invoice to the order
$economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
$economic_module_orders->economic_invoice_id->set($result->bookedInvoiceNumber);
// Remove the economic invoice draft from the customer
$customer->unsetOpenInvoiceDraft();
// Return the response
(new logs_o())->add('economic_invoice', 'global', 1, $user->id, 'ECONOMIC_INVOICE_EXPORT', 'Successfully exported an economic invoice');
$response->success($economic_module_orders->getArray());
} else {
(new logs_o())->add('economic_invoice', 'global', 1, 0, 'ECONOMIC_INVOICE_EXPORT', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
}
/**
* @param economic_invoice_draft_mo $economic_invoice_draft
* @param $department_name
* @param orders_o $order
* @return void
*/
function addTheDepartmentDateReference(economic_invoice_draft_mo $economic_invoice_draft, $department_name, orders_o $order): void
{
// The format is:
// Truck Wash - [department name], [date], (?)Ref(erence): [reference], Reg 1: [reg_1], (?)Reg 2: [reg_2], (?)Reg 3: [reg_3]
// (?)[note]
// Example:
// 2021-09-01, Truck Wash - Administration, Ref: 123456, Reg 1: ABC123, Reg 2: DEF456, Reg 3: GHI789
// This is a note for the invoice
//
// (?) = Optional
$line = 'Truck Wash - ' . $department_name . ', ' . $order->created_at->value();
// If there's a reference, add it to the invoice
if ($order->reference->value() !== '')
$line .= ', Ref: ' . $order->reference->value();
// Add the registration numbers (if any)
if ($order->reg_1->value() !== '')
$line .= ', Reg 1: ' . strtoupper($order->reg_1->value());
if ($order->reg_2->value() !== '')
$line .= ', Reg 2: ' . strtoupper($order->reg_2->value());
if ($order->reg_3->value() !== '')
$line .= ', Reg 3: ' . strtoupper($order->reg_3->value());
// Add the line to the invoice
$economic_invoice_draft->addLineTEXT($line);
// If there's a note, add it to the invoice
if ($order->notes->value() !== '')
$economic_invoice_draft->addLineTEXT((string)$order->notes->value());
}
/**
* @param users_o $customer
* @param orders_o $order
* @param mixed $order_item
* @param economic_invoice_draft_mo $economic_invoice_draft
* @param int $quantity
* @return void
*/
function addOrderItemToInvoice(users_o $customer, orders_o $order, mixed $order_item, economic_invoice_draft_mo $economic_invoice_draft, int $quantity = 1): void
{
// The format is:
// [product name], (?)Ref(erence): [reference], (!?)Reg 1: [reg 1], (!?)Reg 2: [reg 2], (!?)Reg 3: [reg 3], (?)Note: [note], (?)Discount: ([order item price] - [product price]) DKK ([discount percentage] %)
// (?) = Optional
// (!) = Required if the customer requires it
// (!?) = Should be added if the customer requires it
// Example:
// Tankcleaning 4 spulehoveder, Ref: 123456, Reg 1: ABC123, Reg 2: DEF456, Reg 3: GHI789, Note: This is a note, Discount: -100 DKK (20%)
$line = $order_item['product']['name'];
// If there's a reference, add it to the line
if ($order_item['reference'] !== '')
$line .= ', Ref: ' . $order_item['reference'];
// Add the registration numbers (if they exist, and the customer requires it)
if ($customer->doesUserHaveAttribute('requiresRegistrationNumbersInvoice')) {
$line .= ', Reg 1: ' . strtoupper($order->reg_1->value());
$line .= ', Reg 2: ' . strtoupper($order->reg_2->value());
$line .= ', Reg 3: ' . strtoupper($order->reg_3->value());
}
// If there's a note, add it to the line
if ($order_item['notes'] !== '')
$line .= ', Note: ' . $order_item['notes'];
// Calculate the discount percentage
$discountPercentage = round((($order_item['product']['price'] - $order_item['price']) / $order_item['product']['price']) * 100, 2);
// If the price is different from the product price, add it to the line
if ($order_item['price'] !== $order_item['product']['price'])
$line .= ', Discount: ' . ($order_item['price'] - $order_item['product']['price']) . ' DKK (~' . $discountPercentage . '%)';
// Get the department
$department = $order->getDepartmentByOrderId($order->id);
$economic_department_id = $department['economic_department_id'];
$economic_dimension_id = $department['economic_dimension_id'];
// Add the line to the invoice
$economic_invoice_draft->addLine(
(string)$order_item['product']['economic_product_id'],
(string)$line,
(int)$quantity,
(int)$order_item['price'],
0, // Since we can't be specific about the discount, we set it to 0. (The API has a limit of 2 decimals, and that's not enough for our needs)
(int)$economic_department_id ?? 0,
(int)$economic_dimension_id ?? 0 // If the department is not set, we'll set it to 0
);
}
function addOrderToInvoiceDraft(int $economic_invoice_draft_id, orders_o $order, users_o $customer, array $order_items): object
{
$economic_invoice_draft = (new economic_invoice_draft_mo());
// Add the department, date, reference
$this->addTheDepartmentDateReference($economic_invoice_draft, $order->getDepartmentByOrderId($order->id)['name'], $order);
// Add the lines to the invoice
foreach ( $order_items as $order_item ) {
// Add the order item to the invoice draft
$this->addOrderItemToInvoice($customer, $order, $order_item, $economic_invoice_draft, $order_item['quantity'] ?? 1);
}
return $economic_invoice_draft->addLinesToInvoiceDraft($economic_invoice_draft_id);
}
}
@@ -0,0 +1,18 @@
<?php
namespace routes;
use traits\route_t;
class exampleRoute
{
use route_t;
public function run(): void
{
$this->get('/example', function () {
global $response;
$response->success(['message' => 'Hello World!']);
});
}
}

Some files were not shown because too many files have changed in this diff Show More