Merge pull request #9 from copenhagentruckwash/development

Add improvements for bookings and notifications
This commit is contained in:
Jeppe B
2025-01-21 22:30:00 +01:00
committed by GitHub
30 changed files with 1516 additions and 183 deletions
+5
View File
@@ -108,4 +108,9 @@ class db
{
return $this->conn->prepare($sql);
}
public function num_rows(\mysqli_result|bool $result): int|string
{
return $result->num_rows;
}
}
+206
View File
@@ -56,6 +56,10 @@ class redis implements redis_i
*/
public function cache_economic_customer_name(int $customer_number, string $customer_name): self
{
// If the customer name is empty, or null, set it to 'IS_EMPTY_OR_NULL'
if (empty($customer_name)) {
$customer_name = 'IS_EMPTY_OR_NULL';
}
// Cache the economic customer name
$this->set('economic_customer_name_' . $customer_number, $customer_name);
return $this;
@@ -108,4 +112,206 @@ class redis implements redis_i
$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';
}
$this->set('department_name_' . $department_id, $department_name);
return $this;
}
/**
* @inheritDoc
*/
public function get_department_name(int $department_id): string|null
{
// Get the department name
return $this->get('department_name_' . $department_id);
}
/**
* @inheritDoc
*/
public function clear_department_name(int $department_id): self
{
// Clear the department name
$this->delete('department_name_' . $department_id);
return $this;
}
/**
* @inheritDoc
*/
public function cache_economic_customer_discount_percentage(int $customer_number, int $discount_percentage): self
{
// Cache the economic customer discount percentage
$this->set('economic_customer_discount_percentage_' . $customer_number, $discount_percentage);
return $this;
}
/**
* @inheritDoc
*/
public function get_economic_customer_discount_percentage(int $customer_number): int|null
{
// Get the economic customer discount percentage
return $this->get('economic_customer_discount_percentage_' . $customer_number);
}
/**
* @inheritDoc
*/
public function clear_economic_customer_discount_percentage(int $customer_number): self
{
// Clear the economic customer discount percentage
$this->delete('economic_customer_discount_percentage_' . $customer_number);
return $this;
}
/**
* @inheritDoc
*/
public function cache_customer_notes(int $customer_id, array $customer_notes): self
{
// Cache the customer notes
$this->set_array('customer_notes_' . $customer_id, $customer_notes);
return $this;
}
/**
* @inheritDoc
*/
public function get_customer_notes(int $customer_id): array|null
{
// Get the customer notes
return $this->get_array('customer_notes_' . $customer_id);
}
/**
* @inheritDoc
*/
public function clear_customer_notes(int $customer_id): self
{
// Clear the customer notes
$this->delete('customer_notes_' . $customer_id);
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;
}
}
+29 -15
View File
@@ -19,6 +19,7 @@ class response implements response_i
#[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);
@@ -29,6 +30,14 @@ class response implements response_i
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,
@@ -38,6 +47,21 @@ class response implements response_i
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);
@@ -89,11 +113,6 @@ class response implements response_i
$this->meta[$key] = $value;
}
public function get_data(): array
{
return $this->data;
}
public function is_matching_route_found(): bool
{
return $this->matching_route_found;
@@ -123,16 +142,6 @@ class response implements response_i
return $data[$key] ?? null;
}
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 parseFilters(?string $filters): array|null
{
if ($filters) {
@@ -149,4 +158,9 @@ class response implements response_i
}
return null;
}
public function add_debug_list(string $list_name, int $key, $value): void
{
$this->add_data($list_name, [$key => $value]);
}
}
+54 -3
View File
@@ -5,6 +5,7 @@ namespace classes;
use GuzzleHttp\Client;
use interfaces\notification_i;
use objects\departments_o;
use objects\users_o;
use traits\notification_t;
@@ -30,7 +31,7 @@ class slack implements notification_i
return $this;
}
private function get_department_webhook(int $department_id): string
private function get_department_webhook(int $department_id): string|null
{
// Check if the department webhook is cached
$webhook = redis->get_department_webhook($department_id);
@@ -40,8 +41,11 @@ class slack implements notification_i
$department->id = $department_id;
$department->getObjectProperties();
$webhook = $department->slack_webhook->value();
// Cache the department webhook
redis->cache_department_webhook($department_id, $webhook);
// 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);
}
@@ -78,4 +82,51 @@ class slack implements notification_i
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";
}
}
+151
View File
@@ -0,0 +1,151 @@
<?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;
}
$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" => (boolean)$booking["pickup_bool"] ? 'true' : 'false',
"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;
}
}
+14 -4
View File
@@ -4,10 +4,6 @@ if (php_sapi_name() !== 'cli') {
exit;
}
$args = $argv;
echo "This is the CLI script";
echo "\n";
echo "Arguments: ";
print_r($args);
// If the first argument is 'run', switch to the second argument
if ($args[1] === 'run') {
@@ -20,6 +16,10 @@ if ($args[1] === 'run') {
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';
@@ -28,6 +28,16 @@ if ($args[1] === 'run') {
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 'SyncLogs':
require_once 'cron/SyncLogs.php';
break;
default:
echo "Invalid script name";
break;
+2 -1
View File
@@ -1,4 +1,4 @@
<?php global $CONFIG_DB, $DEBUG, $ENCRYPTION_KEY, $CORS, $ECONOMIC_API, $WORDPRESS_STATIC_TOKEN, $USE_PROD_ECONOMIC_IN_DEBUG, $REDIS_CONFIG, $EMAIL_WASH_CERTIFICATE_TOKEN, $MINIO;
<?php global $CONFIG_DB, $DEBUG, $ENCRYPTION_KEY, $CORS, $ECONOMIC_API, $WORDPRESS_STATIC_TOKEN, $USE_PROD_ECONOMIC_IN_DEBUG, $REDIS_CONFIG, $EMAIL_WASH_CERTIFICATE_TOKEN, $MINIO, $WORDPRESS_API_URL;
$CONFIG_DB = [
'host' => '', // IP address of the database server e.g. 127.0.0.1
'user' => '', // Username of the database server e.g. root
@@ -23,6 +23,7 @@ if ($DEBUG && !$USE_PROD_ECONOMIC_IN_DEBUG) {
}
$WORDPRESS_STATIC_TOKEN = ''; // Static token used to authenticate the WordPress plugin
$EMAIL_WASH_CERTIFICATE_TOKEN = ''; // Token used to authenticate the wash certificate generator
$WORDPRESS_API_URL = ''; // URL to the WordPress API ajax endpoint e.g. https://example.com/wp-admin/admin-ajax.php
$MINIO = [
'endpoint' => '', // Minio endpoint e.g. http://0.0.0.0:9000
'access_key' => '', // Minio access
-3
View File
@@ -31,8 +31,5 @@ $htaccess = "
// 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);
// Get the current time
$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 executed in ' . (time() - $now) . ' seconds ( ' . (microtime(true) - $now) . 'ms )' . PHP_EOL, FILE_APPEND);
+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;
}
// Sync the bookings
$bookings_o = new bookings_o();
$bookings_o->syncBookings();
// Check if any bookings from yesterday haven't been fulfilled
$bookings_o->checkUnfulfilledBookings();
+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();
+3 -1
View File
@@ -1,4 +1,5 @@
<?php global $DEBUG, $CONFIG_DB, $ENCRYPTION_KEY, $CORS, $WD, $db, $response, $request, $router, $redis;
<?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.
*/
@@ -39,6 +40,7 @@ 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';
/**
* Modules
+132
View File
@@ -69,4 +69,136 @@ interface redis_i
* @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 $customer_number
* @param int $discount_percentage
* @return self
*/
public function cache_economic_customer_discount_percentage(int $customer_number, int $discount_percentage): self;
/**
* Get an economic customers discount percentage
* @param int $customer_number
* @return int|null
*/
public function get_economic_customer_discount_percentage(int $customer_number): int|null;
/**
* Clear an economic customers discount percentage
* @param int $customer_number
* @return self
*/
public function clear_economic_customer_discount_percentage(int $customer_number): self;
/**
* Cache a customer notes
* @param int $customer_id
* @param array $customer_notes
* @return self
*/
public function cache_customer_notes(int $customer_id, array $customer_notes): self;
/**
* Get a customer notes
* @param int $customer_id
* @return array|null
*/
public function get_customer_notes(int $customer_id): array|null;
/**
* Clear a customer notes
* @param int $customer_id
* @return self
*/
public function clear_customer_notes(int $customer_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;
}
@@ -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;
}
@@ -24,7 +24,7 @@ class economicCustomers extends economic_m
}
// Get the customer name from the economic system
$customer = $this->getCustomerId($customer_number);
if (count($customer) > 0) {
if ($customer) {
$customer = $customer[0];
redis->cache_economic_customer_name($customer_number, $customer->name);
return $customer->name;
@@ -32,12 +32,47 @@ class economicCustomers extends economic_m
return null;
}
public function getCustomerId(int $customerNumber): array|bool
public function getCustomerId(int $customerNumber): object|bool
{
// Check if the customer exists
$url = '/customers?filter=customerNumber$eq:' . $customerNumber;
$url = '/customers/' . $customerNumber;
$response = $this->send_request($url, 'GET', '');
$response = json_decode($response);
return $response->collection;
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;
}
}
@@ -2,6 +2,8 @@
namespace customers;
use classes\response;
class economic_customer_mo
{
public int $customer_number;
@@ -22,8 +24,8 @@ class economic_customer_mo
$economicCustomers = new economicCustomers();
$customer = $economicCustomers->getCustomerId($customer_number);
// Check if the customer exists
if (count($customer) > 0) {
return $this->parseCustomer($customer[0]);
if ($customer) {
return $this->parseCustomer($customer);
}
return $this;
@@ -31,6 +33,8 @@ class economic_customer_mo
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);
@@ -43,6 +47,9 @@ class economic_customer_mo
$this->country = ($customer->country ?? null);
// Cache the customer name
redis->cache_economic_customer_name($this->customer_number, $this->name);
$economicCustomers = new economicCustomers();
// Add the customer to the debug log
$response->add_debug_list('economic_customer', $this->customer_number, $customer);
return $this;
}
+200 -65
View File
@@ -5,6 +5,9 @@ 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
@@ -31,71 +34,6 @@ class bookings_o extends db
$this->setTable('bookings');
}
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 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 addOrUpdate($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);
// 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 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 */
@@ -171,4 +109,201 @@ class bookings_o extends db
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'] === 'true' ? 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'";
$result = $db->query($sql);
return $db->fetch_all($result);
}
public function addOrUpdate($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);
}
}
+39 -9
View File
@@ -9,6 +9,7 @@ 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;
@@ -19,14 +20,6 @@ class customer_notes_o extends db
$this->setTable('customer_notes');
}
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 add(int $customer_id, string $note, int $cashier_id): customer_notes_o
{
global $db, $response;
@@ -42,15 +35,32 @@ class customer_notes_o extends db
// 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 $customer_id): array
{
global $db;
global $response, $db;
// Check if the result is cached
$cached = redis->get_customer_notes($customer_id);
if ($cached) {
$response->add_meta('cached', true);
return $cached;
}
$sql = "SELECT * FROM $this->table WHERE customer_id = $customer_id AND deleted_at IS NULL";
$result = $db->query($sql);
$customer_notes = [];
@@ -59,6 +69,8 @@ class customer_notes_o extends db
$customer_notes[] = $row;
}
}
// Cache the result
redis->cache_customer_notes($customer_id, $customer_notes);
return $customer_notes;
}
@@ -81,6 +93,21 @@ class customer_notes_o extends 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
@@ -89,5 +116,8 @@ class customer_notes_o extends 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);
}
}
+57 -10
View File
@@ -35,6 +35,9 @@ class departments_o extends db
// Set the values of the object properties
$this->getObjectProperties();
// Clear the cache
redis->clear_departments();
}
public function getObjectProperties(): void
@@ -62,34 +65,55 @@ class departments_o extends db
// Set the values of the object properties
$this->getObjectProperties();
// Clear the cache
redis->clear_department($id);
}
public function list($superUser = false): array
{
global $db;
$sql = "SELECT * FROM $this->table";
$result = $db->query($sql);
$result = $db->fetch_all($result);
// 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) {
$result = array_map(function ($department) {
$departments = array_map(function ($department) {
return [
'name' => $department['name'],
'description' => $department['description'],
'id' => $department['id']
];
}, $result);
return $result;
}, $departments);
}
return $result;
return $departments;
}
public function getDepartmentById(int $id): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
return $db->fetch_assoc($result);
// 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;
}
/**
@@ -147,4 +171,27 @@ class departments_o extends db
$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;
}
}
+23 -16
View File
@@ -9,6 +9,7 @@ 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;
@@ -21,6 +22,12 @@ class logs_o extends db
$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);
@@ -31,22 +38,22 @@ class logs_o extends db
$this->message = new object_property($this->table, $this->id, 'message', 'string', false);
}
public function add(string $module, string $department, int $type, int $user_id, string $action, string $message): void
public function syncLogsToDatabase(): void
{
global $db;
// Avoid SQL injection
$module = $db->escape_string($module);
$department = $db->escape_string($department);
$action = $db->escape_string($action);
$message = $db->escape_string($message);
// Create a new record in the database
$sql = "INSERT INTO $this->table (module, department, type, user_id, action, message) VALUES ('$module', '$department', $type, $user_id, '$action', '$message')";
$db->query($sql);
// Get the id of the new record
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
global /** @var db $db */
$db;
// Get all logs from the cache
$logs = redis->get_logs();
if ($logs) {
$stmt = $db->prepare("INSERT INTO $this->table (module, department, type, user_id, action, message, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
foreach ( $logs as $log ) {
$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']);
}
$stmt->close();
}
}
}
-1
View File
@@ -50,7 +50,6 @@ class orders_o extends db
return $this;
} catch (\Exception $e) {
$response->error($e->getMessage());
return $this;
}
}
+22 -1
View File
@@ -106,15 +106,22 @@ class user_price_overrides_o extends db
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) {
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;
@@ -127,6 +134,8 @@ class user_price_overrides_o extends db
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);
@@ -136,6 +145,18 @@ class user_price_overrides_o extends db
$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;
}
}
+41 -12
View File
@@ -103,9 +103,9 @@ class users_o extends db
$customer_data = $economic->getCustomerId($customer_number);
// DEBUG: Return the customer data
// Check if the customer exists
if (isset($customer_data[0])) {
if ($customer_data) {
// Avoid SQL injection
$customer_number = $db->escape_string($customer_data[0]->customerNumber);
$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);
@@ -195,6 +195,7 @@ class users_o extends db
$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(),
@@ -219,6 +220,20 @@ class users_o extends db
return $array;
}
public function getCustomerName(int $customer_number): string|null
{
// Check if the customer name is cached
if (redis->get_economic_customer_name($customer_number) !== null) {
return redis->get_economic_customer_name($customer_number);
}
// Get the customer name from the external source
$economic = new economicCustomers();
$customer_name = $economic->getCustomerName($customer_number);
// Cache the customer name
redis->cache_economic_customer_name($customer_number, $customer_name ?? '');
return $customer_name;
}
public function getNotes(): array
{
global $db;
@@ -634,7 +649,7 @@ class users_o extends db
return true;
}
// Get the record from the database
$sql = "SELECT * FROM bookings WHERE id = $id AND user_id = " . $this->id;
$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;
@@ -642,17 +657,31 @@ class users_o extends db
return false;
}
public function getCustomerName(int $customer_number): string|null
public function parseUsers(array $listObjectsWithPaginationIfSet): array
{
// Check if the customer name is cached
if (redis->get_economic_customer_name($customer_number)) {
return redis->get_economic_customer_name($customer_number);
return self::parseCustomerNumbers($listObjectsWithPaginationIfSet);
}
public function parseCustomerNumbers(array $listObjectsWithPaginationIfSet): array
{
foreach ( $listObjectsWithPaginationIfSet as $key => $value ) {
$listObjectsWithPaginationIfSet[$key]['customer_name'] = $this->getCustomerName($value['customer_number']);
}
// Get the customer name from the external source
return $listObjectsWithPaginationIfSet;
}
public function getEconomicCustomerDiscountPercentage(): int
{
// Check if the discount percentage is cached
$cached_discount_percentage = redis->get_economic_customer_discount_percentage($this->customer_number->value());
if ($cached_discount_percentage !== null) {
return $cached_discount_percentage;
}
// Get the discount percentage from the external source
$economic = new economicCustomers();
$customer_name = $economic->getCustomerName($customer_number);
// Cache the customer name
redis->cache_economic_customer_name($customer_number, $customer_name);
return $customer_name;
$discount_percentage = $economic->getCustomerDiscountPercentage($this->customer_number->value());
// Cache the discount percentage
redis->cache_economic_customer_discount_percentage($this->customer_number->value(), $discount_percentage);
return $discount_percentage;
}
}
+24 -2
View File
@@ -59,8 +59,9 @@ class bookingsRoute
// 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(
(new bookings_o())->getCustomerBookingsPaginated(
$bookings_o->parseBookings($bookings_o->getCustomerBookingsPaginated(
$user->customer_number->value(),
($this->fromRequest('page') ?? 1),
($this->fromRequest('limit') ?? 10),
@@ -68,7 +69,7 @@ class bookingsRoute
$this->fromRequest('search') === null ? '' : $this->fromRequest('search'),
$this->fromRequest('filters') === null ? [] :
$response->parseFilters($this->fromRequest('filters')) ?? []
)
))
);
} else {
// Log the incident
@@ -230,5 +231,26 @@ class bookingsRoute
["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"]
);
});
}
}
+15 -6
View File
@@ -25,16 +25,19 @@ class customerNotes
// 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); }
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
if (!(new users_o())->automaticGetTargetUserFromRequest()->exists()) {
$targetUser = (new users_o())->automaticGetTargetUserFromRequest();
if (!$targetUser->exists()) {
$response->error('Customer not found', 400);
}
// Return the list of customer notes
$response->success(
(new users_o())->automaticGetTargetUserFromRequest()->getNotes()
($targetUser->getNotes())
);
} else {
// Log the incident
@@ -55,8 +58,12 @@ class customerNotes
// 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); }
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
@@ -87,7 +94,9 @@ class customerNotes
// 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); }
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
+7 -5
View File
@@ -55,11 +55,13 @@ class ordersRoute
$response->error('Department not found', 400);
}
// Make sure the customer number set is valid
if (!(new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id'])->exists() || empty($data['customer_id'])) {
$response->error('Customer not found or invalid', 400);
$targetUser = (new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id']);
if (!$targetUser->exists()) {
$response->error('Customer not found', 400);
}
// Check if the user requires a reference
if ((new users_o())->getCustomerByIdOrCustomerNumber((int)$data['customer_id'])->requiresReference() && empty($data['reference'])) {
if ($targetUser->requiresReference() && empty($data['reference'])) {
$response->error('Reference is required by the customer', 400);
}
// Get the registration number
@@ -122,7 +124,7 @@ class ordersRoute
}
// If the department ID is set, validate it
// Log the incident
(new logs_o())->add('orders', $data['id'], 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
// Return a success message
$response->success(['message' => 'Order updated successfully']);
} else {
@@ -156,7 +158,7 @@ class ordersRoute
// Delete the order
$order->delete();
// Log the incident
(new logs_o())->add('orders', $id, 1, $user->id, 'DELETE_ORDER', 'Successfully deleted an order (ID: ' . $id . ')');
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_ORDER', 'Successfully deleted an order (ID: ' . $id . ')');
// Return a success message
$response->success(['message' => 'Order deleted successfully']);
} else {
+5 -2
View File
@@ -3,6 +3,7 @@
namespace routes;
use classes\authentication;
use classes\response;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
@@ -15,7 +16,8 @@ class usersRoute
{
$this->get('/users', function () {
// Require the user to be logged in
global $response;
global /** @var response $response */
$response;
$this->requirePermission('list_users');
// Get the user object
$user = (new authentication())->get_user();
@@ -24,8 +26,9 @@ class usersRoute
// Log the incident
(new logs_o())->add('users', 'global', 1, $user->id, 'LIST_USERS', 'Successfully listed users');
// Return the list of users
$users_o = new users_o();
$response->success(
(new users_o())->listObjects()
$users_o->parseUsers($users_o->listObjectsWithPaginationIfSet())
);
} else {
// Log the incident
+142
View File
@@ -0,0 +1,142 @@
<?php
// prevent direct access
use classes\wordpress_bookings_remote;
if (!defined('WD')) {
exit;
}
function warn($message): void
{
echo "\n\033[33m$message\033[0m\n";
}
// Test if we can fetch a booking.
$booking_id = 155;
$wordpress_bookings_remote = new wordpress_bookings_remote();
// Get the last wash ID from the remote API.
$last_wash_id = $wordpress_bookings_remote->get_last_wash_id();
if (empty($last_wash_id)) {
warn("Failed to fetch the last wash ID");
return;
}
echo "\nLast wash ID fetched successfully from the remote API\n";
echo "\nLast wash ID: $last_wash_id\n";
// Get all wash IDs from the remote API.
$wash_ids = $wordpress_bookings_remote->get_all_wash_ids()['data']['all_wash_ids'];
if (empty($wash_ids)) {
warn("Failed to fetch wash IDs");
return;
}
echo "\nWash IDs fetched successfully from the remote API\n";
echo "\nWash IDs: (count: " . count($wash_ids) . ")\n";
// Parse all bookings from the remote API.
$bookings = $wordpress_bookings_remote->parse_bookings($wordpress_bookings_remote->get_all_bookings());
if (empty($bookings)) {
warn("Failed to fetch bookings");
return;
}
// Get all bookings from the remote API.
$bookings = $wordpress_bookings_remote->get_all_bookings();
if (empty($bookings)) {
warn("Failed to fetch bookings");
return;
}
echo "\nBookings fetched successfully from the remote API\n";
echo "\nBookings: (count: " . count($bookings['data']['all_wash_bookings']) . ")\n";
$booking = $wordpress_bookings_remote->parse_booking($booking_id);
$bookings_o = new \objects\bookings_o();
if (empty($booking)) {
warn("Failed to fetch booking with ID $booking_id");
} else {
// Test if the booking has the expected keys.
$expected_keys = [
'id',
'customer_number',
'wash_type',
'contact_email',
'reference_number',
'regNrTraekker',
'regNrTrailer',
'washCertificateEmail',
'date',
'department',
'pickup_bool',
'notes',
'washCertificateStatus',
'washCertificateUrl',
'status',
];
foreach ( $expected_keys as $key ) {
if (!array_key_exists($key, $booking)) {
warn("Booking with ID $booking_id is missing key $key");
return;
}
}
echo "\nBooking with ID $booking_id fetched successfully\n";
// Check if the booking wash certificate exists.
$wash_certificate_store = new \classes\wash_certificate_store();
if (!$wash_certificate_store->washCertificateExists($booking['id'])) {
warn("Booking with ID $booking_id does not have a wash certificate");
} else {
echo "\nBooking with ID $booking_id has a wash certificate\n";
$booking['washCertificateUrl'] = 'Protected URL';
$booking['washCertificateStatus'] = 'completed';
// Print the booking data.
echo "\nBooking data:\n";
print_r($booking);
// Save the booking data to database.
$bookings_o->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'] === 'true' ? 1 : 0,
$booking['notes'],
$booking['washCertificateStatus'],
$booking['washCertificateUrl'],
$booking['status']
);
echo "\nBooking data saved to database\n";
}
}
echo "\nSyncing bookings to the remote API... This may take a while\n";
// Sync all bookings to the remote API.
$start = microtime(true);
$sync = $bookings_o->syncBookings();
$end = microtime(true);
echo "\nBookings synced to the remote API\n";
echo "\nTime taken: " . ($end - $start) . " seconds\n";
echo "\nTime per booking: " . (($end - $start) / count($bookings['data']['all_wash_bookings'])) . " seconds\n";
echo "\nSync: \n";
print_r($sync);
+26
View File
@@ -0,0 +1,26 @@
<?php
// prevent direct access
if (!defined('WD')) {
exit;
}
global $REDIS_CONFIG;
function warn($message): void
{
echo "\n\033[33m$message\033[0m\n";
}
// Check if the MINIO array is set
if (!isset($REDIS_CONFIG)) {
throw new Exception('REDIS array is not set in config.php');
}
// Check if the MINIO array has the required keys
if (!isset($REDIS_CONFIG['host']) || !isset($REDIS_CONFIG['database']) || !isset($REDIS_CONFIG['password'])) {
throw new Exception('REDIS array is missing required keys');
}
$timestart = microtime(true);
// Create a new Redis object
$logs_o = new objects\logs_o();
$logs_o->syncLogsToDatabase();
$timeend = microtime(true);
echo "\nLogs synced to database in " . ($timeend - $timestart) . 's';
+107 -21
View File
@@ -56,27 +56,6 @@ trait redis_t
return $this->redis->exists($key);
}
/**
* Set the value in the Redis server
* @param string $key
* @param string $value
* @return redis|redis_t
*/
public function set(string $key, string $value): self
{
$this->redis->set($key, $value);
return $this;
}
/**
* Get all keys from the Redis server
* @return array
*/
public function keys(): array
{
return $this->redis->keys('*');
}
/**
* Get the Redis client
* @return PredisClient
@@ -246,4 +225,111 @@ trait redis_t
{
$this->redis->disconnect();
}
/**
* Set array key in Redis
* @param string $key
* @param array $value
* @return redis|redis_t
*/
public function set_array(string $key, array $value): self
{
$this->set($key, json_encode($value));
return $this;
}
/**
* Set the value in the Redis server
* @param string $key
* @param string $value
* @return redis|redis_t
*/
public function set(string $key, string $value): self
{
$this->redis->set($key, $value);
return $this;
}
/**
* Clear keys from Redis
* @param string|array $keys
* @return redis|redis_t
*/
public function clear_keys(array|string $keys): self
{
if (!is_array($keys)) {
// Remove all keys matching the pattern
$keys = $this->get_keys($keys);
}
foreach ( $keys as $key ) {
$this->delete($key);
}
return $this;
}
/**
* Get keys from Redis
* @param string $key The key to search for (supports wildcards)
* @return array
*/
public function get_keys(string $key): array
{
// Get all keys from the cache
return $this->redis->keys($key);
}
/**
* Get all keys from the Redis server
* @return array
*/
public function keys(): array
{
return $this->redis->keys('*');
}
/**
* Get arrays from Redis
* @param string $key The key to search for (supports wildcards)
* @return array|null
*/
public function get_arrays(string $key): array|null
{
$keys = $this->get_keys($key);
$arrays = [];
foreach ( $keys as $key ) {
$arrays[] = $this->get_array($key);
}
return $arrays;
}
/**
* Get array key from Redis
* @param string $key
* @return array|null
*/
public function get_array(string $key): array|null
{
// Get the array from the cache
$value = $this->get($key);
// Check if the value is not null
if ($value === null) {
return null;
}
// Check if the value is not a valid JSON
if (!self::is_json($value)) {
return null;
}
return json_decode($value, true);
}
/**
* Check if the value is a valid JSON
* @param string $value
* @return bool
*/
private function is_json(string $value): bool
{
json_decode($value);
return json_last_error() === JSON_ERROR_NONE;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace traits;
trait wordpress_api_object_t
{
protected string $API_URL; // The URL of the WordPress API
protected string $API_KEY; // The key of the WordPress API
public function __construct()
{
$this->wordpress_api_object_t_construct();
}
public function wordpress_api_object_t_construct(): void
{
global $EMAIL_WASH_CERTIFICATE_TOKEN, $WORDPRESS_API_URL;
// Make sure the WordPress API configuration is set
if (!isset($EMAIL_WASH_CERTIFICATE_TOKEN) || !isset($WORDPRESS_API_URL)) {
throw new \Exception('WordPress API configuration is not set');
}
// Apply the WordPress API configuration
$this->API_KEY = $EMAIL_WASH_CERTIFICATE_TOKEN;
$this->API_URL = $WORDPRESS_API_URL;
}
public function debug(): void
{
echo 'API URL: ' . $this->API_URL . '<br>';
echo 'API KEY: ' . $this->API_KEY . '<br>';
}
public function request($invoke = 'example', $data = []): array
{
// Add the auth_key to the data
$data['auth_key'] = $this->API_KEY;
$data['invoke'] = $invoke;
$data['action'] = 'twc_api_endpoints';
// Send a post request to the API
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->API_URL);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);
// Check if the response is an error
if ($response === false) {
return ['error' => 'Curl error: ' . curl_error($ch)];
}
// If the response is not JSON, return it as is
if ($response[0] !== '{') {
return ['response' => $response];
}
return json_decode($response, true);
}
}