Merge pull request #14 from copenhagentruckwash/development

Refactor and enhance order invoices and data handling
This commit is contained in:
Jeppe B
2025-03-13 14:39:04 +01:00
committed by GitHub
114 changed files with 9770 additions and 346 deletions
@@ -97,12 +97,15 @@ class authentication implements authentication_i
{
// Get the token from the headers
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
$tmp = json_decode(file_get_contents('php://input'), true);
if (!isset($headers['Authorization']) && !isset($_GET['token']) && !isset($_POST['token']) && !isset($tmp['token'])) {
return false;
}
$token = $headers['Authorization'];
// Strip the Bearer prefix
$token = str_replace('Bearer ', '', $token);
$token = $_GET['token'] ?? $headers['Authorization'] ?? $tmp['token'] ?? $_POST['token'];
// Strip the Bearer prefix (If the token is from the headers)
if (isset($headers['Authorization'])) {
$token = str_replace('Bearer ', '', $token);
}
// Get the token from the database
$token = (new plate_scanners_o())->getPlateScannerByApiKey($token);
// Check if the token exists
+30
View File
@@ -8,15 +8,19 @@ require_once WD . '/modules/economic/endpoints/economic_departments_endpoint.php
require_once WD . '/modules/economic/endpoints/economic_layouts_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_customers_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_products_endpoint.php';
require_once WD . '/modules/economic/economic_helpers.php';
use economic_c;
use economic_helpers;
use endpoints\economic_customers_endpoint;
use endpoints\economic_departments_endpoint;
use endpoints\economic_invoices_endpoint;
use endpoints\economic_layouts_endpoint;
use endpoints\economic_orders_endpoint;
use endpoints\economic_products_endpoint;
use helpers\economic_customer;
use helpers\economic_invoice_draft;
use interfaces\economic_i;
class economic implements economic_i
@@ -56,6 +60,11 @@ class economic implements economic_i
* @var economic_products_endpoint
*/
public economic_products_endpoint $products;
/**
* Helper classes
* @var economic_helpers
*/
protected economic_helpers $helpers;
public function __construct()
@@ -67,5 +76,26 @@ class economic implements economic_i
$this->layouts = new economic_layouts_endpoint();
$this->customers = new economic_customers_endpoint();
$this->products = new economic_products_endpoint();
$this->helpers = new economic_helpers();
}
/**
* Get a customer by their customer number
* @param int $customer_number The Economic customer number
* @return economic_customer
*/
public function getCustomer(int $customer_number): economic_customer
{
return new $this->helpers->economic_customer($customer_number);
}
/**
* Get a invoiceDraft (Helper) from the Economic system
* @param int $draft_invoice_number The Economic draft invoice number
* @return economic_invoice_draft
*/
public function getInvoiceDraft(int $draft_invoice_number): economic_invoice_draft
{
return new $this->helpers->economic_invoice_draft($draft_invoice_number);
}
}
+133 -22
View File
@@ -2,9 +2,27 @@
namespace classes;
require_once WD . '/modules/email/email_c.php';
require_once WD . '/modules/email/helpers/email_template.php';
require_once WD . '/modules/email/templates/email_template_header.php';
require_once WD . '/modules/email/templates/email_template_footer.php';
require_once WD . '/modules/email/templates/email_template_head.php';
require_once WD . '/modules/email/templates/email_template_stripe_invoice.php';
use email\email_c;
use email\templates\email_template_footer;
use email\templates\email_template_head;
use email\templates\email_template_header;
use email\templates\email_template_stripe_invoice;
use Exception;
use interfaces\email_i;
use JsonException;
use MailerSend\Exceptions\MailerSendAssertException;
use MailerSend\Exceptions\MailerSendException;
use MailerSend\Helpers\Builder\EmailParams;
use MailerSend\Helpers\Builder\Header;
use MailerSend\Helpers\Builder\Recipient;
use MailerSend\MailerSend;
use Psr\Http\Client\ClientExceptionInterface;
class email implements email_i
{
@@ -24,9 +42,9 @@ class email implements email_i
{
if ($test_recipient) {
try {
$this->sendEmail($test_recipient, 'Test email', 'This is a test email');
$this->sendEmail($test_recipient, 'Test recipient', 'Test email', 'This is a test email');
return 'Email sent successfully to ' . $test_recipient;
} catch (\Exception $e) {
} catch (Exception $e) {
return 'Email error: ' . $e->getMessage();
}
} else {
@@ -34,28 +52,121 @@ class email implements email_i
}
}
private function sendEmail($to, $subject, $message): void
/**
* Send an email using the preferred email service
* @param string $to Email address to send the email to
* @param string $recipient_name Name of the recipient
* @param string $subject Subject of the email
* @param string $message Message of the email
* @throws Exception If an error occurs while sending the email
*/
public function sendEmail(string $to, string $recipient_name, string $subject, string $message, string $references = null): void
{
// Send POST request to email service
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://maintenancemode.cloud/mailer.php');
curl_setopt($ch, CURLOPT_POST, 1);
// Add the data to the request
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'recipient' => $to,
'subject' => $subject,
'message' => $message,
]);
// Return the response instead of outputting it
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the POST request
$response = curl_exec($ch);
// Close cURL resource
curl_close($ch);
// Check for errors
if ($response === false) {
throw new \Exception('Curl error: ' . curl_error($ch));
if ($this->config->mailersend_enabled->getVariableValue()) {
try {
// Generate HTML email
$html = self::generateHtmlHeader();
// Add email content
$html .= $message;
// Add email footer
$html .= self::generateHtmlFooter();
$this->sendEmailMailerSend($to, $recipient_name, $subject, $message, $html, $references);
} catch (JsonException|MailerSendException|ClientExceptionInterface $e) {
throw new Exception('Error sending email: ' . $e->getMessage());
}
} else {
$this->sendEmailDefault($to, $subject, $message);
}
}
private static function generateHtmlHeader(): false|string
{
$email_template_head = (new email_template_head())->generate_html();
$email_template_header = (new email_template_header())->generate_html();
return "
<!DOCTYPE html>
<html lang='da'>
<head>
$email_template_head
</head>
<body>
$email_template_header";
}
private static function generateHtmlFooter(): string
{
$email_template_footer = (new email_template_footer())->generate_html();
return "
$email_template_footer
</body>
</html>";
}
/**
* @throws ClientExceptionInterface
* @throws JsonException
* @throws MailerSendAssertException
* @throws MailerSendException
*/
private function sendEmailMailerSend(string $to, string $recipient_name, string $subject, string $message, string $html = null, string $references = null): void
{
// Send POST request to email service
$mailersend = new MailerSend(['api_key' => $this->config->mailersend_api_key->getVariableValue()]);
$recipients = [
new Recipient($to, $recipient_name)
];
$emailParams = (new EmailParams())
->setFrom($this->config->smtp_from->getVariableValue())
->setFromName($this->config->smtp_from_name->getVariableValue())
->setRecipients($recipients)
->setSubject($subject)
->setHtml($html ?? $message)
->setReplyTo($this->config->smtp_reply_to->getVariableValue())
->setReplyToName($this->config->smtp_reply_to_name->getVariableValue());
if ($references) {
$emailParams->setHeaders([
new Header('References', $references)
]);
}
$mailersend->email->send($emailParams);
}
/**
* Send an email using the default SMTP service
* @param string $to Email address to send the email to
* @param string $subject Subject of the email
* @param string $message Message of the email
* @throws Exception If an error occurs while sending the email
*/
private function sendEmailDefault(string $to, string $subject, string $message): void
{
// Send email using default SMTP service
throw new Exception('Default SMTP service not implemented');
}
/**
* @throws MailerSendException
* @throws ClientExceptionInterface
* @throws JsonException
* @throws MailerSendAssertException
*/
public function sendStripeInvoiceEmail(string $to, string|null $recipient_name, string $payment_link, $order_id): void
{
$recipient_name = $recipient_name ?? 'Kunde';
$recipient_name = ucfirst($recipient_name);
// Generate HTML email
$html = self::generateHtmlHeader();
// Add email content
$html .= (new email_template_stripe_invoice(
$order_id,
$payment_link,
$recipient_name
))->generate_html();
// Add email footer
$html .= self::generateHtmlFooter();
$this->sendEmailMailerSend($to, $recipient_name, 'Betalingslink for bestilling #' . $order_id, '', $html);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace classes;
/** Require the form_t trait */
require_once WD . '/traits/form_t.php';
/** Require the form_helper_c class */
require_once WD . '/modules/forms/form_helper_c.php';
use Exception;
use forms\book_wash_f;
use forms\form_helper_c;
use traits\form_t;
/** Require the forms */
require_once WD . '/modules/forms/book_wash_f.php';
class form
{
/**
* The BOOK_WASH form
* @var book_wash_f $book_wash The BOOK_WASH form
*/
public book_wash_f $book_wash;
public function __construct()
{
$this->book_wash = new book_wash_f();
}
/**
* Submit a form
* @throws Exception If the form is not valid
* @throws Exception If the form is not submitted
* @throws Exception If the form is not found
* @throws Exception If the form is not found in the form helper
*/
public function submitForm(object $form): void
{
/** @var form_t|object $form */
$form->submit();
}
/**
* Check if a form exists (by its identifier)
* @param string $form_identifier The form identifier
* @return bool True if the form exists, false if not
*/
public function doesFormExist(string $form_identifier): bool
{
// Check if there's any form with the given identifier
$form_classes = get_declared_classes();
foreach ( $form_classes as $form_class ) {
if (is_subclass_of($form_class, form_helper_c::class)) {
$form = new $form_class();
// Check if the class is a subclass of form_helper_c, and has the form_t trait
if (method_exists($form, 'getFormIdentifier') && $form->getFormIdentifier() === $form_identifier) {
return true;
}
}
}
return false;
}
/**
* Get a form by its identifier
* @param string $form_identifier The form identifier
* @return form_t The form object
* @throws Exception If the form is not found
*/
public function getForm(string $form_identifier): object
{
// Check if there's any form with the given identifier
$form_classes = get_declared_classes();
foreach ( $form_classes as $form_class ) {
// Check if the class is a subclass of form_helper_c, and has the form_t trait
if (is_subclass_of($form_class, form_helper_c::class) && trait_exists(form_t::class)) {
// Create an instance of the form class (If it is a subclass of form_helper_c, and use the form_t trait)
$form = new $form_class();
if (method_exists($form, 'getFormIdentifier') && $form->getFormIdentifier() === $form_identifier) {
return $form;
}
}
}
throw new Exception('The form was not found');
}
}
+179 -34
View File
@@ -3,9 +3,14 @@
namespace classes;
require_once WD . '/modules/motorapi/motorapi_c.php';
require_once WD . '/modules/motorapi/helpers/motorapi_vehicle_types.php';
/** Actions */
require_once WD . '/modules/motorapi/actions/license_plate_lookup_a.php';
use Exception;
use interfaces\motorapi_i;
use motorapi\actions\license_plate_lookup_a;
use motorapi\helpers\motorapi_vehicle_types;
use motorapi\motorapi_c;
use objects\motorapi_lookups_o;
@@ -16,6 +21,11 @@ class motorapi implements motorapi_i
* @var motorapi_c
*/
public motorapi_c $config;
/**
* Vehicle types
* @var motorapi_vehicle_types
*/
public motorapi_vehicle_types $vehicle_types;
/**
* API URL
@@ -23,47 +33,49 @@ class motorapi implements motorapi_i
*/
private string $api_url = 'https://v1.motorapi.dk/';
/**
* ACTION: LICENSE_PLATE_LOOKUP
* @see license_plate_lookup_a
* @notation This action is when a license plate lookup request is made, and logs the request in the database
* @var license_plate_lookup_a
*/
private license_plate_lookup_a $license_plate_lookup;
public function __construct()
{
$this->config = new motorapi_c();
$this->vehicle_types = new motorapi_vehicle_types();
/** Actions */
$this->license_plate_lookup = new license_plate_lookup_a();
}
/**
* @inheritDoc
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
* Get the recommended products for a license plate
* @param string $plate The license plate
* @return array The recommended products
* @throws Exception If the module is not enabled
* @throws Exception If the license plate is invalid
* @throws Exception If the daily limit is exceeded
* @throws Exception If the secret key is invalid
*/
function getLicensePlateInformation(string $licensePlate): object
public function getRecommendedProducts(string $plate): array
{
// Get the license plate information from the motorapi
return $this->sendRequest($licensePlate, 'vehicles', [], 'GET');
}
/**
* @inheritDoc
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
*/
function sendRequest(string $licensePlate, string $endpoint, array $data = [], string $method = 'GET'): object
{
// Validate the module is enabled
self::requireModuleEnabled();
// Validate the license plate
self::requireValidLicensePlate($licensePlate);
// Validate the daily limit
self::requireDailyLimitNotExceeded();
// Validate the secret key
self::requireValidSecretKey();
// Send the request
$response = match ($method) {
'GET' => self::sendGetRequest($licensePlate, $endpoint, $data),
'POST' => self::sendPostRequest($licensePlate, $endpoint, $data),
'PUT' => self::sendPutRequest($licensePlate, $endpoint, $data),
'DELETE' => self::sendDeleteRequest($licensePlate, $endpoint, $data),
default => throw new Exception('Invalid method'),
self::requireValidLicensePlate($plate);
$data = self::getLicensePlateInformation($plate, false);
// Get the recommended products for the license plate
return match ($data->type) {
// TODO: Add recommended products for each vehicle type, and convert this into an object type.
$this->vehicle_types->PLATE_TYPE_CAR => [
1,
2,
],
$this->vehicle_types->PLATE_TYPE_TRUCK => [
3,
4,
],
default => [],
};
// Add the request to the log
self::addRequestToLog($licensePlate, $endpoint, $response);
// Return the response
return $response;
}
/**
@@ -88,6 +100,84 @@ class motorapi implements motorapi_i
}
}
/**
* @inheritDoc
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
*/
function getLicensePlateInformation(string $licensePlate, bool $ignoreCache = true): object
{
// Check if the license plate information is cached
if (!$ignoreCache && self::isCached($licensePlate)) {
// Get the cached result
return self::getCachedResult($licensePlate);
}
// Get the license plate information from the motorapi
return $this->sendRequest($licensePlate, 'vehicles', [], 'GET');
}
/**
* @inheritDoc
*/
function isCached(string $licensePlate): bool
{
// Check if the license plate response is stored in the log/local database/cache
$motorapi_lookups = new motorapi_lookups_o();
return $motorapi_lookups->isCached($licensePlate);
}
/**
* @inheritDoc
*/
function getCachedResult(string $licensePlate): object
{
global /** @var response $response */
$response;
// Get the cached result from the log/local database/cache
$motorapi_lookups = new motorapi_lookups_o();
// Add the cached value to the meta
$response->add_meta('cached', true);
return json_decode($motorapi_lookups->getCachedResult($licensePlate)->result->value());
}
/**
* @inheritDoc
* @throws Exception If the module is not enabled, the license plate is invalid, the daily limit is exceeded, or the secret key is invalid
*/
function sendRequest(string $licensePlate, string $endpoint, array $data = [], string $method = 'GET'): object
{
// Validate the module is enabled
self::requireModuleEnabled();
// Validate the license plate
self::requireValidLicensePlate($licensePlate);
// Validate the daily limit
self::requireDailyLimitNotExceeded();
// Validate the secret key
self::requireValidSecretKey();
// Send the request
$response = match ($method) {
'GET' => self::sendGetRequest($licensePlate, $endpoint, $data),
'POST' => self::sendPostRequest($licensePlate, $endpoint, $data),
'PUT' => self::sendPutRequest($licensePlate, $endpoint, $data),
'DELETE' => self::sendDeleteRequest($licensePlate, $endpoint, $data),
default => self::exception(
$licensePlate, [
'method' => $method,
'endpoint' => $endpoint,
'data' => $data,
'license_plate' => $licensePlate,
'response' => null,
'status_code' => 400,
'error' => 'Invalid request method',
],
500
),
};
// Add the request to the log
self::addRequestToLog($licensePlate, $endpoint, $response);
// Return the response
return $response;
}
/**
* @inheritDoc
*/
@@ -117,10 +207,27 @@ class motorapi implements motorapi_i
{
// Check if the secret key is valid
if ($this->config->secret_key->getVariableValue() === null) {
throw new Exception('Invalid secret key');
self::exception(
'',
[
'status_code' => 500,
'error' => 'Invalid secret key defined in the config (motorapi_secret_key_c)',
],
500
);
}
}
/**
* @throws Exception
*/
function exception(string $licensePlate, array $data = [], int $status_code = 500): exception
{
// Add the request to the log
$this->license_plate_lookup->license_plate_lookup($licensePlate, $data, $status_code);
return throw new Exception($data['error'] ?? 'An error occurred while processing the request, in ' . $this->config->getModuleName() . ' module');
}
/**
* @inheritDoc
* @throws Exception
@@ -148,11 +255,46 @@ class motorapi implements motorapi_i
// Check for errors
if (curl_errno($ch)) {
throw new Exception('cURL error: ' . curl_error($ch));
self::exception(
$licensePlate, [
'method' => 'GET',
'endpoint' => $endpoint,
'data' => $data,
'license_plate' => $licensePlate,
'response' => null,
'status_code' => 500,
'error' => curl_error($ch),
],
500
);
}
// Get the status code
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Decode and return JSON response
// Check if the response is valid JSON
if (!json_decode($output)) {
$exception_message = match ($status_code) {
401 => 'Unauthorized',
404 => 'Not found',
429 => 'Too many requests',
default => 'Invalid response',
};
self::exception(
$licensePlate, [
'method' => 'GET',
'endpoint' => $endpoint,
'data' => $data,
'license_plate' => $licensePlate,
'response' => null,
'status_code' => $status_code,
'error' => $exception_message,
],
$status_code
);
}
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), $status_code);
return json_decode($output);
}
@@ -169,6 +311,7 @@ class motorapi implements motorapi_i
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
return json_decode($output);
}
@@ -185,6 +328,7 @@ class motorapi implements motorapi_i
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
return json_decode($output);
}
@@ -201,6 +345,7 @@ class motorapi implements motorapi_i
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
$this->license_plate_lookup->license_plate_lookup($licensePlate, json_decode($output), 200);
return json_decode($output);
}
@@ -20,7 +20,7 @@ class object_property
$this->required = $required;
$this->default = $default;
}
public function __toString(): string
{
// Return the value of the field in the database table
@@ -54,7 +54,10 @@ class object_property
// 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
} // If the value is an integer, set it to the integer value
elseif (is_int($value)) {
$sql = "UPDATE $this->table SET $this->column = $value 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";
+18
View File
@@ -2,8 +2,10 @@
namespace classes;
use Exception;
use interfaces\response_i;
use JetBrains\PhpStorm\NoReturn;
use objects\users_o;
class response implements response_i
{
@@ -11,6 +13,7 @@ class response implements response_i
private array $data = [];
private array $meta = [];
private array $includes = [];
private users_o $users_o;
#[NoReturn] public function success(mixed $data, int $status = null): void
{
@@ -206,4 +209,19 @@ class response implements response_i
error_reporting(0);
ini_set('display_errors', '0');
}
/**
* This function gets the user object from the token
* @throws Exception If the token is invalid, or the user is not found
*/
public function get_user(): users_o|false
{
// Check if the user is already set
if (!isset($this->users_o)) {
// Get the user object
$this->users_o = (new authentication())->get_user();
}
// Return the user object
return $this->users_o;
}
}
+21 -2
View File
@@ -17,9 +17,9 @@ class router
$this->routeClasses = [];
}
public function add($route, $method, $function): void
public function add($route, $method, $function, array $permissions = []): void
{
$this->routes[] = ['route' => $route, 'method' => $method, 'function' => $function];
$this->routes[] = ['route' => $route, 'method' => $method, 'function' => $function, 'permissions' => $permissions];
}
public function auto_load_routes(string $path): void
@@ -106,4 +106,23 @@ class router
$response->internal_server_error($e->getMessage());
}
}
public function countRoutes(): int
{
return count($this->routes);
}
public function getPermissions(): array
{
$permissions = [];
foreach ( $this->routes as $route ) {
$permissions = array_merge($permissions, $route['permissions']);
}
return $permissions;
}
public function getRoutes(): array
{
return $this->routes;
}
}
+17
View File
@@ -7,6 +7,8 @@ require_once WD . '/modules/stripe/endpoints/stripe_endpoint_customers.php';
require_once WD . '/modules/stripe/endpoints/stripe_endpoint_payment_link.php';
require_once WD . '/modules/stripe/endpoints/stripe_endpoint_product.php';
require_once WD . '/modules/stripe/endpoints/stripe_endpoint_prices.php';
require_once WD . '/modules/stripe/endpoints/stripe_endpoint_invoice.php';
require_once WD . '/modules/stripe/endpoints/stripe_endpoint_readers.php';
// Require all helper classes
@@ -19,9 +21,11 @@ require_once WD . '/modules/stripe/helpers/stripe_price.php';
use Exception;
use interfaces\stripe_i;
use stripe\endpoints\stripe_endpoint_customers;
use stripe\endpoints\stripe_endpoint_invoice;
use stripe\endpoints\stripe_endpoint_payment_link;
use stripe\endpoints\stripe_endpoint_prices;
use stripe\endpoints\stripe_endpoint_product;
use stripe\endpoints\stripe_endpoint_readers;
use stripe\stripe_c;
use Stripe\StripeClient;
@@ -56,12 +60,23 @@ class stripe implements stripe_i
* @var stripe_endpoint_prices
*/
public stripe_endpoint_prices $prices;
/**
* Invoice endpoint
* @var stripe_endpoint_invoice
*/
public stripe_endpoint_invoice $invoice;
/**
* Readers endpoint
* @var stripe_endpoint_readers
*/
public stripe_endpoint_readers $readers;
/**
* Stripe client
* @var StripeClient
*/
protected StripeClient $client;
public function __construct()
{
$this->config = new stripe_c();
@@ -69,6 +84,8 @@ class stripe implements stripe_i
$this->payment_link = new stripe_endpoint_payment_link();
$this->product = new stripe_endpoint_product();
$this->prices = new stripe_endpoint_prices();
$this->invoice = new stripe_endpoint_invoice();
$this->readers = new stripe_endpoint_readers();
}
/**
+9 -1
View File
@@ -10,6 +10,14 @@
"aws/aws-sdk-php": "^3.0",
"predis/predis": "*",
"phpmailer/phpmailer": "^6.9.2",
"stripe/stripe-php": "^16.5"
"stripe/stripe-php": "^16.5",
"php-http/guzzle7-adapter": "^1.1",
"nyholm/psr7": "^1.8",
"mailersend/mailersend": "^0.28.0"
},
"config": {
"allow-plugins": {
"php-http/discovery": true
}
}
}
+1111 -1
View File
File diff suppressed because it is too large Load Diff
+21 -1
View File
@@ -1,6 +1,7 @@
<?php
// prevent direct access
use classes\backup_store;
use objects\bookings_o;
use objects\logs_o;
use objects\users_o;
@@ -39,7 +40,7 @@ $cron_tasks = [
'function' => 'syncLogsToDatabase',
],
'SyncUserEconomicCustomerDiscounts' => [
'interval' => 600, // 10 minutes
'interval' => 60, // 1 minute
'last_run' => 0,
'next_run' => 0,
'function' => 'SyncUserEconomicCustomerDiscounts',
@@ -50,10 +51,19 @@ $cron_tasks = [
'next_run' => 0,
'function' => 'SyncUserEconomicCustomerDetails',
],
'backup' => [
'interval' => 43200, // 12 hours
'last_run' => 0,
'next_run' => 0,
'function' => 'backup',
],
];
function checkUnfulfilledBookings(): void
{
// This is deactivated for now, as it is not wanted.
// I'm saving this for later, as it is a good idea to have this in place.
return;
$bookings_o = new bookings_o();
$bookings_o->checkUnfulfilledBookings();
}
@@ -70,6 +80,16 @@ function syncLogsToDatabase(): void
$logs_o->syncLogsToDatabase();
}
function backup(): void
{
try {
$backup = new backup_store();
$backup->createBackup();
} catch (Exception $e) {
warn('Backup failed: ' . $e->getMessage());
}
}
function SyncUserEconomicCustomerDiscounts(): void
{
$users_o = new users_o();
+1
View File
@@ -60,6 +60,7 @@ require_once 'classes/email.php';
require_once 'classes/backup_store.php';
require_once 'classes/motorapi.php';
require_once 'classes/stripe.php';
require_once 'classes/form.php';
/**
* Modules
+24 -5
View File
@@ -2,14 +2,17 @@
namespace interfaces;
use Exception;
interface motorapi_i
{
/**
* Get license plate information from the motorapi
* @param string $licensePlate The license plate to get information about (e.g. "AB12345")
* @param bool $ignoreCache Whether to ignore the cache or not. If true, the cache will be ignored and the request will be sent to the motorapi directly.
* @return object The license plate information
*/
function getLicensePlateInformation(string $licensePlate): object;
function getLicensePlateInformation(string $licensePlate, bool $ignoreCache = true): object;
/**
* Get the daily request counter
@@ -30,7 +33,7 @@ interface motorapi_i
/**
* Require the module to be enabled
* @return void
* @throws \Exception If the module is not enabled
* @throws Exception If the module is not enabled
*/
function requireModuleEnabled(): void;
@@ -38,21 +41,21 @@ interface motorapi_i
* Require the license plate to be valid
* @param string $licensePlate The license plate to validate
* @return void
* @throws \Exception If the license plate is not valid
* @throws Exception If the license plate is not valid
*/
function requireValidLicensePlate(string $licensePlate): void;
/**
* Require the daily limit to not be exceeded
* @return void
* @throws \Exception If the daily limit is exceeded
* @throws Exception If the daily limit is exceeded
*/
function requireDailyLimitNotExceeded(): void;
/**
* Require the secret key to be valid
* @return void
* @throws \Exception If the secret key is not valid
* @throws Exception If the secret key is not valid
*/
function requireValidSecretKey(): void;
@@ -100,4 +103,20 @@ interface motorapi_i
* @return void
*/
function addRequestToLog(string $licensePlate, string $endpoint, object $response): void;
/**
* Check if a license plate response is stored in the log/local database/cache
* @param string $licensePlate The license plate to check
* @return bool Whether the license plate response is stored in the log/local database/cache
* @throws Exception If the license plate is not valid
*/
function isCached(string $licensePlate): bool;
/**
* Get the cached response for a license plate
* @param string $licensePlate The license plate to get the cached response for
* @return object The (latest) cached response for the license plate
* @throws Exception If the license plate is not valid or the response is not cached
*/
function getCachedResult(string $licensePlate): object;
}
@@ -0,0 +1,28 @@
<?php
namespace config;
use traits\module_config_variable;
class economic_payment_terms_c
{
use module_config_variable;
/**
* @throws \Exception
*/
public function __construct()
{
self::setupConfigVariable(
'economic',
'paymentTermsNumber',
'int',
true,
null,
'The payment terms number to use when creating invoices',
'1',
false,
1
);
}
}
@@ -113,7 +113,7 @@ class economicCustomers extends economic_m
// Define filterable property groups
$likeSupported = [
'zip', 'customerNumber', 'customerGroup.customerGroupNumber', 'name', 'address',
'city', 'country', 'email', 'telephoneAndFaxNumber', 'website', 'mobilePhone'
'city', 'country', 'email', 'telephoneAndFaxNumber', 'website', 'mobilePhone', 'corporateIdentificationNumber'
];
// If a search term is present, build the filter expressions
@@ -1,7 +1,9 @@
<?php
require_once WD . '/modules/economic/config/economic_invoice_layout_c.php';
require_once WD . '/modules/economic/config/economic_payment_terms_c.php';
use config\economic_invoice_layout_c;
use config\economic_payment_terms_c;
use traits\module_config_t;
class economic_c
@@ -9,13 +11,16 @@ class economic_c
use module_config_t;
public economic_invoice_layout_c $invoice_layout;
public economic_payment_terms_c $payment_terms;
public function __construct()
{
$this->setupConfig('economic');
$this->allowUpdate([
economic_invoice_layout_c::class
economic_invoice_layout_c::class,
economic_payment_terms_c::class
]);
$this->invoice_layout = new economic_invoice_layout_c();
$this->payment_terms = new economic_payment_terms_c();
}
}
@@ -0,0 +1,21 @@
<?php
require_once WD . '/modules/economic/helpers/economic_customer.php';
require_once WD . '/modules/economic/helpers/economic_invoice_draft.php';
use helpers\economic_customer;
use helpers\economic_invoice_draft;
class economic_helpers
{
/**
* The Economic customer helper
* @var string $economic_customer
*/
public string $economic_customer = economic_customer::class;
/**
* The Economic invoice draft helper
* @var string $economic_invoice_draft
*/
public string $economic_invoice_draft = economic_invoice_draft::class;
}
@@ -36,5 +36,4 @@ class economic_customers_endpoint
// Return true if the customer exists
return isset($tmp->customerNumber);
}
}
@@ -2,6 +2,9 @@
namespace endpoints\orders;
use classes\economic;
use Exception;
use objects\orders_o;
use traits\economic_endpoint_t;
class economic_invoices_draft_endpoint
@@ -12,7 +15,7 @@ class economic_invoices_draft_endpoint
* Get draft invoice
* @param int $invoice_id Invoice id to get
* @return object {invoice}
* @throws \Exception If the request fails
* @throws Exception If the request fails
*/
public function get(int $invoice_id): object
{
@@ -23,4 +26,66 @@ class economic_invoices_draft_endpoint
// Return the response as an object
return json_decode($response);
}
/**
* Get draft invoice from external id
* @param string $external_id External id to get
* @return int Invoice id
* @throws Exception If the request fails
* @throws Exception If the invoice is not found
*/
public function get_from_external_id(string $external_id): int
{
$response = $this->send_request(
'/invoices/drafts?filter=references.other$eq:' . $external_id,
'GET'
);
// Return the response as an object
$response = json_decode($response);
//var_dump($response);
if (isset($response->collection[0])) {
return $response->collection[0]->draftInvoiceNumber;
} else {
throw new Exception('Invoice draft with external id (' . $external_id . ') not found');
}
}
/**
* Add an order to a draft invoice
* @param int $invoiceDraftId The invoice draft id to add the order to
* @param orders_o $order The order to add
* @return void
* @throws Exception If the request fails
*/
public function add_order(int $invoiceDraftId, orders_o $order): void
{
$draftInvoice = (new economic())->getInvoiceDraft($invoiceDraftId);
// Add the transaction header (Timestamp, department, etc.)
$draftInvoice->addNewTransactionHeader($order);
// Add the order lines
$draftInvoice->addOrderItemLines($order);
// Add an empty line, so the invoice is not empty
$draftInvoice->addTextLine('');
// Save the draft invoice lines
$draftInvoice->addLines();
}
/**
* Add lines to a draft invoice
* @param int $draft_invoice_number The draft invoice number
* @param array $draft_lines The draft lines to add
* @return object {draftInvoiceNumber: number, lines: [invoice_line]}
*/
public function add_lines(int $draft_invoice_number, array $draft_lines): object
{
$response = $this->send_request(
'/invoices/drafts/' . $draft_invoice_number . '/lines',
'POST',
json_encode(
['lines' => $draft_lines]
)
);
return json_decode($response);
}
}
@@ -2,6 +2,7 @@
namespace endpoints\orders;
use Exception;
use traits\economic_endpoint_t;
class economic_invoices_booked_endpoint
@@ -42,4 +43,27 @@ class economic_invoices_booked_endpoint
return $tmp_invoices;
}
/**
* Get booked invoice from external id
* @param string $external_id External id to get
* @return int Invoice id
* @throws Exception If the request fails
* @throws Exception If the invoice is not found
*/
public function get_from_external_id(string $external_id): int
{
$response = $this->send_request(
'/invoices/booked?filter=references.other$eq:' . $external_id,
'GET'
);
// Return the response as an object
$response = json_decode($response);
//var_dump($response);
if (isset($response->collection[0])) {
return $response->collection[0]->bookedInvoiceNumber;
} else {
throw new Exception('Invoice booked with external id (' . $external_id . ') not found');
}
}
}
@@ -2,6 +2,8 @@
namespace endpoints\orders;
use classes\economic;
use Exception;
use traits\economic_endpoint_t;
class economic_invoices_drafts_endpoint
@@ -43,4 +45,88 @@ class economic_invoices_drafts_endpoint
return $tmp_invoices;
}
/**
* Add a new invoice
* @param int $customer_number The E-conomic customer number
* @param string $external_id The external id of the invoice
* @return object {id: number, getExternalId: string}
* @throws Exception If the request fails
*/
public function add(int $customer_number, string $external_id = ''): object
{
// Get the layout number
$layout_number = (int)(new economic())->config->invoice_layout->getVariableValue();
// Get the date
$date = date('Y-m-d');
// Get the customer object
$customer = (new economic())->getCustomer($customer_number);
// Set the recipient details
$customer_name = $customer->getName() ?? 'Ukendt';
$customer_address = $customer->getAddress() ?? 'Ukendt';
$customer_zip = $customer->getZipCode() ?? 'Ukendt';
$customer_city = $customer->getCity() ?? 'Ukendt';
// Send the request
$response = $this->send_request(
'/invoices/drafts/',
'POST',
json_encode([
// Set the layout number (This is defined in the E-conomic module settings)
'layout' => [
'layoutNumber' => $layout_number
],
// Set the date to today
'date' => $date,
// Set the payment terms to the default payment terms (This is defined in the E-conomic module settings)
'paymentTerms' => [
'paymentTermsNumber' => (int)$customer->getPaymentTermsNumber(),
],
// Set the customer number
'customer' => [
'customerNumber' => $customer_number
],
// Set the external id (This is used to link the invoice to the collected order invoice)
'references' => [
'other' => $external_id
],
// Set the currency TODO: Make this dynamic
'currency' => 'DKK',
// Set the recipient details
'recipient' => [
'name' => $customer_name,
'address' => $customer_address,
'zip' => $customer_zip,
'city' => $customer_city,
'vatZone' => [
'vatZoneNumber' => 1
],
],
])
);
// Return the response as an object
return json_decode($response);
}
public function exists(int $draft_invoice_number): bool
{
$response = $this->send_request(
'/invoices/drafts/' . $draft_invoice_number,
'GET'
);
// Return the response as an object
try {
return json_decode($response)->draftInvoiceNumber == $draft_invoice_number;
} catch (Exception $e) {
return false;
}
}
}
@@ -0,0 +1,207 @@
<?php
namespace helpers;
use classes\economic;
use Exception;
class economic_customer
{
/**
* The Economic customerNumber, which is the unique identifier for a customer
* @var int $customer_number
*/
protected int $customer_number;
/**
* The raw customer data from the Economic API
* @var object $customer_data
*/
protected object $customer_data_object;
/**
* Construct a new Economic customer object
* @throws Exception if the customer does not exist
*/
public function __construct(int $customer_number)
{
self::setCustomerNumber($customer_number);
self::requireExists();
self::fetchCustomerData();
self::requireSelected();
}
/**
* Check if a customer exists
* @param int $customer_number The Economic customer number to check for
* @throws Exception if the customer does not exist
*/
protected function requireExists(): void
{
if (!self::exists()) {
throw new Exception('The customer does not exist in the Economic system');
}
}
/**
* Check if a customer exists in the Economic system
* @return bool True if the customer exists, false if not
* @throws Exception
*/
public function exists(): bool
{
// Make sure the customer number is set
self::requireCustomerNumber();
// Check if the customer exists
$economic = new economic();
return $economic->customers->customers->exists(self::getCustomerNumber());
}
/**
* Require that the customer number is set
* @throws Exception if the customer number is not set
*/
protected function requireCustomerNumber(): void
{
if (!isset($this->customer_number)) {
throw new Exception('The customer number is not set');
}
}
/**
* Get the customer number
* @return int The Economic customer number
*/
public function getCustomerNumber(): int
{
return $this->customer_number;
}
/**
* Set the customer number
* @param int $customer_number The Economic customer number
*/
protected function setCustomerNumber(int $customer_number): void
{
$this->customer_number = $customer_number;
}
/**
* Fetch the customer data from the Economic API and store it in the object property $customer_data_object
* @throws Exception if the customer data could not be fetched
* @throws Exception if the customer data is invalid or empty
*/
protected function fetchCustomerData(): void
{
// Get the customer data from the Economic API
$economic = new economic();
$tmp = $economic->customers->customers->get($this->customer_number);
// Check if the customer data is invalid or empty
if (!isset($tmp->customerNumber)) {
throw new Exception('The customer data is invalid or empty');
}
// Store the customer data in the object property
$this->customer_data_object = $tmp;
}
/**
* Require that a customer is selected, i.e. that the customer data is valid and not empty
* @throws Exception if the customer data is invalid or empty
*/
protected function requireSelected(): void
{
if (!isset($this->customer_data_object->customerNumber)) {
throw new Exception('The customer data is invalid or empty');
}
}
/**
* Get the customer data as an object
* @return object The customer data as an object
* @throws Exception if the customer data is invalid or empty
*/
public function asObject(): object
{
self::requireSelected();
return $this->customer_data_object;
}
/**
* Get the customer name
* @return string The customer name
* @throws Exception if the customer data is invalid or empty
*/
public function getName(): string
{
self::requireSelected();
return $this->customer_data_object->name;
}
/**
* Get the customer email
* @return string The customer email
* @throws Exception if the customer data is invalid or empty
*/
public function getEmail(): string
{
self::requireSelected();
return $this->customer_data_object->email;
}
/**
* Get the customer address
* @return string The customer address
* @throws Exception if the customer data is invalid or empty
*/
public function getAddress(): string
{
self::requireSelected();
return $this->customer_data_object->address;
}
/**
* Get the customer postal code
* @return string The customer postal code
* @throws Exception if the customer data is invalid or empty
*/
public function getZipCode(): string
{
self::requireSelected();
return $this->customer_data_object->zip;
}
/**
* Get the customer city
* @return string The customer city
* @throws Exception if the customer data is invalid or empty
*/
public function getCity(): string
{
self::requireSelected();
return $this->customer_data_object->city;
}
/**
* Get the customer country
* @return string The customer country
* @throws Exception if the customer data is invalid or empty
*/
public function getCountry(): string
{
self::requireSelected();
return $this->customer_data_object->country;
}
/**
* Get the customers payment terms number
* @return int The customers payment terms number
* @throws Exception if the customer data is invalid or empty
*/
public function getPaymentTermsNumber(): int
{
self::requireSelected();
return $this->customer_data_object->paymentTerms->paymentTermsNumber;
}
}
@@ -0,0 +1,322 @@
<?php
namespace helpers;
use classes\economic;
use Exception;
use objects\departments_o;
use objects\orders_o;
class economic_invoice_draft
{
/**
* The Economic draftInvoiceNumber
* @var int $draft_invoice_number
*/
protected int $draft_invoice_number;
/**
* The draft lines
* @var array $draft_lines
*/
protected array $draft_lines = [];
/**
* The raw draft invoice data
* @var object $draft_invoice_data
*/
protected object $draft_invoice_data;
/**
* Construct a new Economic draft invoice object
* @throws Exception if the invoice data is invalid or empty
*/
public function __construct(int $draft_invoice_number)
{
$this->setDraftInvoiceNumber($draft_invoice_number);
$this->fetchDraftInvoiceData();
$this->requireSelected();
}
/**
* Set the draft invoice number
* @param int $draft_invoice_number
* @return void
*/
private function setDraftInvoiceNumber(int $draft_invoice_number): void
{
$this->draft_invoice_number = $draft_invoice_number;
}
/**
* Fetch the draft invoice data from the Economic system
* @throws Exception If the request fails
* @throws Exception If the invoice is not found
*/
private function fetchDraftInvoiceData(): void
{
$economic = new economic();
$this->draft_invoice_data = $economic->invoices->draft->get($this->draft_invoice_number);
if (!isset($this->draft_invoice_data->draftInvoiceNumber)) {
throw new Exception('Draft invoice not found');
}
}
/**
* Require that the draft invoice data is selected
* @throws Exception if the draft invoice data is not set
*/
protected function requireSelected(): void
{
if (!isset($this->draft_invoice_data->draftInvoiceNumber)) {
throw new Exception('The draft invoice data is not set');
}
}
/**
* Add the lines to the draft invoice
* @return void
*/
public function addLines(): void
{
$economic = new economic();
$economic->invoices->draft->add_lines($this->draft_invoice_number, $this->draft_lines);
}
/**
* Get the draft invoice number
* @return int The draft invoice number
*/
public function getDraftInvoiceNumber(): int
{
return $this->draft_invoice_number;
}
/**
* Add a new transaction header to the draft invoice
* @note The lines won't be saved until the addLines() method is called.
* @note This is the "[28/02/2025 18:29 Roskilde #685]" line in the invoice
* @param orders_o $order The order to add
* @return void
*/
public function addNewTransactionHeader(orders_o $order): void
{
// Get the department name
$department_name = (new departments_o())->getDepartmentName((int)$order->department_id->value());
// Parse the date of the transaction.
$parsed_date = date('d/m/Y H:i', strtotime($order->created_at->value()));
// Add the text line to the draft invoice
self::addTextLine("[ " . $parsed_date . ' ' . $department_name . ' #' . $order->id . " ]");
// If there's a reference, add it to the invoice
if ($order->reference->value() !== '') {
self::addTextLine('Reference:');
// Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
if (str_contains($order->reference->value(), "\n")) {
foreach ( explode("\n", $order->reference->value()) as $line ) {
self::addTextLine('# ' . $line);
}
} else {
self::addTextLine('# ' . $order->reference->value());
}
}
// Add the registration numbers (if any)
$line_reg = '';
if ($order->reg_1->value() !== '')
$line_reg .= 'Reg 1: ' . strtoupper($order->reg_1->value());
if ($order->reg_2->value() !== '')
$line_reg .= ', Reg 2: ' . strtoupper($order->reg_2->value());
if ($order->reg_3->value() !== '')
$line_reg .= ', Reg 3: ' . strtoupper($order->reg_3->value());
// Add the line to the invoice (If there's any registration numbers)
if ($line_reg !== '')
self::addTextLine($line_reg);
// If there's a note, add it to the invoice
if ($order->notes->value() !== '') {
self::addTextLine('Notat:');
// Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
if (str_contains($order->notes->value(), "\n")) {
foreach ( explode("\n", $order->notes->value()) as $line ) {
self::addTextLine('# ' . $line);
}
} else {
self::addTextLine('# ' . $order->notes->value());
}
}
}
/**
* Add a text line to the draft invoice
* @note The lines won't be saved until the addLines() method is called.
* @param string $text The text to add
* @return void
*/
public function addTextLine(string $text): void
{
$this->draft_lines[] = [
'description' => $text
];
}
/**
* Add an order to the draft invoice
* @note The lines won't be saved until the addLines() method is called.
* @param orders_o $order The order to add
* @return void
* @throws Exception if the order is not found
* @throws Exception if the order is not valid
*/
public function addOrderItemLines(orders_o $order): void
{
// Get the order items
$order_items = $order->getOrderItems($order->id);
// Apply the department pricing
$order_items = $order->applyDepartmentPrices($order_items, $order->department_id->value());
// Get the department
$department = $order->getDepartmentByOrderId($order->id);
// Loop through the order items
foreach ( $order_items as $order_item ) {
// Add the order item to the draft invoice
self::addOrderItemLine($order_item, $department);
}
}
/**
* Add an order item line to the draft invoice
* @note The lines won't be saved until the addLines() method is called.
* @param array $order_item The order item to add
* @param array $department The department to add
* @return void
* @throws Exception if the order item is not found
* @throws Exception if the order item is not valid
*/
public function addOrderItemLine(array $order_item, array $department): void
{
// Check if the order item is valid
if (!isset($order_item['id'])) {
throw new Exception('The order item is not valid');
}
// Get the department id
$economic_department_id = $department['economic_department_id'];
// Get the dimension id
$economic_dimension_id = $department['economic_dimension_id'] ?? 0;
// Add the order item to the draft invoice
self::addProductLine(
(string)$order_item['product']['economic_product_id'],
(string)$order_item['product']['name'],
(int)$order_item['quantity'] ?? 1,
(int)$order_item['price'],
$economic_department_id,
$economic_dimension_id
);
// Calculate the discount percentage
$discountPercentage = round((($order_item['product']['price'] - $order_item['price']) / $order_item['product']['price']) * 100, 0);
// If the price is different from the product price, add it to the line
if ($order_item['price'] !== $order_item['product']['price'])
self::addTextLine('Rabat: ' . ($order_item['price'] - $order_item['product']['price']) . ' DKK (' . $discountPercentage . '%)');
// If there's a reference, add it to the line
if ($order_item['reference'] !== '') {
self::addTextLine('Reference:');
// Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
if (str_contains($order_item['reference'], "\n")) {
foreach ( explode("\n", $order_item['reference']) as $line ) {
self::addTextLine('# ' . $line);
}
} else {
self::addTextLine('# ' . $order_item['reference']);
}
}
// If there's a note, add it to the line
if (!empty($order_item['notes'])) {
self::addTextLine('Notat:');
// Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
if (str_contains($order_item['notes'], "\n")) {
foreach ( explode("\n", $order_item['notes']) as $line ) {
self::addTextLine('# ' . $line);
}
} else {
self::addTextLine('# ' . $order_item['notes']);
}
}
}
/**
* Add a product line to the draft invoice
* @note The lines won't be saved until the addLines() method is called.
*/
public function addProductLine(string $productNumber, string $description, float $quantity, float $unitNetPrice, 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' => 0,
'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->draft_lines[] = $line;
}
/**
* Check if a draft invoice exists in the Economic system
* @throws Exception if the draft invoice does not exist
*/
protected function requireExists(): void
{
if (!self::exists()) {
throw new Exception('The draft invoice does not exist');
}
}
/**
* Check if a customer exists in the Economic system
* @return bool True if the customer exists, false if not
* @throws Exception
*/
public function exists(): bool
{
// Make sure the draft invoice number is set before calling
self::requireDraftInvoiceNumber();
// Check if the draft invoice exists
$economic = new economic();
return $economic->invoices->drafts->exists($this->draft_invoice_number);
}
/**
* Require that the draft invoice number is set
* @throws Exception if the draft invoice number is not set
*/
protected function requireDraftInvoiceNumber(): void
{
if (!isset($this->draft_invoice_number)) {
throw new Exception('The draft invoice number is not set');
}
}
/**
* Require that the draft invoice data is set
* @throws Exception if the draft invoice data is not set
*/
protected function requireDraftInvoiceData(): void
{
if (!isset($this->draft_invoice_data)) {
throw new Exception('The draft invoice data is not set');
}
}
}
@@ -0,0 +1,29 @@
<?php
namespace email\config;
use Exception;
use traits\module_config_variable;
class email_mailersend_api_key_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'Email',
'mailersend_api_key',
'string',
false,
null,
'The API key for the MailerSend email service',
'mlsn.7dd13651...',
true,
''
);
}
}
@@ -0,0 +1,29 @@
<?php
namespace email\config;
use Exception;
use traits\module_config_variable;
class email_mailersend_enabled_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'Email',
'mailersend_enabled',
'bool',
true,
null,
'Whether the email service should use MailerSend for sending emails',
'1',
false,
'false'
);
}
}
@@ -0,0 +1,29 @@
<?php
namespace email\config;
use Exception;
use traits\module_config_variable;
class email_smtp_reply_to_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'Email',
'smtp_reply_to',
'string',
true,
null,
'The email address to reply to',
'user@example.com',
false,
''
);
}
}
@@ -0,0 +1,29 @@
<?php
namespace email\config;
use Exception;
use traits\module_config_variable;
class email_smtp_reply_to_name_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'Email',
'smtp_reply_to_name',
'string',
true,
null,
'The name to reply to',
'Company Name Support',
false,
''
);
}
}
+46 -3
View File
@@ -1,22 +1,34 @@
<?php
namespace email;
/** Universal */
require_once WD . '/modules/email/config/email_enabled_c.php';
require_once WD . '/modules/email/config/email_smtp_from_c.php';
require_once WD . '/modules/email/config/email_smtp_from_name_c.php';
require_once WD . '/modules/email/config/email_smtp_reply_to_c.php';
require_once WD . '/modules/email/config/email_smtp_reply_to_name_c.php';
/** SMTP */
require_once WD . '/modules/email/config/email_smtp_encryption_c.php';
require_once WD . '/modules/email/config/email_smtp_host_c.php';
require_once WD . '/modules/email/config/email_smtp_password_c.php';
require_once WD . '/modules/email/config/email_smtp_port_c.php';
require_once WD . '/modules/email/config/email_smtp_username_c.php';
require_once WD . '/modules/email/config/email_smtp_from_c.php';
require_once WD . '/modules/email/config/email_smtp_from_name_c.php';
/** Mailersend */
require_once WD . '/modules/email/config/email_mailersend_enabled_c.php';
require_once WD . '/modules/email/config/email_mailersend_api_key_c.php';
use email\config\email_enabled_c;
use email\config\email_mailersend_api_key_c;
use email\config\email_mailersend_enabled_c;
use email\config\email_smtp_encryption_c;
use email\config\email_smtp_from_c;
use email\config\email_smtp_from_name_c;
use email\config\email_smtp_host_c;
use email\config\email_smtp_password_c;
use email\config\email_smtp_port_c;
use email\config\email_smtp_reply_to_c;
use email\config\email_smtp_reply_to_name_c;
use email\config\email_smtp_username_c;
use traits\module_config_t;
@@ -72,6 +84,29 @@ class email_c
*/
public email_smtp_from_name_c $smtp_from_name;
/**
* The status of Mailersend, whether it is enabled or not
* @var email_mailersend_enabled_c
*/
public email_mailersend_enabled_c $mailersend_enabled;
/**
* The API key for the Mailersend email service
* @var email_mailersend_api_key_c
*/
public email_mailersend_api_key_c $mailersend_api_key;
/**
* The email address to reply to
* @var email_smtp_reply_to_c The email address to reply to
*/
public email_smtp_reply_to_c $smtp_reply_to;
/**
* The name to reply to
* @var email_smtp_reply_to_name_c The name to reply to
*/
public email_smtp_reply_to_name_c $smtp_reply_to_name;
public function __construct()
{
@@ -84,7 +119,11 @@ class email_c
email_smtp_password_c::class,
email_smtp_encryption_c::class,
email_smtp_from_c::class,
email_smtp_from_name_c::class
email_smtp_from_name_c::class,
email_mailersend_enabled_c::class,
email_mailersend_api_key_c::class,
email_smtp_reply_to_c::class,
email_smtp_reply_to_name_c::class
]);
$this->enabled = new email_enabled_c();
$this->smtp_host = new email_smtp_host_c();
@@ -94,6 +133,10 @@ class email_c
$this->smtp_encryption = new email_smtp_encryption_c();
$this->smtp_from = new email_smtp_from_c();
$this->smtp_from_name = new email_smtp_from_name_c();
$this->mailersend_enabled = new email_mailersend_enabled_c();
$this->mailersend_api_key = new email_mailersend_api_key_c();
$this->smtp_reply_to = new email_smtp_reply_to_c();
$this->smtp_reply_to_name = new email_smtp_reply_to_name_c();
}
}
@@ -0,0 +1,12 @@
<?php
namespace email\helpers;
trait email_template
{
protected string $subject;
abstract function generate_html(): string;
abstract function generate_text(): string;
}
@@ -0,0 +1,40 @@
<?php
namespace email\templates;
use email\helpers\email_template;
class email_template_footer
{
use email_template;
public function generate_text(): string
{
return "Truckwash ApS | CVR: 41004355 | https://truckwash.dk | Send os en email på cph@truckwash.dk - Ring til os på (+45) 43 71 78 86";
}
public function generate_html(): string
{
ob_start();
# Start of the html
?>
<!-- Footer -->
<br><br>
<div style="max-width: 600px;">
<img src="https://usercontent.one/wp/www.truckwash.dk/wp-content/uploads/2021/04/truckwash-banner-png.png"
alt="Truck Wash logo" style="width: 25%; height: auto; display: block; margin: 0 auto;">
<p style="text-align: center;"><a href="https://truckwash.dk/">Truckwash ApS</a> | <a
href="https://datacvr.virk.dk/enhed/virksomhed/41004355"> CVR: 41004355</a> | <a
href="https://truckwash.dk/">https://truckwash.dk</a></p>
<p style="text-align: center;"><a href="mailto:cph@truckwash.dk"
style="color: black; text-decoration: none;">Send os en email på
cph@truckwash.dk</a> - <a href="tel:+4543717886" style="color: black; text-decoration: none;">Ring
til os på (+45) 43 71 78 86</a></p>
</div>
<?php
# End of the html
return ob_get_clean();
}
}
@@ -0,0 +1,45 @@
<?php
namespace email\templates;
use email\helpers\email_template;
class email_template_head
{
use email_template;
public function generate_text(): string
{
return "";
}
public function generate_html(): string
{
ob_start();
# Start of the html
?>
<!-- Styles -->
<style>
.email-template {
width: 100%;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
font-family: Arial, sans-serif;
}
.email-template h1 {
color: #333;
}
.email-template p {
color: #333;
}
</style>
<?php
# End of the html
return ob_get_clean();
}
}
@@ -0,0 +1,33 @@
<?php
namespace email\templates;
use email\helpers\email_template;
class email_template_header
{
use email_template;
public function generate_text(): string
{
return "";
}
public function generate_html(): string
{
ob_start();
# Start of the html
?>
<!-- Logo -->
<div style="max-width: 600px;">
<img src="https://usercontent.one/wp/www.truckwash.dk/wp-content/uploads/2021/04/truckwash-banner-png.png"
alt="Truck Wash logo"
style="width: 100%; max-width: 600px; height: auto; display: block; margin: 0 auto;">
</div>
<?php
# End of the html
return ob_get_clean();
}
}
@@ -0,0 +1,44 @@
<?php
namespace email\templates;
use email\helpers\email_template;
class email_template_stripe_invoice
{
use email_template;
protected int $order_id;
protected string $stripe_payment_link;
protected string $name;
public function __construct(int $order_id, string $stripe_payment_link, string $name)
{
$this->order_id = $order_id;
$this->stripe_payment_link = $stripe_payment_link;
$this->name = $name;
}
public function generate_text(): string
{
return "";
}
public function generate_html(): string
{
ob_start();
# Start of the html
?>
<!-- Email template -->
<p>Kære <?= $this->name ?>,</p>
<p>Tak for din bestilling hos Truck Wash. Vi har sendt dig en faktura på Stripe.
Du kan betale fakturaen ved at klikke på linket nedenfor:</p>
<p><a href="<?= $this->stripe_payment_link ?>">Betal faktura for ordre <?= $this->order_id ?></a></p>
<!-- End of the email template -->
<?php
# End of the html
return ob_get_clean();
}
}
@@ -0,0 +1,63 @@
<?php
namespace forms;
use traits\form_t;
class book_wash_f extends form_helper_c
{
use form_t;
/**
* @inheritDoc
*/
public function sanitize(): void
{
foreach ( $this->form_unsanitized_data as $key => $value ) {
// Sanitize the input TODO: Implement the sanitization logic
$this->form_unsanitized_data[$key] = $value;
}
// Set the sanitized data
$this->form_sanitized_data = $this->form_unsanitized_data;
}
/**
* @inheritDoc
*/
public function validateInput(): void
{
// TODO: Implement validateInput() method.
}
/**
* @inheritDoc
*/
public function afterSubmit(): void
{
// TODO: Implement afterSubmit() method.
}
/**
* @inheritDoc
*/
public function setup(): void
{
self::setFormIdentifier('BOOK_WASH');
self::setFormName('Book a wash');
self::setFormDescription('This form is used to book a wash for a vehicle.');
// Set the form fields
self::defineInputFields([
'customer_number' => self::className() . '::validateCustomerNumber',
'contact_email' => self::className() . '::validateEmail',
'reference' => self::className() . '::validateString',
'registration_number_tractor' => self::className() . '::validateRegistrationNumber',
'registration_number_trailer' => self::className() . '::validateRegistrationNumber',
'wants_wash_certificate' => self::className() . '::validateBoolean',
'wants_wash_certificate_email' => self::className() . '::validateEmail',
'date' => self::className() . '::validateDate',
'department_id' => self::className() . '::validateInt',
'wants_pickup' => self::className() . '::validateBoolean',
'notes' => self::className() . '::validateString',
]);
}
}
@@ -0,0 +1,7 @@
<?php
namespace forms;
abstract class form_helper_c
{
}
@@ -0,0 +1,68 @@
<?php
namespace motorapi\actions;
use Exception;
use traits\module_action_t;
class license_plate_lookup_a
{
use module_action_t;
/**
* @inheritDoc
* @throws Exception If the module name is invalid
*/
public function run(): void
{
self::set_module_name('MOTORAPI');
/** Add the action to the list of valid actions */
// LICENSE_PLATE_LOOKUP
self::add_action(
'LICENSE_PLATE_LOOKUP',
'This action is when a license plate lookup request is made',
// Method to call
self::class . '::license_plate_lookup',
[
'license_plate' => 'The license plate that was looked up',
'data' => 'The data returned from the lookup (If any)',
'status_code' => 'The status code of the action',
]
);
}
/**
* License plate lookup action
* @param string $license_plate The license plate that was looked up
* @param array|object $data The data returned from the lookup (If any)
* @param int $status_code The status code of the action
* @throws Exception If the action name is invalid
* @throws Exception If the action data is invalid
* @throws Exception If the action status code is invalid
* @throws Exception If the action is not valid
*/
public function license_plate_lookup(string $license_plate, array|object $data = [], int $status_code = 0): void
{
self::set_action('LICENSE_PLATE_LOOKUP');
// If the data is an object, convert it to an array
if (is_object($data)) {
$data = (array)$data;
}
// Validate the action data
if (!is_array($data)) {
throw new Exception('Action data is invalid');
}
// Validate the action status code
self::set_data([
'license_plate' => $license_plate,
'data' => $data,
]);
self::set_status($status_code);
// Add the action to the log
self::add_action_log(
self::get_action_name(),
(int)self::get_status(),
);
}
}
@@ -0,0 +1,17 @@
<?php
namespace motorapi\helpers;
class motorapi_vehicle_types
{
/**
* Personbil
* @var string
*/
public string $PLATE_TYPE_CAR = 'Personbil';
/**
* Lastbil
* @var string
*/
public string $PLATE_TYPE_TRUCK = 'Lastbil';
}
@@ -0,0 +1,158 @@
<?php
trait notification_type_t
{
/**
* The name of the notification type
* @var string
*/
public string $name;
/**
* The description of the notification type
* @var string
*/
public string $description;
/**
* The type of the notification
* @notation This is the identifier of the notification type, it is used to identify the notification type in the system.
* @var string
*/
public string $type;
/**
* The constructor of the notification type
* @throws Exception If the id is not set
* @throws Exception If the name is not set
* @throws Exception If the description is not set
*/
public function __construct()
{
self::run();
self::validate();
}
/**
* The run method, to be implemented by the class using this trait
* @notation This method should be implemented in the class using this trait, it is not meant to be called directly. It will be called when the notification type is triggered.
* @throws Exception If type is not set
* @throws Exception If name is not set
* @throws Exception If description is not set
*/
abstract public function run(): void;
/**
* Validate the notification type
* @throws Exception If the type is not set
* @throws Exception If the name is not set
* @throws Exception If the description is not set
*/
public function validate(): void
{
if (!isset($this->type)) {
throw new Exception('The type is not set');
}
if (empty($this->name)) {
throw new Exception('The name is not set');
}
if (empty($this->description)) {
throw new Exception('The description is not set');
}
}
/**
* Set the type of the notification
* @param string $type
* @throws Exception If the type is empty
* @throws Exception If the type is not a string
* @throws Exception If the type is not a valid format (e.g. "TYPE_NAME")
*/
public function set_type(string $type): void
{
if (empty($type)) {
throw new Exception('The type is empty');
}
if (!preg_match('/^[A-Z_]+$/', $type)) {
throw new Exception('The type is not a valid format (e.g. "TYPE_NAME")');
}
$this->type = $type;
}
/**
* Set the name of the notification type
* @param string $name
* @throws Exception If the name is empty
*/
public function set_name(string $name): void
{
if (empty($name)) {
throw new Exception('The name is empty');
}
$this->name = $name;
}
/**
* Set the description of the notification type
* @param string $description
* @throws Exception If the description is empty
*/
public function set_description(string $description): void
{
if (empty($description)) {
throw new Exception('The description is empty');
}
$this->description = $description;
}
/**
* Get the notification type as an array
* @return array
*/
public function as_array(): array
{
return [
'type' => $this->type,
'name' => $this->name,
'description' => $this->description,
'data' => self::get_data(),
];
}
/**
* Get the data of the notification type
* @notation This takes all data_* properties and returns them as an array
* @return array
*/
public function get_data(): array
{
// Get all the properties of the class
$properties = get_object_vars($this);
// Filter the properties to only include those that start with "data_"
$data_properties = array_filter($properties, function ($key) {
return str_starts_with($key, 'data_');
}, ARRAY_FILTER_USE_KEY);
// Remove the "data_" prefix from the keys
// Return the data properties as an array
return array_combine(
array_map(function ($key) {
return substr($key, 5);
}, array_keys($data_properties)),
array_values($data_properties)
);
}
/**
* Get the notification type as an object
* @return object
*/
public function as_object(): object
{
return (object)[
'type' => $this->type,
'name' => $this->name,
'description' => $this->description,
'data' => self::get_data(),
];
}
}
@@ -0,0 +1,25 @@
<?php
namespace types;
use notification_type_t;
class notifications_type_new_booking_c
{
use notification_type_t;
/**
* The id of the booking
* @var int $data_booking_id The id of the booking
*/
public int $data_booking_id;
/**
* @inheritDoc
*/
public function run(): void
{
self::set_type('NEW_BOOKING');
self::set_description('This notification is sent when a new booking is created');
}
}
@@ -0,0 +1,29 @@
<?php
namespace stripe\config;
use Exception;
use traits\module_config_variable;
class stripe_economic_customer_number_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'Stripe',
'economic_customer_number',
'int',
true,
null,
'The Economic customer number, to be used for invoicing',
'1',
false,
''
);
}
}
@@ -19,9 +19,11 @@ class stripe_endpoint_customers
* @throws ApiErrorException
* @throws Exception
*/
public function create(array $data): Customer
public function create(string $email): Customer
{
return self::getClient()->customers->create($data);
return self::getClient()->customers->create([
'email' => $email
]);
}
/**
@@ -0,0 +1,99 @@
<?php
namespace stripe\endpoints;
use Exception;
use objects\orders_o;
use Stripe\Exception\ApiErrorException;
use stripe\helpers\stripe_line_item;
use stripe\helpers\stripe_line_items;
use Stripe\Invoice;
use traits\stripe_endpoint_t;
class stripe_endpoint_invoice
{
use stripe_endpoint_t;
/**
* Retrieve a payment link
* @param string $id
* @return Invoice
* @throws ApiErrorException
* @throws Exception
*/
public function retrieve(string $id): Invoice
{
return self::getClient()->invoices->retrieve($id);
}
/**
* @throws ApiErrorException
* @throws Exception
*/
public function generate(int $order_id, string $stripe_customer_id): Invoice
{
$order = (new orders_o())->select($order_id);
$order->requireSelected();
// Get the order items
$order_items = $order->getOrderItems($order_id);
// Create a new line items object
$line_items = new stripe_line_items('invoice');
// Loop through the order items
foreach ( $order_items as $order_item ) {
// Add the order item to the line items
$line_items->add(
new stripe_line_item(
$order_item['product_id'],
$order_item['price'],
$order_item['quantity']
)
);
}
// Create the payment link
return $this->create(
$line_items,
$stripe_customer_id,
[
'order_id' => $order_id,
'customer_id' => $stripe_customer_id
]
);
}
/**
* Create a new payment link
* @param stripe_line_items $line_items
* @param string $stripe_customer_id
* @param array $metadata Additional metadata
* @return Invoice
* @throws ApiErrorException
* @throws Exception
*/
public function create(stripe_line_items $line_items, string $stripe_customer_id, array $metadata = []): Invoice
{
$invoice = self::getClient()->invoices->create([
'customer' => $stripe_customer_id,
'collection_method' => 'send_invoice',
'days_until_due' => 30,
'metadata' => $metadata,
'auto_advance' => true,
//'payment_settings' => [
// 'payment_method_types' => [
// 'card',
// 'paypal'
// ],
//],
]);
// Add the line items to the invoice
foreach ( $line_items->get() as $line_item ) {
self::getClient()->invoiceItems->create([
'customer' => $stripe_customer_id,
'invoice' => $invoice->id,
'price' => $line_item['price'],
]);
}
// Finalize the invoice
return $invoice->finalizeInvoice();
}
}
@@ -0,0 +1,158 @@
<?php
namespace stripe\endpoints;
use Exception;
use Stripe\Collection;
use Stripe\Exception\ApiErrorException;
use Stripe\Terminal\Location;
use Stripe\Terminal\Reader;
use traits\stripe_endpoint_t;
class stripe_endpoint_readers
{
use stripe_endpoint_t;
/**
* Get all readers
* @return Collection
* @throws ApiErrorException
* @throws Exception
*/
public function list(): Collection
{
return self::getClient()->terminal->readers->all();
}
/**
* Get all readers for a location
* @throws ApiErrorException If the location is not set
* @throws Exception If the location is not set
*/
public function listLocation(string $location_id): Collection
{
return self::getClient()->terminal->readers->all([
'location' => $location_id
]);
}
/**
* Register a reader
* @param string $label
* @param string $location
* @return Reader
* @throws ApiErrorException
* @throws Exception
*/
public function register(string $label, string $location): Reader
{
return self::getClient()->terminal->readers->create([
'label' => $label,
'location' => $location
]);
}
/**
* Get all locations
* @return Collection
* @throws ApiErrorException
* @throws Exception
*/
public function listLocations(): Collection
{
return self::getClient()->terminal->locations->all();
}
/**
* Get a location
* @param string $location_id
* @return Location
* @throws ApiErrorException
* @throws Exception
*/
public function retrieveLocation(string $location_id): Location
{
return self::getClient()->terminal->locations->retrieve($location_id);
}
/**
* Get a reader
* @param string $reader_id
* @return Reader
* @throws ApiErrorException
* @throws Exception
*/
public function retrieve(string $reader_id): Reader
{
return self::getClient()->terminal->readers->retrieve($reader_id);
}
/**
* Register a location
* @param string $label
* @return Location
* @throws ApiErrorException
* @throws Exception
*/
public function registerLocation(string $label): Location
{
return self::getClient()->terminal->locations->create([
'label' => $label
]);
}
/**
* Update a location
* @param string $location_id
* @param string $label
* @return Location
* @throws ApiErrorException
* @throws Exception
*/
public function updateLocation(string $location_id, string $label): Location
{
return self::getClient()->terminal->locations->update($location_id, [
'label' => $label
]);
}
/**
* Update a reader
* @param string $reader_id
* @param string $label
* @return Reader
* @throws ApiErrorException
* @throws Exception
*/
public function update(string $reader_id, string $label): Reader
{
return self::getClient()->terminal->readers->update($reader_id, [
'label' => $label
]);
}
/**
* Delete a location
* @param string $location_id
* @return Location
* @throws ApiErrorException
* @throws Exception
*/
public function deleteLocation(string $location_id): Location
{
return self::getClient()->terminal->locations->delete($location_id);
}
/**
* Delete a reader
* @param string $reader_id
* @return Reader
* @throws ApiErrorException
* @throws Exception
*/
public function delete(string $reader_id): Reader
{
return self::getClient()->terminal->readers->delete($reader_id);
}
}
@@ -98,8 +98,14 @@ class stripe_line_item
);
}
public function get(): array
public function get($type = 'payment_link'): array
{
if ($type === 'invoice') {
return [
'price' => (string)$this->price_id,
'amount' => (int)$this->quantity,
];
}
return [
//'product' => (int)$this->product_id,
'price' => (string)$this->price_id,
@@ -8,6 +8,22 @@ class stripe_line_items
* @var array $line_items
*/
protected array $line_items = [];
protected array $types = [
'invoice',
'payment_link'
];
protected string $type;
/**
* @throws \Exception
*/
public function __construct($type = 'payment_link')
{
if (!in_array($type, $this->types)) {
throw new \Exception('Invalid type');
}
$this->type = $type;
}
public function __toString(): string
{
@@ -20,7 +36,9 @@ class stripe_line_items
$line_items = [];
/** @var stripe_line_item|array $line_item */
foreach ( $this->line_items as $line_item ) {
$line_items[] = is_array($line_item) ? $line_item : $line_item->get();
$line_items[] = is_array($line_item)
? $line_item
: $line_item->get($this->type);
}
return $line_items;
}
+10 -1
View File
@@ -4,7 +4,9 @@ namespace stripe;
require_once WD . '/modules/stripe/config/stripe_enabled_c.php';
require_once WD . '/modules/stripe/config/stripe_publishable_key_c.php';
require_once WD . '/modules/stripe/config/stripe_secret_key_c.php';
require_once WD . '/modules/stripe/config/stripe_economic_customer_number_c.php';
use stripe\config\stripe_economic_customer_number_c;
use stripe\config\stripe_enabled_c;
use stripe\config\stripe_publishable_key_c;
use stripe\config\stripe_secret_key_c;
@@ -29,6 +31,11 @@ class stripe_c
* @var stripe_secret_key_c
*/
public stripe_secret_key_c $secret_key;
/**
* The economic customer number for stripe
* @var stripe_economic_customer_number_c
*/
public stripe_economic_customer_number_c $economic_customer_number;
public function __construct()
{
@@ -36,10 +43,12 @@ class stripe_c
$this->allowUpdate([
stripe_enabled_c::class,
stripe_publishable_key_c::class,
stripe_secret_key_c::class
stripe_secret_key_c::class,
stripe_economic_customer_number_c::class
]);
$this->enabled = new stripe_enabled_c();
$this->publishable_key = new stripe_publishable_key_c();
$this->secret_key = new stripe_secret_key_c();
$this->economic_customer_number = new stripe_economic_customer_number_c();
}
}
+28 -9
View File
@@ -269,6 +269,18 @@ class bookings_o extends db
public function checkUnfulfilledBookings(): void
{
$unfulfilled_bookings = [];
function addUnfulfilledBooking(int $department, array $arr): array
{
// Check if the department has a count in the unfulfilled bookings array
if (!isset($arr[$department])) {
$arr[$department] = 0;
}
// Add the unfulfilled booking to the department count
$arr[$department]++;
return $arr;
}
global $db;
// Get all the unfulfilled bookings
$bookings = $this->listObjectsWithPagination(1, 100000, null, ['status' => 'pending']);
@@ -278,19 +290,26 @@ class bookings_o extends db
echo "Booking with ID $booking[id] has been cancelled\n";
continue;
}
// Check if the booking is scheduled for the future
if (strtotime($booking['date']) > time()) {
continue;
}
// Check if the booking has been fulfilled
$fulfilled = $this->checkBookingFulfilled($booking['id']);
if (!$fulfilled) {
$unfulfilled_bookings = addUnfulfilledBooking($booking['department'], $unfulfilled_bookings);
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());
}
}
}
// Send a department webhook if the booking has not been fulfilled
foreach ( $unfulfilled_bookings as $department => $count ) {
$slack = new slack();
try {
$slack->send_department_booking_notification($department, 'There are ' . $count . ' unfulfilled bookings');
} catch (\Exception $e) {
// Log the error
$logs = new logs_o();
$logs->add('slack', 0, 3, 0, 'SEND_DEPARTMENT_UNFULFILLED_BOOKINGS_NOTIFICATION', $e->getMessage());
}
}
}
@@ -0,0 +1,654 @@
<?php
namespace objects;
use classes\db;
use classes\economic;
use classes\object_property;
use Exception;
use traits\db_object_t;
class collected_order_invoices_o extends db
{
use db_object_t;
public object_property $customer_number;
public object_property $name;
public object_property $notes;
public object_property $processor;
public object_property $external_id;
public object_property $booked_invoice_id;
public object_property $created_at;
public object_property $updated_at;
public object_property $closed_at;
public array $processors = [
1 => 'E-conomic',
2 => 'Stripe',
3 => 'Other, without tracking',
];
public function structure(): void
{
$this->setTable('collected_order_invoices');
}
/**
* List all collected order invoices for a customer
* @param int $customer_number The E-conomic customer number
* @return array The list of collected order invoices
* @throws Exception If the request was not successful
*/
public function getCustomerInvoiceCollections(int $customer_number): array
{
$collections = self::getFieldsWhere(
[
'customer_number' => $customer_number,
'deleted_at' => null
],
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at']
);
// Parse the results
$result = [];
foreach ( $collections as $collection ) {
$this->id = $collection['id'];
self::getObjectProperties();
self::requireSelected();
$result[] = self::asArray();
}
return $result;
}
public function getObjectProperties(): void
{
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', false);
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->processor = new object_property($this->table, $this->id, 'processor', 'int', false);
$this->external_id = new object_property($this->table, $this->id, 'external_id', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
$this->closed_at = new object_property($this->table, $this->id, 'closed_at', 'string', false);
$this->booked_invoice_id = new object_property($this->table, $this->id, 'booked_invoice_id', 'int', false);
}
public function asArray(): array
{
$tmp = [
'id' => (int)$this->id,
'customer_number' => (int)$this->customer_number->value(),
'name' => (string)$this->name->value(),
'notes' => (string)$this->notes->value(),
'processor' => (int)$this->processor->value(),
'external_id' => (string)$this->external_id->value(),
'created_at' => (string)$this->created_at->value(),
'updated_at' => (string)$this->updated_at->value(),
'closed_at' => (string)$this->closed_at->value(),
'orders' => $this->getOrders(),
'economic_invoice_draft_id' => null, // This will be overwritten if the external id is set
'economic_invoice_booked_id' => null, // This will be overwritten if the external id is set
'total_net_amount' => self::getTotalAmount(),
'user' => (array)(new users_o())->getUserByCustomerNumber($this->customer_number->value())->asArray(),
];
// If the external id is set, get the invoice draft id
if (!empty($this->external_id->value())) {
$tmp['economic_invoice_draft_id'] = self::isDraftExisting() ? self::getInvoiceDraftId() : null;
$tmp['economic_invoice_booked_id'] = self::isBooked() ? self::getInvoiceBookedId() : null;
}
return $tmp;
}
/**
* Get the orders in the invoice collection
* @param bool $count If the count of orders should be returned instead of the orders
* @return array|int The list of orders in the invoice collection, or the count of orders if $count is true
* @throws Exception If the request was not successful
*/
public function getOrders(bool $count = false): array|int
{
$orders = new orders_o();
$order_ids = $orders->getFieldsWhere(
[
'invoice_collection_id' => $this->id,
'deleted_at' => null
],
['id']
);
if ($count) {
return count($order_ids);
}
$result = [];
foreach ( $order_ids as $order_id ) {
$orders->select($order_id['id']);
$orders->requireSelected();
$result[] = $orders->asArray();
}
return $result;
}
/**
* Get the net amount (sum) of the invoice collection
* @return float The total amount of the invoice collection
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
*/
public function getTotalAmount(): float
{
// Require the invoice collection to be selected
self::requireSelected();
// Get the orders in the invoice collection
$order_ids = self::getOrderIds();
// Get the orders in the invoice collection
$net_amount = 0;
foreach ( $order_ids as $order_id ) {
$order = new orders_o();
$order->select($order_id['id']);
$order->requireSelected();
// Get the net amount of the order
$net_amount += $order->getNetAmount();
}
return $net_amount;
}
/**
* Get the order ids in the invoice collection
* @return array The list of order ids in the invoice collection
* @throws Exception If the request was not successful
*/
public function getOrderIds(): array
{
// Require the invoice collection to be selected
self::requireSelected();
// Get the orders in the invoice collection
$orders_o = new orders_o();
return $orders_o->getFieldsWhere(
[
'invoice_collection_id' => $this->id,
'deleted_at' => null
],
['id']
);
}
/**
* Check if the invoice draft is existing in E-conomic
* @returns bool If the invoice draft is existing
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
*/
public function isDraftExisting(): bool
{
// Require the invoice collection to be selected
self::requireSelected();
// If the external id is empty, the invoice draft does not exist
if ($this->external_id->value() === null) {
return false;
}
// Get the invoice draft id from the external id
try {
self::getInvoiceDraftId();
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Get the invoice draft id from the external id
* @return int The invoice draft id
* @throws Exception If the request was not successful
* @throws Exception If the invoice draft was not found
*/
public function getInvoiceDraftId(): int
{
// Require the invoice collection to be selected
self::requireSelected();
// Check if the external id is set
if ($this->external_id->value() === null) {
throw new Exception('Invoice draft does not exist');
}
// Create an economic object
$economic = new economic();
// Get the invoice draft id from the external id
$invoice_draft_id = $economic->invoices->draft->get_from_external_id($this->external_id->value());
return (int)$invoice_draft_id;
}
/**
* Check if the invoice is booked in E-conomic
* @returns bool If the invoice is booked
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
*/
public function isBooked(): bool
{
// Require the invoice collection to be selected
self::requireSelected();
// Get the invoice booked id from the external id
try {
self::getInvoiceBookedId();
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Get the invoice booked id from the external id
* @return int The invoice booked id
* @throws Exception If the request was not successful
* @throws Exception If the invoice booked was not found
*/
public function getInvoiceBookedId(): int
{
// Require the invoice collection to be selected
self::requireSelected();
// Check if the invoice_booked_id is already set (We don't want to make a request to E-conomic if we already have the id - Since this is slow.)
if (!empty($this->booked_invoice_id->value())) {
return (int)$this->booked_invoice_id->value();
}
// If the external id is empty, the invoice booked does not exist
if ($this->external_id->value() === null) {
throw new Exception('Invoice booked does not exist');
}
// Create an economic object
$economic = new economic();
// Get the invoice booked id from the external id
$invoice_booked_id = $economic->invoices->booked->get_from_external_id($this->external_id->value());
$this->booked_invoice_id->set($invoice_booked_id);
return (int)$invoice_booked_id;
}
/**
* Check if a customer has an open invoice collection
* @param int $customer_number The E-conomic customer number
* @return bool If the customer has an open invoice collection
*/
public function hasOpenInvoiceCollection(int $customer_number): bool
{
$collections = self::getFieldsWhere(
[
'customer_number' => $customer_number,
'closed_at' => null,
],
['id']
);
return !empty($collections);
}
/**
* Get the open invoice collections for a customer
* @param int $customer_number The E-conomic customer number
* @return array The open invoice collections
* @throws Exception If the request was not successful
*/
public function getOpenInvoiceCollections(int $customer_number): array
{
$collections = self::getFieldsWhere(
[
'customer_number' => $customer_number,
'closed_at' => null,
],
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at']
);
// Parse the results
$result = [];
foreach ( $collections as $collection ) {
$this->id = $collection['id'];
self::getObjectProperties();
self::requireSelected();
$result[] = self::asArray();
}
return $result;
}
/**
* @param int $customer_number The E-conomic customer number
* @return self The latest open invoice collection
* @throws Exception If no open invoice collections were found
* @throws Exception If the request was not successful
*/
public function getLatestOpenInvoiceCollection(int $customer_number): self
{
$collections = self::getFieldsWhere(
[
'customer_number' => $customer_number,
'closed_at' => null,
],
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at'],
);
if (empty($collections)) {
throw new Exception('No open invoice collections found');
}
// Parse the results
$this->id = $collections[0]['id'];
self::getObjectProperties();
self::requireSelected();
return $this;
}
/**
* Add the invoice collection to E-conomic
* @param bool $ignore_closed If the function should ignore when the invoice collection is closed
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection was not found
* @throws Exception If the customer number is not set
* @throws Exception If the invoice collection is already closed
*/
public function addToEconomic(bool $ignore_closed = false): self
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the customer number to be set
if (empty($this->customer_number->value())) {
throw new Exception('Customer number is not set');
}
if (!$ignore_closed) {
// Require the invoice collection to be open
self::requireOpen();
}
// Create an economic draft
self::createInvoiceDraft();
// Add the invoices to the invoice draft
self::addInvoicesToDraft();
// Close the invoice collection
// If the invoice collection is closed, we don't want to close it again
if ($this->closed_at->value() === null) {
self::closeCollection();
}
return $this;
}
/**
* Require the invoice collection to be open
* @throws Exception If the invoice collection is closed
* @throws Exception If the request was not successful
*/
public function requireOpen(): void
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice collection to not be closed
if (!empty($this->closed_at->value())) {
throw new Exception('Invoice collection is closed');
}
}
/**
* Create an invoice draft in E-conomic
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
* @throws Exception If the invoice collection is already closed
* @throws Exception If the invoice draft already exists
*/
public function createInvoiceDraft(): self
{
// Require the invoice collection to be selected
self::requireSelected();
// Check if the invoice draft already exists
if (self::isDraftExisting()) {
throw new Exception('Invoice draft already exists');
}
// Require the invoice draft to not already exist
self::requireInvoiceDraftDoesNotExist();
// Create the invoice draft
$economic = new economic();
$response = $economic->invoices->drafts->add(
$this->customer_number->value(),
self::getExternalId(),
);
// Validate the response, by checking if the external id is set
if (empty($response->references->other)) {
throw new Exception('Invoice collection was not created successfully');
}
// Set the processor
$this->processor->set(1);
// Object changed
self::objectChanged();
return $this;
}
/**
* Require the invoice draft to not already exist
* @throws Exception If the request was not successful
* @throws Exception If the invoice draft already exists
*/
public function requireInvoiceDraftDoesNotExist(): void
{
// Require the invoice collection to be selected
self::requireSelected();
// Try to get the invoice draft id from the external id
try {
self::getInvoiceDraftId();
throw new Exception('Invoice draft already exists');
} catch (Exception $e) {
// Ignore the exception, as it is expected.
}
}
/**
* Create a new collected order invoice
* @param int $customer_number The E-conomic customer number
* @param string|null $name The name of the invoice (optional)
* @param string|null $notes The notes for the invoice (optional)
* @param int|null $processor The processor id (optional)
* @return self The created object
* @throws Exception If the object was not created successfully
*/
public function add(int $customer_number, ?string $name = null, ?string $notes = null, ?int $processor = null): self
{
global /** @var db $db */
$db;
// Sanitize the input
$customer_number = $db->escape_string($customer_number);
if (!empty($name)) {
$name = $db->escape_string($name);
}
if (!empty($notes)) {
$notes = $db->escape_string($notes);
}
if (!empty($processor)) {
$processor = $db->escape_string($processor);
}
// Require the customer number to be of a valid customer
self::requireValidCustomer($customer_number);
// Add the object
$tmp_id = self::add_object([
'customer_number' => (int)$customer_number,
'name' => $name,
'notes' => $notes
]);
self::select((int)$tmp_id);
self::requireSelected();
// Set the processor
if (!empty($processor)) {
$this->processor->set($processor);
}
self::objectChanged();
return $this;
}
/**
* Require the E-conomic customer number to be a valid customer
* @throws Exception If the customer number is not a valid customer
*/
private static function requireValidCustomer(string $customer_number): void
{
$customers = new users_o();
$customers->select($customer_number);
$customers->requireSelected();
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Get the external id of the invoice collection
* @throws Exception If the request was not successful
* @throws Exception If the UUID could not be generated
* @throws Exception If the external id is not unique
*/
public function getExternalId(): string
{
if (empty($this->external_id->value())) {
$this->external_id->set(self::generateExternalId());
}
return $this->external_id->value();
}
/**
* Generate a unique external id (UUID)
* @return string The generated UUID
* @throws Exception If the UUID could not be generated
* @throws Exception If the request was not successful
*/
public static function generateExternalId(): string
{
$uuid = bin2hex(random_bytes(16));
return substr($uuid, 0, 8) . '-' . substr($uuid, 8, 4) . '-' . substr($uuid, 12, 4) . '-' . substr($uuid, 16, 4) . '-' . substr($uuid, 20);
}
/**
* Add the invoices to the invoice draft
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
* @throws Exception If the invoice collection is already closed
* @throws Exception If the invoice draft was not found
*/
public function addInvoicesToDraft(): self
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice collection to be open
self::requireInvoiceIsNotBooked();
// Get the orders in the invoice collection
$orders = self::getOrders();
// Check if there are any orders in the invoice collection
if (empty($orders)) {
throw new Exception('No orders in invoice collection');
}
// Require the invoice draft to be set (and exists)
self::requireInvoiceDraft();
// Add the invoices to the invoice draft
foreach ( $orders as $order ) {
self::addInvoiceToDraft($order['id']);
}
return $this;
}
/**
* Require the invoice collection to not be booked
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is already closed
*/
public function requireInvoiceIsNotBooked(): void
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice collection to not be booked
if (!empty($this->booked_invoice_id->value())) {
throw new Exception('Invoice collection is already booked');
}
}
/**
* Require the invoice draft to be set
* @throws Exception If the request was not successful
* @throws Exception If the invoice draft was not found
*/
public function requireInvoiceDraft(): void
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice draft to be set
if (empty($this->external_id->value())) {
throw new Exception('Invoice draft is not set');
}
// Require the invoice draft to exist
self::requireInvoiceDraftExists();
}
/**
* Require the invoice draft to exist (in E-conomic)
* @throws Exception If the request was not successful
* @throws Exception If the invoice draft was not found
*/
public function requireInvoiceDraftExists(): void
{
// Require the invoice collection to be selected
self::requireSelected();
// Get the invoice draft id from the external id
$invoice_draft_id = self::getInvoiceDraftId();
// Check if the invoice draft id is set
if (empty($invoice_draft_id)) {
throw new Exception('Invoice draft not found');
}
}
/**
* Add an invoice to the invoice draft
* @param int $order_id The order id to add
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
* @throws Exception If the invoice collection is already closed
* @throws Exception If the invoice draft was not found
*/
public function addInvoiceToDraft(int $order_id): self
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice collection to be open
self::requireInvoiceIsNotBooked();
// Get the order object
$order = new orders_o();
$order->select($order_id);
$order->requireSelected();
// Require the invoice draft to be set (and exists)
self::requireInvoiceDraft();
// Add the invoice to the invoice draft
$economic = new economic();
$economic->invoices->draft->add_order(
self::getInvoiceDraftId(),
$order
);
return $this;
}
/**
* Close the invoice collection
* @throws Exception If the request was not successful
* @throws Exception If the invoice collection is not set
* @throws Exception If the invoice collection is already closed
*/
public function closeCollection(): self
{
// Require the invoice collection to be selected
self::requireSelected();
// Require the invoice collection to be open
self::requireOpen();
// Close the invoice collection
$this->closed_at->set(date('Y-m-d H:i:s'));
self::objectChanged();
return $this;
}
public function listCustomersWithIndividualOrderInvoicing(): array
{
// Get all the customers with the "invoiceAllOrdersIndividually" attribute
$users = new users_o();
return $users->getCustomerNumbersWithAttributes([
'invoiceAllOrdersIndividually'
]);
}
}
@@ -0,0 +1,465 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class department_daily_reports_o extends db
{
use db_object_t;
/**
* The id of the department
* @var object_property $department_id
*/
public object_property $department_id;
/**
* The cm3 of water used in the department today
* @var object_property $water_usage
*/
public object_property $water_usage;
/**
* The cm3 of water used in the department today
* @var object_property $water_usage_morning
*/
public object_property $water_usage_morning;
/**
* Any notes for the report
* @var object_property $notes
*/
public object_property $notes;
/**
* The user (id) that filled the report
* @var object_property $filled_by
*/
public object_property $filled_by;
/**
* The timestamp of when the object was created
* @var object_property $created_at
*/
public object_property $created_at;
public function structure(): void
{
$this->setTable('department_daily_reports');
}
public function asArray(): array
{
return [
'id' => (int)$this->id,
'department_id' => (int)$this->department_id->value(),
'water_usage' => (int)$this->water_usage->value(),
'water_usage_morning' => (int)$this->water_usage_morning->value(),
'notes' => (string)$this->notes->value(),
'filled_by' => (int)$this->filled_by->value(),
'created_at' => (string)$this->created_at->value(),
];
}
/**
* Add a department daily report
* @param int $department_id The id of the department
* @param int $water_usage The cm3 of water used in the department today
* @param int $water_usage_morning The cm3 of water used in the department (Checked in the morning)
* @param string $notes Any notes for the report
* @param int $filled_by The user (id) that filled the report
* @return department_daily_reports_o
* @throws Exception If the object was not created successfully
*/
public function add(
int $department_id,
int $water_usage,
int $water_usage_morning,
string $notes,
int $filled_by,
string $created_at = null
): department_daily_reports_o
{
// Validate the created_at date, if provided
if ($created_at !== null) {
$dateTime = \DateTime::createFromFormat('Y-m-d', $created_at);
if (!$dateTime || $dateTime->format('Y-m-d') !== $created_at) {
throw new Exception('Invalid date format for created_at. Expected format: YYYY-MM-DD');
}
}
$tmp_id = $this->add_object([
'department_id' => (int)$department_id,
'water_usage' => (int)$water_usage,
'water_usage_morning' => (int)$water_usage_morning,
'notes' => (string)$notes,
'filled_by' => (int)$filled_by,
'created_at' => $created_at ? $created_at : date('Y-m-d H:i:s'),
]);
$this->id = $tmp_id;
$this->getObjectProperties();
$this->objectChanged();
return $this;
}
public function getObjectProperties(): void
{
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', false);
$this->water_usage = new object_property($this->table, $this->id, 'water_usage', 'int', false);
$this->water_usage_morning = new object_property($this->table, $this->id, 'water_usage_morning', 'int', false);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
$this->filled_by = new object_property($this->table, $this->id, 'filled_by', 'int', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Get the amount of products sold on a given date
* @note This does not count products from removed orders, only products from orders that are still active
* @param string $date The date to get the report for (YYYY-MM-DD)
* @param int $department_id The id of the department
* @param int $product_id The id of the product
* @return int The amount of products sold on the given date (Not counting products from removed orders)
*/
public function getProductsSoldOnDate(string $date, int $department_id, int $product_id): int
{
global /** @var db $db */
$db;
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT SUM(oi.quantity) as amount FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.department_id = ? AND oi.product_id = ? AND DATE(o.created_at) = ? AND o.deleted_at IS NULL'
);
if ($stmt) {
$stmt->bind_param('iis', $department_id, $product_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$amount = $data['amount'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount;
}
/**
* Get the amount of products sold, where the product would be applicable as an addon
* E.g. if the product is "Cheese" this would return the amount of "Pizza" sold
* @note This does not count products from removed orders, only products from orders that are still active
* @param string $date The date to get the report for (YYYY-MM-DD)
* @param int $department_id The id of the department
* @param int $product_id The id of the product (the addon)
* @return int The amount of products sold on the given date (Not counting products from removed orders)
*/
public function getProductsSoldWithAddonApplicable(string $date, int $department_id, int $product_id): int
{
global /** @var db $db */
$db;
// Get all the addons for the product
$addons = (new product_options_o())->getOptionProducts($product_id);
$addons = array_map(function ($addon) {
return $addon['product_id'];
}, $addons);
$addons = implode(',', $addons);
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT SUM(oi.quantity) as amount FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.department_id = ? AND oi.product_id IN (' . $addons . ') AND DATE(o.created_at) = ? AND o.deleted_at IS NULL'
);
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$amount = $data['amount'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount;
}
/**
* Get the amount of products sold on a given date
* @note This does not count products from removed orders, only products from orders that are still active
* @param string $date The date to get the report for (YYYY-MM-DD)
* @param int $department_id The id of the department
* @return int The amount of products sold on the given date (Not counting products from removed orders)
*/
public function getTransactionProductsOnDateCount(string $date, int $department_id): int
{
global /** @var db $db */
$db;
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT SUM(oi.quantity) as amount FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.department_id = ? AND DATE(o.created_at) = ? AND o.deleted_at IS NULL'
);
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$amount = $data['amount'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount;
}
/**
* Get the amount of transactions on a given date
* @note This does not count products from removed orders, only products from orders that are still active
* @param string $date The date to get the report for (YYYY-MM-DD)
* @param int $department_id The id of the department
* @return int The amount of transactions on the given date (Not counting products from removed orders)
*/
public function getTransactionsOnDateCount(string $date, int $department_id): int
{
global /** @var db $db */
$db;
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT COUNT(*) as amount FROM orders o
WHERE o.department_id = ? AND DATE(o.created_at) = ? AND o.deleted_at IS NULL'
);
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$amount = $data['amount'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount;
}
/**
* Select the department daily report for today
* @param int $department_id The id of the department
* @return department_daily_reports_o The department daily report
* @throws Exception If a department daily report does not exist for today
* @throws Exception If the object was not selected
*/
public function selectDepartmentDailyReport(int $department_id): department_daily_reports_o
{
global $db;
if (!$this->doesDepartmentDailyReportExist($department_id)) {
throw new Exception('Department daily report does not exist');
}
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT id FROM department_daily_reports o
WHERE o.department_id = ? AND DATE(o.created_at) = CURDATE()'
);
// Check if the statement was prepared successfully
if ($stmt) {
$stmt->bind_param('i', $department_id); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$id = $data['id'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
$this->select($id);
return $this;
}
/**
* Check if a department daily report exists for today
* @param int $department_id The id of the department
* @param string $date The date to check for (YYYY-MM-DD)
* @return bool True if the report exists, false otherwise
*/
public function doesDepartmentDailyReportExist(int $department_id, string $date = 'today'): bool
{
global $db;
if ($date === 'today') {
$date = date('Y-m-d');
}
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT COUNT(*) as amount FROM department_daily_reports o
WHERE o.department_id = ? AND DATE(o.created_at) = ?'
);
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
$amount = $data['amount'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount > 0;
}
/**
* Select the last department daily report for a department
* @param int $department_id The id of the department
* @return department_daily_reports_o The department daily report
* @throws Exception If the object was not selected
*/
public function selectLastDepartmentDailyReport(int $department_id): department_daily_reports_o
{
global $db;
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT id FROM department_daily_reports o
WHERE o.department_id = ? ORDER BY o.created_at DESC LIMIT 1'
);
// Check if the statement was prepared successfully
if ($stmt) {
$stmt->bind_param('i', $department_id); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "id" field
if (!$data) {
throw new Exception('Department daily report does not exist');
}
$id = $data['id'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
$this->select($id);
return $this;
}
public function getTransactionsOnDateEarnings(string $date, int $department_id): int
{
global /** @var db $db */
$db;
$conn = $db->conn();
// Get all the product prices, in all the orders (Not counting removed orders), for the given date and department, and sum them
$stmt = $conn->prepare(
'SELECT SUM(oi.price * oi.quantity) as amount FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.department_id = ? AND DATE(o.created_at) = ? AND o.deleted_at IS NULL'
);
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
if (!$data) {
// If there are no orders, set the amount to 0
$amount = 0;
} else {
$amount = $data['amount'];
}
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
return (int)$amount;
}
/**
* Check if a department daily report exists for today
* @param int $department_id The id of the department
* @return bool True if the report exists, false otherwise
*/
public function hasReportForToday(int $department_id): bool
{
return $this->doesDepartmentDailyReportExist($department_id);
}
/**
* Select the department daily report for a given date
* @param int $department_id The id of the department
* @param string $date The date to get the report for (YYYY-MM-DD)
* @return department_daily_reports_o The department daily report
* @throws Exception If a department daily report does not exist for today
* @throws Exception If the object was not selected
*/
public function selectDepartmentReportByDate(int $department_id, string $date): department_daily_reports_o
{
global $db;
if (!$this->doesDepartmentDailyReportExist($department_id, $date)) {
throw new Exception('Department daily report does not exist');
}
$conn = $db->conn();
$stmt = $conn->prepare(
'SELECT id FROM department_daily_reports o
WHERE o.department_id = ? AND DATE(o.created_at) = ?'
);
// Check if the statement was prepared successfully
if ($stmt) {
$stmt->bind_param('is', $department_id, $date); // Bind parameters (i = integer, s = string)
$stmt->execute();
$result = $stmt->get_result(); // Get the result set from the statement
$data = $result->fetch_assoc(); // Fetch the result as an associative array
// Access the "amount" field
if (!$data) {
throw new Exception('Department daily report does not exist');
}
$id = $data['id'];
$stmt->close(); // Close the statement
} else {
// Handle query preparation error
die('Query preparation failed: ' . $conn->error);
}
$this->select($id);
return $this;
}
public function hasReport(int $department_id, string $date): bool
{
return $this->doesDepartmentDailyReportExist($department_id, $date);
}
}
@@ -0,0 +1,194 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class department_variables_o extends db
{
use db_object_t;
public int $department_id;
public object_property $variable;
public object_property $value;
public object_property $created_at;
public function structure(): void
{
$this->setTable('department_variables');
}
/**
* Set a department variable
* @param string $variable The variable name
* @param string $value The variable value
* @return void
*
* @throws Exception If the object was not created successfully
*/
public function set(string $variable, mixed $value): void
{
self::requireDepartmentId();
// Check if the variable already exists
$isAlreadyDefinedId = self::getFieldsWhere([
'department_id' => $this->department_id,
'variable' => $variable
], ['id']);
// Update the variable if it already exists
if ($isAlreadyDefinedId) {
$this->id = $isAlreadyDefinedId[0]['id'];
$this->getObjectProperties();
$this->value->set($value);
return;
}
// Add the variable
$tmp_id = self::add_object([
'department_id' => $this->department_id,
'variable' => $variable,
'value' => $value
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
if (!$this->id) {
throw new Exception('The variable was not created successfully.');
}
}
/**
* Require the department ID to be set, and throw an exception if it is not
* @throws Exception If the department ID is not set.
*/
public function requireDepartmentId(): void
{
if (!$this->department_id) {
throw new Exception('The department ID is not set.');
}
}
/**
* Get the department variable by ID
* @throws Exception If the department ID is not set
*/
public function getObjectProperties(): void
{
self::requireDepartmentId();
$this->variable = new object_property($this->table, $this->id, 'variable', 'string', false);
$this->value = new object_property($this->table, $this->id, 'value', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* List all department variables
* @throws Exception If the department ID is not set
*/
public function list(): array
{
self::requireDepartmentId();
$variables = self::getFieldsWhere([
'department_id' => $this->department_id
], ['id']);
$result = [];
foreach ( $variables as $variable ) {
$tmp = new department_variables_o();
$tmp->select($variable['id']);
$result[] = $tmp->asArray();
}
return $result;
}
/**
* Get the department variable by name
* @throws Exception If the object was not selected
*/
public function asArray(): array
{
self::requireSelected();
return [
'id' => (int)$this->id,
'department_id' => (int)$this->department_id,
'variable' => (string)$this->variable->value(),
'value' => (string)$this->value->value(),
'created_at' => (string)$this->created_at->value(),
];
}
/**
* Get a department variable
* @param string $variable The variable name
* @return null If the variable is not set
* @return mixed The variable value
* @throws Exception If the department ID is not set
*/
public function getVariable(string $variable): mixed
{
self::requireDepartmentId();
$variable = self::getFieldsWhere([
'department_id' => $this->department_id,
'variable' => $variable
], ['value']);
if (!$variable) {
return null;
}
return $variable[0]['value'];
}
/**
* Check if a department variable is set
* @param string $variable The variable name
* @return bool If the variable is set
* @throws Exception If the department ID is not set
*/
public function isSet(string $variable): bool
{
self::requireDepartmentId();
$variable = self::getFieldsWhere([
'department_id' => $this->department_id,
'variable' => $variable
], ['id']);
return (bool)count($variable);
}
/**
* Nullify a department variable
* @param string $variable The variable name
* @return void If the variable was nullified
* @throws Exception If the object was not selected
* @throws Exception If the department ID is not set
*/
public function nullify(string $variable): void
{
self::requireDepartmentId();
$variable = self::getFieldsWhere([
'department_id' => $this->department_id,
'variable' => $variable
], ['id']);
if ($variable) {
$tmp = new department_variables_o();
$tmp->select($variable[0]['id']);
$tmp->requireSelected();
$tmp->delete();
}
}
/**
* Select a department
* @param int $id The department ID
* @return self
*/
public function selectDepartment(int $id): self
{
$this->department_id = $id;
return $this;
}
}
+104 -1
View File
@@ -4,6 +4,8 @@ namespace objects;
use classes\db;
use classes\object_property;
use classes\stripe;
use Exception;
use traits\db_object_t;
class departments_o extends db
@@ -14,6 +16,10 @@ class departments_o extends db
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 department_variables_o $variables; // The department variables object
public object_property $dimension; // The dimension of the department
public object_property $created_at;
public object_property $updated_at;
public function structure(): void
{
@@ -26,6 +32,12 @@ class departments_o extends db
redis->clear_departments();
}
/**
* Add a department
* @param string $name
* @param string $description
* @throws Exception If the object was not created successfully
*/
public function create(string $name, string $description): void
{
global $db;
@@ -46,12 +58,22 @@ class departments_o extends db
redis->clear_departments();
}
/**
* Get the object properties of the department
* @throws Exception If the object is not selected
*/
public function getObjectProperties(): void
{
self::requireSelected();
$this->name = new object_property($this->table, $this->id, 'name', 'string', true);
$this->description = new object_property($this->table, $this->id, 'description', 'string', false);
$this->economic_department_id = new object_property($this->table, $this->id, 'economic_department_id', 'int', false);
$this->slack_webhook = new object_property($this->table, $this->id, 'slack_webhook', 'string', false);
$this->variables = (new department_variables_o())->selectDepartment($this->id);
$this->dimension = new object_property($this->table, $this->id, 'dimension', 'int', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
}
public function edit(int $id, string $name, string $description, int $economic_department_id): void
@@ -193,11 +215,92 @@ class departments_o extends db
if (!empty($name)) {
redis->cache_department_name($department, $name);
}
} catch (\Exception $e) {
} catch (Exception $e) {
$name = 'Unable to get department name: ' . $department;
}
return $name;
}
return redis->get_department_name($department) ?? 'Unable to get department name: ' . $department;
}
/**
* Get the Stripe terminal location department variable
* @note This is a link to the department_variables_o::getVariable(stripe_terminal_location) method
* @return string The Stripe terminal location id
* @throws Exception If the department variable is not set
* @throws Exception If the department is not selected
*/
public function getStripeTerminalLocation(): string
{
self::requireSelected();
if (!$this->variables->isSet('stripe_terminal_location')) {
throw new Exception('The Stripe terminal location is not set for the department.');
}
return $this->variables->getVariable('stripe_terminal_location');
}
/**
* Check if the Stripe terminal location variable is set
* @note This is a link to the department_variables_o::isSet(stripe_terminal_location) method
* @return bool true If the Stripe terminal location is set
* @throws Exception If the department is not selected
*/
public function isStripeTerminalLocationSet(): bool
{
self::requireSelected();
return $this->variables->isSet('stripe_terminal_location');
}
/**
* Set the Stripe terminal location department variable
* @note This is a link to the department_variables_o::set(stripe_terminal_location) method
* @param string $location The Stripe terminal location id
* @throws Exception If the department is not selected
* @throws Exception If the department variable is not updated successfully
*/
public function setStripeTerminalLocation(string $location): void
{
self::requireSelected();
$this->variables->set('stripe_terminal_location', $location);
}
/**
* Get the Stripe terminal readers
* @note This is a link to the stripe_endpoint_readers::listLocation method
* @throws Exception If the department is not selected
* @throws Exception If the Stripe terminal location is not set for the department
*/
public function getStripeTerminalReaders(): \Stripe\Collection
{
self::requireSelected();
if (!$this->variables->isSet('stripe_terminal_location')) {
throw new Exception('The Stripe terminal location is not set for the department.');
}
return (new stripe())->readers->listLocation($this->variables->getVariable('stripe_terminal_location'));
}
/**
* Convert the object to an array
* @param array|null $options The options for the conversion (e.g. ['slack_webhook' => true])
* @return array The object as an array
* @throws Exception If the object is not selected
*/
public function asArray(array $options = null): array
{
self::requireSelected();
$tmp = [
'id' => (int)$this->id,
'name' => (string)$this->name->value(),
'description' => (string)$this->description->value(),
'economic_department_id' => (int)$this->economic_department_id->value(),
'dimension' => (int)$this->dimension->value(),
'created_at' => (string)$this->created_at->value(),
'updated_at' => (string)$this->updated_at->value(),
];
// Check if the webhook should be included
if ($options && $options['slack_webhook']) {
$tmp['slack_webhook'] = (string)$this->slack_webhook->value();
}
return $tmp;
}
}
@@ -0,0 +1,87 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class form_submissions_o extends db
{
use db_object_t;
public object_property $form_identifier;
public object_property $data;
public object_property $user_id;
public object_property $department_id;
public object_property $created_at;
public function structure(): void
{
$this->setTable('form_submissions');
}
/**
* Add a form submission object, and set this object to the new object
* @param string $form_identifier The form identifier
* @param array $data The form data
* @param int|null $user_id The user id (If applicable)
* @param int|null $department_id The department id (If applicable)
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(string $form_identifier, array $data, ?int $user_id = null, ?int $department_id = null): void
{
global /** @var db $db */
$db;
// Sanitize the input
$form_identifier = $db->escape_string($form_identifier);
$encoded_data = $db->escape_string(json_encode($data));
// Add the object
$tmp_arr = [
'form_identifier' => $form_identifier,
'data' => $encoded_data,
];
// Add the user id and department id if they are set
if (!empty($user_id)) {
$tmp_arr['user_id'] = (int)$user_id;
}
if (!empty($department_id)) {
$tmp_arr['department_id'] = (int)$department_id;
}
$tmp_id = self::add_object($tmp_arr);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
}
public function getObjectProperties(): void
{
$this->form_identifier = new object_property($this->table, $this->id, 'form_identifier', 'string', false);
$this->data = new object_property($this->table, $this->id, 'data', 'string', false);
$this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', false);
$this->department_id = new object_property($this->table, $this->id, 'department_id', 'int', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
public function asArray(): array
{
return [
'id' => (int)$this->id,
'form_identifier' => (string)$this->form_identifier->value(),
'data' => (string)$this->data->value(),
'user_id' => (int)$this->user_id->value(),
'department_id' => (int)$this->department_id->value(),
'created_at' => (string)$this->created_at->value(),
];
}
}
+147
View File
@@ -0,0 +1,147 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class groups_o extends db
{
use db_object_t;
public object_property $name;
public object_property $description;
public object_property $created_at;
public function structure(): void
{
self::setTable('groups');
}
/**
* Get the group by name
* @throws Exception If an object is not selected
*/
public function asArray(): array
{
self::requireSelected();
return [
'id' => (int)$this->id,
'name' => (string)$this->name->value(),
'description' => (string)$this->description->value(),
'created_at' => (string)$this->created_at->value(),
'permissions' => $this->getPermissions()
];
}
/**
* Get all permissions for a group
* @return array
* @throws Exception If the group was not selected
*/
public function getPermissions(): array
{
self::requireSelected();
return (new groups_permissions_o())->getGroupPermissions($this->id);
}
/**
* Get department ids, that the group has permission (department_access_:group_id) to access
* @return array An array of department ids e.g. [1, 2, 3]
* @throws Exception If the group was not selected
*/
public function getDepartments(): array
{
self::requireSelected();
$permissions = (new groups_permissions_o())->getGroupPermissionsMatching($this->id, '/^department_access_[0-9]+$/');
$departments = [];
foreach ( $permissions as $value ) {
$tmp = explode('_', $value['permission']);
$departments[] = (int)$tmp[2];
}
return $departments;
}
/**
* Remove a permission from a group
*
* @param string $permission_id
* @return self $this
* @throws Exception If the permission was not removed successfully
*/
public function removePermission(string $permission_id): self
{
self::requireSelected();
(new groups_permissions_o())->remove($this->id, $permission_id);
return $this;
}
/**
* Clone a group with all its permissions
* @param string $name
* @param string $description
* @return void
* @throws Exception
*/
public function clone(string $name, string $description): void
{
self::requireSelected();
$group = new groups_o();
$group->add($name, $description);
$permissions = (new groups_permissions_o())->getGroupPermissions($this->id);
foreach ( $permissions as $permission ) {
$group->addPermission($permission);
}
}
/**
* Add a group
* @param string $name
* @param string $description
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(string $name, string $description): void
{
$tmp_id = self::add_object([
'name' => $name,
'description' => $description
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
if (!$this->id) {
throw new Exception('The group was not created successfully.');
}
}
public function getObjectProperties(): void
{
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
$this->description = new object_property($this->table, $this->id, 'description', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
self::objectChanged();
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Add a permission to a group
*
* @param string $permission_id
* @return self $this
* @throws Exception If the permission was not added successfully
*/
public function addPermission(string $permission_id): self
{
self::requireSelected();
(new groups_permissions_o())->add($this->id, $permission_id);
return $this;
}
}
@@ -0,0 +1,146 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class groups_permissions_o extends db
{
use db_object_t;
public object_property $group_id;
public object_property $permission;
public function structure(): void
{
$this->setTable('groups_permissions');
}
/**
* Add a permission to a group
* @param int $group_id
* @param string $permission
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(int $group_id, string $permission): void
{
// Check if the group exists
$group = new groups_o();
$group->select($group_id);
$group->requireSelected();
// Check if the permission already exists for the group
$isAlreadyDefined = self::getFieldsWhere([
'group_id' => $group_id,
'permission' => $permission
], ['id']);
// Throw an exception if the permission already exists for the group
if ($isAlreadyDefined) {
throw new Exception('The permission already exists for the group.');
}
// Add the permission
$tmp_id = self::add_object([
'group_id' => $group_id,
'permission' => $permission
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
if (!$this->id) {
throw new Exception('The permission was not created successfully.');
}
}
public function getObjectProperties(): void
{
$this->group_id = new object_property($this->table, $this->id, 'group_id', 'int', false);
$this->permission = new object_property($this->table, $this->id, 'permission', 'string', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Remove a permission from a group
* @param int $group_id
* @param string $permission
* @return void
* @throws Exception If the permission does not exist for the group
* @throws Exception If the object was not deleted successfully
* @throws Exception If the group does not exist
*/
public function remove(int $group_id, string $permission): void
{
// Check if the group exists
$group = new groups_o();
$group->select($group_id);
$group->requireSelected();
// Check if the permission exists for the group
$isDefined = self::getFieldsWhere([
'group_id' => $group_id,
'permission' => $permission
], ['id']);
// Throw an exception if the permission does not exist for the group
if (!$isDefined) {
throw new Exception('The permission does not exist for the group.');
}
// Get the id of the permission entry in the database
$id = $isDefined[0]['id'];
// Remove the permission
$tmp_id = new groups_permissions_o();
$tmp_id->select((int)$id);
$tmp_id->requireSelected();
$tmp_id->delete();
}
public function asArray(): array
{
return [
'id' => (int)$this->id,
'group_id' => (int)$this->group_id->value(),
'permission' => (string)$this->permission->value()
];
}
/**
* Get all permissions for a group
* @param int $group_id
* @return array
*/
public function getGroupPermissions(int $group_id): array
{
$raw_permissions = self::getFieldsWhere([
'group_id' => $group_id
], ['permission']);
$permissions = [];
foreach ( $raw_permissions as $permission ) {
$permissions[] = $permission['permission'];
}
return $permissions;
}
/**
* Get all permissions for a group matching a regex
* @throws Exception If the group does not exist
*/
public function getGroupPermissionsMatching(int $id, string $regex): array
{
// Check if the group exists
$group = new groups_o();
$group->select($id);
$group->requireSelected();
// Get the permissions
$permissions = self::getFieldsWhere([
'group_id' => $id
], ['permission']);
// Filter the permissions
return array_filter($permissions, function ($permission) use ($regex) {
return preg_match($regex, $permission['permission']);
});
}
}
@@ -0,0 +1,119 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class module_action_logs_o extends db
{
use db_object_t;
/**
* The module name
* @var object_property $module The module name
*/
public object_property $module;
/**
* The action name
* @var object_property $action The action name
*/
public object_property $action;
/**
* The action status code (HTTP status code, e.g. 200, 404, 500)
* @var object_property $status_code The action status code (HTTP status code, e.g. 200, 404, 500)
*/
public object_property $status_code;
/**
* The action data
* @var object_property $data The data JSON encoded
*/
public object_property $data;
/**
* The action created at
* @var object_property $created_at The action created at
*/
public object_property $created_at;
public function structure(): void
{
$this->setTable('module_usage_logs');
}
/**
* Add a module action log
* @param string $module The module name
* @param string $action The action name
* @param int $status_code The action status code (HTTP status code, e.g. 200, 404, 500)
* @param array $data The action data
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(string $module, string $action, int $status_code, array $data): void
{
global /** @var db $db */
$db;
// Sanitize the input
$module = $db->escape_string($module);
$action = $db->escape_string($action);
$status_code = (int)$status_code;
// JSON encode the data
$encoded_data = $db->escape_string(json_encode($data));
// Add the object
$tmp_id = self::add_object([
'module' => $module,
'action' => $action,
'status_code' => $status_code,
'data' => $encoded_data,
]);
if (!$tmp_id) {
throw new Exception('The object was not created successfully.');
}
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
}
public function getObjectProperties(): void
{
$this->module = new object_property($this->table, $this->id, 'module', 'string', false);
$this->action = new object_property($this->table, $this->id, 'action', 'string', false);
$this->status_code = new object_property($this->table, $this->id, 'status_code', 'int', false);
$this->data = new object_property($this->table, $this->id, 'data', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'datetime', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Get the object as an array
* @return array The object as an array
*/
public function asArray(): array
{
return [
'id' => (int)$this->id,
'module' => (string)$this->module->value(),
'action' => (string)$this->action->value(),
'status_code' => (int)$this->status_code->value(),
'data' => self::decodeData((string)$this->data->value()),
'created_at' => (string)$this->created_at->value(),
];
}
/**
* Decode the data
* @param string $data The data JSON encoded
* @return array The data
*/
public static function decodeData(string $data): array
{
return json_decode($data, true);
}
}
@@ -32,6 +32,13 @@ class motorapi_lookups_o extends db
*/
public function add(string $license_plate, string $result, string $endpoint): void
{
global /** @var db $db */
$db;
// Sanitize the input
$license_plate = $db->escape_string($license_plate);
$result = $db->escape_string($result);
$endpoint = $db->escape_string($endpoint);
// Add the object
$tmp_id = self::add_object([
'license_plate' => $license_plate,
'result' => $result,
@@ -79,4 +86,58 @@ class motorapi_lookups_o extends db
];
}
/**
* Is the license plate in the cache?
* @param string $license_plate
* @return bool
* @throws Exception If the license plate is not valid
*/
public function isCached(string $license_plate): bool
{
global /** @var db $db */
$db;
if (!self::validateLicensePlateFormat($license_plate)) {
throw new Exception('The license plate is not valid.');
}
$sql = 'SELECT COUNT(*) FROM ' . $this->table . ' WHERE license_plate = "' . $db->escape_string($license_plate) . '"';
$result = $db->query($sql);
return (bool)$result->fetch_row()[0];
}
/**
* Validate the format of a license plate
* @note This simply checks if the license plate is in the format of two letters followed by four or five numbers.
* @note This does not check if the license plate is actually valid.
* @param string $license_plate
* @return bool
*/
public static function validateLicensePlateFormat(string $license_plate): bool
{
return (bool)preg_match('/^[A-Z]{2}[0-9]{4,5}$/', $license_plate);
}
/**
* Get the (latest) cached result for a license plate
* @param string $license_plate
* @return object
* @throws Exception If the license plate is not valid or the response is not cached
*/
public function getCachedResult(string $license_plate): object
{
global /** @var db $db */
$db;
if (!self::validateLicensePlateFormat($license_plate)) {
throw new Exception('The license plate is not valid.');
}
$result = self::getFieldsWhere(['license_plate' => $license_plate], ['id', 'result', 'endpoint', 'created_at']);
if (!$result) {
throw new Exception('The response is not cached.');
}
// Get the latest result
$latest = array_pop($result);
$this->id = $latest['id'];
self::getObjectProperties();
return $this;
}
}
@@ -0,0 +1,113 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class notifications_o extends db
{
use db_object_t;
/**
* The notification type
* @var object_property $type The notification type
*/
public object_property $type;
/**
* The user id
* @var object_property $user_id The user id
*/
public object_property $user_id;
/**
* The notification data
* @var object_property $data The notification data (JSON encoded)
*/
public object_property $data;
/**
* The notification created at
* @var object_property $created_at The notification created at
*/
public object_property $created_at;
public function structure(): void
{
$this->setTable('notifications');
}
/**
* Add a notification object, and set this object to the new object
* @param string $type The notification type id
* @param int $user_id The user id
* @param array $data The notification data
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(string $type, int $user_id, array $data): void
{
global /** @var db $db */
$db;
// Sanitize the input
$type = $db->escape_string($type);
$user_id = (int)$user_id;
// JSON encode the data
$encoded_data = $db->escape_string(json_encode($data));
// Add the object
$tmp_id = self::add_object([
'type' => $type,
'user_id' => $user_id,
'data' => $encoded_data,
]);
if (!$tmp_id) {
throw new Exception('The object was not created successfully.');
}
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
}
/**
* Get the object properties
* @return void
*/
public function getObjectProperties(): void
{
$this->type = new object_property($this->table, $this->id, 'type', 'string', false);
$this->user_id = new object_property($this->table, $this->id, 'user_id', 'int', false);
$this->data = new object_property($this->table, $this->id, 'data', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'datetime', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* Get the object as an array
* @return array The object as an array
*/
public function asArray(): array
{
return [
'id' => (int)$this->id,
'type' => (string)$this->type->value(),
'user_id' => (int)$this->user_id->value(),
'data' => self::decodeData((string)$this->data->value()),
'created_at' => (string)$this->created_at->value(),
];
}
/**
* Decode the data
* @param string $data The data JSON encoded
* @return array The data
*/
public static function decodeData(string $data): array
{
return json_decode($data, true);
}
}
+18 -2
View File
@@ -45,6 +45,11 @@ class order_items_o extends db
* @var object_property
*/
public object_property $quantity;
/**
* The id of the related item, if it exists
* @var object_property
*/
public object_property $related_item_id;
public function structure(): void
{
@@ -78,6 +83,7 @@ class order_items_o extends db
$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);
$this->related_item_id = new object_property($this->table, $this->id, 'related_item_id', 'int', false);
}
public function add(int $order_id, int $product_id, string $reference, string $notes, int $cashier_id, int $price, int $quantity): void
@@ -120,7 +126,7 @@ class order_items_o extends db
}
}
public function addItemToOrder(int $order_id, int $product_id, int $cashier_id, int $quantity): void
public function addItemToOrder(int $order_id, int $product_id, int $cashier_id, int $quantity, $related_item_id = null, $notes = null): void
{
global $db, $response;
try {
@@ -143,6 +149,15 @@ class order_items_o extends db
$this->id = $db->insert_id();
// Set the values of the object properties
$this->getObjectProperties();
// Set the related item id, if it is set
if ($related_item_id) {
$this->related_item_id->set($related_item_id);
}
// Set the notes, if it is set
if ($notes) {
$this->notes->set($notes);
}
} catch (\Exception $e) {
$response->error($e->getMessage());
}
@@ -154,7 +169,7 @@ class order_items_o extends db
// TODO: Implement delete() method instead
global $db;
$this->id = $id;
$sql = "DELETE FROM $this->table WHERE id = $this->id";
$sql = "DELETE FROM $this->table WHERE id = $this->id or related_item_id = $this->id";
$db->query($sql);
}
@@ -169,6 +184,7 @@ class order_items_o extends db
'cashier_id' => (int)$this->cashier_id->value(),
'price' => (int)$this->price->value(),
'quantity' => (int)$this->quantity->value(),
'related_item_id' => (int)$this->related_item_id->value(),
'product' => (array)(new products_o())->getProductById($this->product_id->value())->asArray(),
'cashier' => (array)(new users_o())->getUserById($this->cashier_id->value())->asArray()
];
+146 -22
View File
@@ -3,8 +3,10 @@
namespace objects;
use classes\db;
use classes\motorapi;
use classes\object_property;
use classes\response;
use Exception;
use traits\db_object_t;
class orders_o extends db
@@ -23,6 +25,7 @@ class orders_o extends db
public object_property $created_at;
public object_property $deleted_at;
public stripe_module_orders_o $stripe_module_orders;
public object_property $invoice_collection_id;
public function structure(): void
{
@@ -43,7 +46,7 @@ class orders_o extends db
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
@@ -62,8 +65,10 @@ class orders_o extends db
$this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id);
$this->stripe_module_orders = (new stripe_module_orders_o())->select($this->id);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
$this->invoice_collection_id = new object_property($this->table, $this->id, 'invoice_collection_id', 'int', false);
}
public function includeIncludes(): orders_o
{
global /** @var response $response */
@@ -137,19 +142,40 @@ class orders_o extends db
{
return [
'id' => $this->id,
'customer_id' => $this->customer_id->value(),
'cashier_id' => $this->cashier_id->value(),
'customer_id' => (int)$this->customer_id->value(),
'cashier_id' => (int)$this->cashier_id->value(),
'reference' => $this->reference->value(),
'notes' => $this->notes->value(),
'department_id' => $this->department_id->value(),
'department_id' => (int)$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(),
'total_net_amount' => $this->getNetAmount(),
'invoice_collection_id' => (int)$this->invoice_collection_id->value(),
'closed_at' => (int)$this->invoice_collection_id->value() ? (new collected_order_invoices_o())->select((int)$this->invoice_collection_id->value())->closed_at->value() : null,
];
}
public function getNetAmount(): float
{
self::requireSelected();
$net = 0;
// Get the order items object
$order_items = new order_items_o();
// Get the price, quantity of the order items
$items = $order_items->getAllItemsAsArray($this->id);
// Loop through the items and get the net amount
foreach ( $items as $item ) {
$tmp_price = (int)$item['price'];
$tmp_quantity = (int)$item['quantity'];
// Add the price to the net amount
$net += $tmp_price * $tmp_quantity;
}
return $net;
}
public function exists(): bool
{
// Check if the id is greater than 0, and that the deleted_at property is null
@@ -273,19 +299,6 @@ class orders_o extends db
$this->{$data['field']}->set($data['value']);
}
/**
* Get the order history for a vehicle plate
* @param string $plate The vehicle plate
* @return array The order history
*/
public function get_vehicle_order_history(string $plate): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' AND deleted_at IS NULL";
$result = $db->query($sql);
return $db->fetch_all($result);
}
public function getOrderByInvoiceId(int $invoiceId): orders_o
{
global $db;
@@ -295,17 +308,17 @@ class orders_o extends db
$row = $result->fetch_assoc();
return $this->getOrderById($row['id']);
}
throw new \Exception('Order not found');
throw new Exception('Order not found');
}
/**
* Set the stripe invoicing for an order
* @throws \Exception
* @throws Exception
*/
public function setStripeInvoicing(string $payment_link_id, string $email, string $url): void
public function setStripeInvoicing(string $invoice_id, string $stripe_customer_id, string $url): void
{
self::requireSelected();
$this->stripe_module_orders->add($this->id, $email, $payment_link_id, $url);
$this->stripe_module_orders->add($this->id, $invoice_id, $stripe_customer_id, $url);
self::objectChanged();
}
@@ -328,16 +341,127 @@ class orders_o extends db
// Set the values of the object properties
$this->getObjectProperties();
self::requireSelected();
self::assignToInvoiceCollection();
return $this;
} catch (\Exception $e) {
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
/**
* Assign the order to an invoice collection
* @param int|null $invoiceCollectionId The invoice collection id, if not set, the default invoice collection id will be used.
* @throws Exception If the order is not selected
*/
public function assignToInvoiceCollection(int $invoiceCollectionId = null): void
{
// If the invoice collection id is not set, get the default invoice collection id
self::requireSelected();
// Get the customer
$customer = new users_o();
$customer->getUserByCustomerNumber($this->customer_id->value());
$customer->requireSelected();
// Get the invoice collection id
$invoiceCollectionId = $invoiceCollectionId ?? $customer->getNewOrderInvoiceCollectionId();
// Assign the order to the invoice collection
$this->invoice_collection_id->set($invoiceCollectionId);
self::objectChanged();
}
public function objectChanged(): void
{
// Since the orders object is not cached, there is no need to invalidate the cache
}
/**
* Get the recommended order items, based on the order history, vehicle plate and department
* @return array The recommended order items
* @throws Exception If the order is not selected
*/
public function getRecommendedOrder(): array
{
self::requireSelected();
$recommended = [
'reg_1' => self::getRecommendedOrderPlate($this->reg_1->value()),
'reg_2' => [],
'reg_3' => [],
];
return $recommended;
}
/**
* @throws Exception If the order is not selected
*/
public function getRecommendedOrderPlate(string $plate): array
{
self::requireSelected();
$result = [
'order_history' => self::get_vehicle_last_orders_items($plate),
];
// If the MotorApi is enabled, get the recommended order items based on the vehicle plate
$MotorApi = new motorapi();
if ($MotorApi->config->enabled->isTrue()) {
$MotorApi_data = $MotorApi->getRecommendedProducts($plate);
if ($MotorApi_data) {
// Get the recommended order items based on the vehicle plate
$result['motorapi'] = $MotorApi_data;
}
}
return $result;
}
/**
* Get the last orders item ids for a vehicle plate
* @param string $plate The vehicle plate
* @return array The last order item ids
*/
public function get_vehicle_last_orders_items(string $plate): array
{
global /** @var db $db */
$db;
$sql = "SELECT id FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' AND deleted_at IS NULL ORDER BY id DESC LIMIT 5";
$result = $db->query($sql);
$orders = $db->fetch_all($result);
$order_items = [
1 => [],
2 => [],
3 => [],
4 => [],
5 => [],
];
// Get the order item (ids) from the last 5 orders
$index = 0;
foreach ( $orders as $order ) {
$index++;
$order_tmp = new orders_o();
// Get the order items for the order
$items_tmp = $order_tmp->getOrderItems($order['id']);
// Add the items to the result
foreach ( $items_tmp as $item ) {
$order_items[$index][] = [
'product_id' => $item['product_id'],
'reference' => $item['reference'],
'quantity' => $item['quantity'],
'product' => $item['product'],
];
}
}
return $order_items;
}
/**
* Get the order history for a vehicle plate
* @param string $plate The vehicle plate
* @return array The order history
*/
public function get_vehicle_order_history(string $plate): array
{
global $db;
$sql = "SELECT * FROM $this->table WHERE reg_1 = '$plate' OR reg_2 = '$plate' OR reg_3 = '$plate' AND deleted_at IS NULL ORDER BY id DESC LIMIT 5";
$result = $db->query($sql);
return $db->fetch_all($result);
}
}
@@ -14,6 +14,8 @@ class product_options_o extends db
public object_property $product_id;
public object_property $option_id;
public object_property $name;
public object_property $min;
public object_property $max;
public function structure(): void
{
@@ -43,6 +45,8 @@ class product_options_o extends db
$this->product_id = new object_property($this->table, $this->id, 'product_id', 'int', false);
$this->option_id = new object_property($this->table, $this->id, 'option_id', 'int', false);
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
$this->min = new object_property($this->table, $this->id, 'min', 'int', false);
$this->max = new object_property($this->table, $this->id, 'max', 'int', false);
}
public function objectChanged(): void
@@ -50,6 +54,18 @@ class product_options_o extends db
//TODO: Add cache invalidation
}
/**
* There's no need to keep track of when a product option is deleted,
* so we just forcefully delete it.
* @throws Exception If the object was not selected
* @throws Exception If the object was not deleted successfully
*/
public function delete(): void
{
self::requireSelected();
self::deletePermanently();
}
/**
* Set the name of the product option
* @param string $name
@@ -58,10 +74,60 @@ class product_options_o extends db
public function set_name(string $name): void
{
self::requireSelected();
// If the string is empty, set it to null
if (empty($name)) {
$this->name->nullify();
self::objectChanged();
return;
}
$this->name->set($name);
self::objectChanged();
}
/**
* Set the min value of the product option
* @param int|null $min
* @notation If the int is less than 1 or empty, set it to null
* @throws Exception If the object was not selected
*/
public function set_min(int|null $min): void
{
self::requireSelected();
// If the int is empty, set it to null
if (empty($min) || $min < 1) {
$this->min->nullify();
self::objectChanged();
return;
}
$this->min->set($min);
self::objectChanged();
}
/**
* Set the max value of the product option
* @param int|null $max
* @notation If the int is less than 1, or empty, it will be set to null
* @throws Exception If the object was not selected
*/
public function set_max(int|null $max): void
{
self::requireSelected();
// If the int is empty, set it to null
if (empty($max) || $max < 1) {
$this->max->nullify();
self::objectChanged();
return;
}
$this->max->set($max);
self::objectChanged();
}
/**
* Get all options for a product
* @param int $id The id of the product
* @return array
* @throws Exception If the object was not selected
*/
public function getProductOptions(int $id): array
{
$options = self::getFieldsWhere(['product_id' => $id],
@@ -70,6 +136,8 @@ class product_options_o extends db
'product_id',
'option_id',
'name',
'min',
'max',
'created_at',
'updated_at'
]
@@ -82,6 +150,8 @@ class product_options_o extends db
'product_id' => (int)$option['product_id'],
'option_id' => (int)$option['option_id'],
'name' => (string)$option['name'] === '' ? $tmp_product['name'] : (string)$option['name'],
'min' => $option['min'] === null ? null : (int)$option['min'],
'max' => $option['max'] === null ? null : (int)$option['max'],
'created_at' => (string)$option['created_at'],
'updated_at' => (string)$option['updated_at'],
'product' => $tmp_product
@@ -89,4 +159,14 @@ class product_options_o extends db
}
return $tmp;
}
/**
* Get all products with a specific option
* @param int $product_id The id of the addon product
* @return array
*/
public function getOptionProducts(int $product_id): array
{
return self::getFieldsWhere(['option_id' => $product_id], ['product_id']);
}
}
+22 -1
View File
@@ -45,6 +45,21 @@ class products_o extends db
* @var object_property
*/
public object_property $apply_category_discount;
/**
* Whether the product requires a note
* @var object_property
*/
public object_property $requires_note;
/**
* The timestamp of when the object was created
* @var object_property
*/
public object_property $created_at;
/**
* The timestamp of when the object was last updated
* @var object_property
*/
public object_property $updated_at;
public function structure(): void
{
@@ -78,6 +93,9 @@ class products_o extends db
$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);
$this->requires_note = new object_property($this->table, $this->id, 'requires_note', 'bool', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
}
public function add(string $name, string $description, int $price, string|bool $category = false, string|bool $piktogram = false, string|bool $economicProductId = false): void
@@ -162,7 +180,10 @@ class products_o extends db
'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_category_discount' => (bool)$this->apply_category_discount->value(),
'requires_note' => (bool)$this->requires_note->value(),
'created_at' => (string)$this->created_at->value(),
'updated_at' => (string)$this->updated_at->value(),
];
}
@@ -0,0 +1,104 @@
<?php
namespace objects;
use classes\db;
use classes\object_property;
use classes\stripe;
use Exception;
use Stripe\Exception\ApiErrorException;
use traits\db_object_t;
class stripe_module_customers_o extends db
{
use db_object_t;
public object_property $email;
public object_property $customer_id;
public object_property $created_at;
private bool $paid;
public function structure(): void
{
$this->setTable('stripe_module_customers');
}
/**
* Add a customer to the database
* @param string $email The customer's email address
* @param string $customer_id The Stripe customer ID
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(string $email, string $customer_id): void
{
$tmp_id = self::add_object([
'email' => $email,
'customer_id' => $customer_id,
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
}
public function getObjectProperties(): void
{
$this->email = new object_property($this->table, $this->id, 'email', 'string', false);
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
/**
* @throws ApiErrorException
*/
public function asArray(): array
{
$object = self::retrieveCustomer();
return [
'id' => (int)$this->id,
'email' => $this->email->value(),
'customer_id' => $this->customer_id->value(),
'created_at' => $this->created_at->value(),
'object' => $object
];
}
/**
* @throws ApiErrorException
* @throws Exception
*/
public function retrieveCustomer(): object
{
// Validate the object
self::requireSelected();
// Return the payment link
return (new stripe())->customers->retrieve($this->customer_id->value());
}
/**
* Check if a customer has an account
* @param string $email
* @return bool
*/
public function doesCustomerHaveAccount(string $email): bool
{
return (bool)self::getFieldsWhere(['email' => $email], ['id']);
}
/**
* Get the customer ID from the email address
* @param string $email
* @return string
*/
public function getCustomerId(string $email): string
{
return self::getFieldsWhere(['email' => $email], ['customer_id'])[0]['customer_id'];
}
}
@@ -13,13 +13,13 @@ class stripe_module_orders_o extends db
{
use db_object_t;
public object_property $payment_link_id;
public object_property $email;
public object_property $invoice_id;
public object_property $customer_id;
public object_property $email_sent;
public object_property $url;
public object_property $created_at;
private bool $paid;
public function structure(): void
{
@@ -35,12 +35,12 @@ class stripe_module_orders_o extends db
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(int $order_id, string $email, string $payment_link_id, string $url): void
public function add(int $order_id, string $invoice_id, string $stripe_customer_id, string $url): void
{
$tmp_id = self::add_object([
'id' => $order_id,
'email' => $email,
'payment_link_id' => $payment_link_id,
'invoice_id' => $invoice_id,
'customer_id' => $stripe_customer_id,
'url' => $url,
]);
$this->id = $tmp_id;
@@ -50,8 +50,8 @@ class stripe_module_orders_o extends db
public function getObjectProperties(): void
{
$this->payment_link_id = new object_property($this->table, $this->id, 'payment_link_id', 'string', false);
$this->email = new object_property($this->table, $this->id, 'email', 'string', false);
$this->invoice_id = new object_property($this->table, $this->id, 'invoice_id', 'string', false);
$this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'string', false);
$this->email_sent = new object_property($this->table, $this->id, 'email_sent', 'timestamp', false);
$this->url = new object_property($this->table, $this->id, 'url', 'string', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
@@ -67,14 +67,17 @@ class stripe_module_orders_o extends db
*/
public function asArray(): array
{
$object = self::retrievePaymentLink();
return [
'id' => (int)$this->id,
'payment_link_id' => (string)$this->payment_link_id->value(),
'email' => (string)$this->email->value(),
'email_sent' => $this->email_sent->value() ? (string)$this->email_sent->value() : null,
'invoice_id' => (string)$this->invoice_id->value(),
'customer_id' => (string)$this->customer_id->value(),
'url' => (string)$this->url->value(),
'created_at' => (string)$this->created_at->value(),
'object' => self::retrievePaymentLink()
'paid' => $object->paid,
'status' => $object->status,
'amount_due' => $object->amount_due,
'amount_paid' => $object->amount_paid,
];
}
@@ -87,7 +90,7 @@ class stripe_module_orders_o extends db
// Validate the object
self::requireSelected();
// Return the payment link
return (new stripe())->payment_link->retrieve($this->payment_link_id->value());
return (new stripe())->invoice->retrieve($this->invoice_id->value());
}
}
@@ -4,6 +4,7 @@ namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class user_key_value_pairs_o extends db
@@ -39,6 +40,7 @@ class user_key_value_pairs_o extends db
public function setValue($var, $val): user_key_value_pairs_o
{
global $db;
self::requireSelected();
// Avoid SQL injection
$var = $db->escape_string($var);
$val = $db->escape_string($val);
@@ -54,9 +56,20 @@ class user_key_value_pairs_o extends db
return $this;
}
/**
* @throws Exception
*/
public function requireSelected(): void
{
if (empty($this->user_id)) {
throw new Exception('The user id is not set!.');
}
}
public function getValue($var): mixed
{
global $db;
self::requireSelected();
// Avoid SQL injection
$var = $db->escape_string($var);
// Get the record from the database
@@ -71,6 +84,7 @@ class user_key_value_pairs_o extends db
public function deleteValue($var): user_key_value_pairs_o
{
global $db;
self::requireSelected();
// Avoid SQL injection
$var = $db->escape_string($var);
// Create a new record in the database
@@ -82,6 +96,7 @@ class user_key_value_pairs_o extends db
public function getAllKeys(): array
{
global $db;
self::requireSelected();
// Get all the keys from the database
$sql = "SELECT var, val FROM $this->table WHERE user_id = $this->user_id";
$result = $db->query($sql);
@@ -147,17 +147,26 @@ class user_price_overrides_o extends db
$prices = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
// Parse the row
$row['id'] = (int)$row['id'];
$row['user_id'] = (int)$row['user_id'];
$row['is_category'] = (bool)$row['is_category'];
$row['product_or_category_id'] = (int)$row['product_or_category_id'];
$row['percentage'] = (int)$row['percentage'];
$row['created_at'] = (string)$row['created_at'];
$row['updated_at'] = (string)$row['updated_at'];
// Add the row to the list
$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",
'id' => (int)999999,
'user_id' => (int)$this->user_id,
'is_category' => true,
'product_or_category_id' => "global",
'percentage' => "" . $economic_user_global_discount,
'percentage' => (int)$economic_user_global_discount,
'created_at' => "2021-01-01 00:00:00",
'updated_at' => "2021-01-01 00:00:00"
];
+116 -9
View File
@@ -8,6 +8,7 @@ use classes\object_property;
use classes\response;
use customers\economic_customer_mo;
use customers\economicCustomers;
use Exception;
use languages\language_pack_en_us;
use traits\db_object_t;
@@ -398,15 +399,6 @@ class users_o extends db
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
@@ -528,6 +520,19 @@ class users_o extends db
$this->all_keys = $this->keys->setUser($this->id)->getAllKeys();
}
/**
* Get the group of the user
* @throws Exception If the user is not selected
*/
public function getGroup(): groups_o
{
self::requireSelected();
// Get the group of the user
$group = new groups_o();
$group->select($this->group_id->value());
return $group;
}
public function getCode(): string|null
{
// Get the customer code
@@ -858,4 +863,106 @@ class users_o extends db
$user = self::getFieldsWhere(['customer_number' => $customerNumber], ['id']);
return $user[0]['id'];
}
/**
* Get the invoice collection ID, where the new order should be added to.
* This is based on the user's settings. (If the user has the attribute 'invoiceAllOrdersIndividually', the new order should be added to a new invoice collection)
* @return int The ID of the order invoice collection the new order should be added to.
* @throws Exception
*/
public function getNewOrderInvoiceCollectionId(): int
{
// Check if the user has the attribute 'invoiceAllOrdersIndividually'
$invoice_collection = new collected_order_invoices_o();
if ($this->invoicePerOrder() || !self::hasOpenInvoiceCollection()) {
// Require the user to have a customer number above 0
if (is_null($this->customer_number->value()) || $this->customer_number->value() === 0) {
throw new Exception('The user does not have a customer number');
}
// The user has the attribute 'invoiceAllOrdersIndividually', create a new invoice collection
$invoice_collection->add((int)$this->customer_number->value());
return $invoice_collection->id;
}
// The user does not have the attribute 'invoiceAllOrdersIndividually', get the ID of the last invoice collection
return self::getOpenInvoiceCollection()->id;
}
/**
* 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');
}
/**
* Check if the user has an open invoice collection
* @return bool
*/
public function hasOpenInvoiceCollection(): bool
{
// Check if the user has an open invoice collection
$invoice_collection = new collected_order_invoices_o();
return $invoice_collection->hasOpenInvoiceCollection($this->customer_number->value());
}
/**
* Get the open invoice collection of the user
* @return collected_order_invoices_o
* @throws Exception If the user does not have an open invoice collection
* @throws Exception If the user does not have a customer number
*/
public function getOpenInvoiceCollection(): collected_order_invoices_o
{
// Require the user to have a customer number above 0
if (is_null($this->customer_number->value()) || $this->customer_number->value() === 0) {
throw new Exception('The user does not have a customer number');
}
self::requireSelected();
// Get the open invoice collection of the user
$invoice_collection = new collected_order_invoices_o();
return $invoice_collection->getLatestOpenInvoiceCollection($this->customer_number->value());
}
/**
* Get all customers with the given attributes
* @notation Retrieve all customers with ALL the given attributes
* @param array $attributes The attributes to search for (e.g. ['attribute1', 'attribute2'])
* @return array The e-conomic customer numbers with the given attributes (e.g. [123456, 654321])
*/
public function getCustomerNumbersWithAttributes(array $attributes): array
{
global $db;
// Create an array to store the customer numbers
$user_ids = [];
$customer_numbers = [];
// Loop through the attributes
foreach ( $attributes as $attribute ) {
// Get the customer numbers with the attribute
$sql = "SELECT user_id FROM customer_attributes WHERE attribute = '$attribute'";
$result = $db->query($sql);
// Loop through the results
while ($row = $result->fetch_assoc()) {
// Add the customer number to the array
$user_ids[] = (int)$row['user_id'];
}
}
// Remove duplicates from the array
$user_ids = array_unique($user_ids);
// Get the customer numbers from the user IDs
foreach ( $user_ids as $user_id ) {
// Get the customer number from the user ID
$sql = "SELECT customer_number FROM $this->table WHERE id = $user_id";
$result = $db->query($sql);
// Loop through the results
while ($row = $result->fetch_assoc()) {
// Add the customer number to the array
$customer_numbers[] = (int)$row['customer_number'];
}
}
// Remove duplicates from the array
// Return the customer numbers
return array_unique($customer_numbers);
}
}
+53 -10
View File
@@ -41,7 +41,12 @@ class bookingsRoute
function ($booking) {
$booking['department'] = (int)$booking['department'];
return $booking;
}
},
$bookings_o->forceRestrictFilters(
[
'department' => $user->getGroup()->getDepartments(),
]
)
))
);
} else {
@@ -50,7 +55,12 @@ class bookingsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_bookings' => 'List all bookings',
'issue_wash_certificates' => 'When set, the response will include the wash certificate token for sending wash certificates'
]
);
/** Own bookings */
$this->get('/user/bookings', function () {
// Require the user to be logged in
@@ -82,7 +92,11 @@ class bookingsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_own_bookings' => 'List all bookings for the logged in user'
]
);
// Synchronize booking from the external system
$this->post('/admin/bookings/sync', function () {
// Require the user to be logged in
@@ -130,7 +144,11 @@ class bookingsRoute
$response->success(
['message' => 'Successfully synced booking']
);
});
},
[
'sync_bookings' => 'Sync bookings from the external system NOTE: This permission is only required if the auth_key is not set'
]
);
// Get a departments unfulfilled bookings (count) for the day
$this->get('/admin/bookings/department/count', function () {
@@ -173,12 +191,17 @@ class bookingsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_department_bookings_count' => 'List the unfulfilled bookings count for a department'
]
);
$this->post('/user/bookings/washcertificate/download', function () {
// Require the user to be logged in
global /** @var response $response */
$response;
// Check if the user has access to the department
$this->requirePermission('download_own_wash_certificate');
// Get the user object
$user = (new authentication())->get_user();
@@ -206,7 +229,11 @@ class bookingsRoute
$response->success(
["link" => $wash_certificate_store->getWashCertificateDownload($id)]
);
});
},
[
'download_own_wash_certificate' => 'Download the wash certificate for a booking'
]
);
$this->post('/admin/bookings/delete', function () {
// Require the user to be logged in
@@ -235,7 +262,11 @@ class bookingsRoute
$response->success(
["message" => "Booking deleted"]
);
});
},
[
'delete_booking' => 'Delete a booking'
]
);
$this->post('/superuser/bookings/sync/all', function () {
// Require the user to be logged in
@@ -256,7 +287,11 @@ class bookingsRoute
$response->success(
["message" => "All bookings synced"]
);
});
},
[
'sync_all_bookings' => 'Sync all bookings from the external system'
]
);
$this->post('/admin/bookings/completeWashWithoutWashCertificate', function () {
// Require the user to be logged in
@@ -285,7 +320,11 @@ class bookingsRoute
$response->success(
["message" => "Wash completed without wash certificate"]
);
});
},
[
'complete_wash_without_wash_certificate' => 'Complete a wash without a wash certificate'
]
);
$this->post('/user/bookings/delete', function () {
// Require the user to be logged in
@@ -314,6 +353,10 @@ class bookingsRoute
$response->success(
["message" => "Booking deleted"]
);
});
},
[
'delete_own_booking' => 'Delete the users own booking'
]
);
}
}
+15 -3
View File
@@ -52,7 +52,11 @@ class categoriesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_categories' => 'List all categories'
]
);
$this->post('/categories', function () {
@@ -85,7 +89,11 @@ class categoriesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_category' => 'Add a category'
]
);
$this->put('/categories', function () {
@@ -127,6 +135,10 @@ class categoriesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_category' => 'Edit a category'
]
);
}
}
+5 -1
View File
@@ -43,6 +43,10 @@ class cronRoute
'message' => 'All cron jobs ran successfully',
'data' => $response_cron ?? []
]);
});
},
[
'SUPERUSER_RUN_CRON' => 'Run cron jobs'
]
);
}
}
@@ -47,7 +47,11 @@ class customerAttributes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_customer_attributes' => 'List all customer attributes'
]
);
$this->post('/customer/attributes', function () {
// Require the user to be logged in
@@ -78,7 +82,11 @@ class customerAttributes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_customer_attribute' => 'Add a customer attribute'
]
);
$this->delete('/customer/attributes', function () {
// Require the user to be logged in
@@ -113,6 +121,10 @@ class customerAttributes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'delete_customer_attribute' => 'Delete a customer attribute'
]
);
}
}
@@ -3,7 +3,6 @@
namespace routes;
use classes\authentication;
use objects\customer_notes_o;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
@@ -25,7 +24,9 @@ class customerCodeDepartmentRoute
// 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_codes', 'global', 1, $user->id, 'GET_CUSTOMER_CODE', 'Successfully retrieved customer code');
// Check if the user exists
@@ -42,7 +43,11 @@ class customerCodeDepartmentRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'get_customer_code' => 'Get customer code'
]
);
$this->post('/admin/customer/code', function () {
// Require the user to be logged in
@@ -55,7 +60,9 @@ class customerCodeDepartmentRoute
// 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); }
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
@@ -72,6 +79,10 @@ class customerCodeDepartmentRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_customer_code' => 'Add a customer code'
]
);
}
}
+15 -3
View File
@@ -45,7 +45,11 @@ class customerNotes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_customer_notes' => 'List all customer notes'
]
);
$this->post('/customer/notes', function () {
// Require the user to be logged in
@@ -81,7 +85,11 @@ class customerNotes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_customer_note' => 'Add a customer note'
]
);
$this->delete('/customer/notes', function () {
// Require the user to be logged in
@@ -109,6 +117,10 @@ class customerNotes
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'delete_customer_note' => 'Delete a customer note'
]
);
}
}
@@ -14,6 +14,7 @@ class customerSearchRoute
public function run(): void
{
//TODO: Remove this, this is deprecated in favor of the new search endpoint
$this->post('/customers/search', function () {
// Require the user to be logged in
global $response;
@@ -47,12 +48,16 @@ class customerSearchRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'search_customers' => 'Search for customers'
]
);
self::get('/customers', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_customers');
self::requirePermission('search_customers');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
@@ -100,7 +105,11 @@ class customerSearchRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'search_customers' => 'Search for customers, and list all customers if no search is provided'
]
);
}
private static function parseFunction($collection, \Closure $param): array
@@ -0,0 +1,554 @@
<?php
namespace routes;
use classes\authentication;
use objects\department_daily_reports_o;
use objects\logs_o;
use traits\route_t;
class departmentDailyReportsRoute
{
use route_t;
public function run(): void
{
$this->get('/departments/daily-reports', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_department_daily_reports');
// 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_DEPARTMENT_DAILY_REPORTS', 'Successfully listed departments daily reports');
$department_daily_reports_o = new department_daily_reports_o();
// Return the list of departments
$response->success(
$department_daily_reports_o
->setSearchableFields([
// The fields that can be searched. This would otherwise make it possible to get secret information from the database, simply by searching for it and getting the result count back
'id',
'water_usage',
'water_usage_morning',
'notes',
'filled_by',
'created_at',
'department_id',
])
->listObjectsWithPaginationIfSet(
function ($department_report) use ($user) {
return [
'id' => (int)$department_report['id'],
'department_id' => (int)$department_report['department_id'],
'water_usage' => (int)$department_report['water_usage'],
'water_usage_morning' => (int)$department_report['water_usage_morning'],
'notes' => (string)$department_report['notes'],
'filled_by' => (int)$department_report['filled_by'],
'created_at' => (string)$department_report['created_at'],
];
},
// This makes sure that the user can only see reports from the departments they explicitly have access to
$department_daily_reports_o->forceRestrictFilters(
[
// This makes sure that the user can only see orders from the departments they explicitly have access to
'department_id' => $user->getGroup()->getDepartments(),
]
)
)
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
'department_access_:id' => 'Access the department'
]
);
$this->get('/departments/daily-reports/get', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_department_daily_reports');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(
[
'id',
'date'
]
);
// Validate the department_id
self::requireType(
(int)self::getParameter('id'),
self::type_int()
);
// Require the department_id to be above 0
self::requireMinValue(
(int)self::getParameter('id'),
1
);
// Require the date to be at least 0 characters long
self::requireMinLength(
'date',
0
);
// Validate the date
self::requireDateFormat(
(string)self::getParameter('date'),
self::FORMAT_DATE()
);
// Determine if the user has access to the department
self::requireDepartmentAccess((int)self::getParameter('id'));
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS', 'Successfully listed departments daily reports');
// Return the list of departments
$department_daily_reports_o = new department_daily_reports_o();
try {
$department_daily_report_latest = $department_daily_reports_o->selectDepartmentReportByDate(
(int)self::getParameter('id'),
(string)self::getParameter('date')
);
} catch (\Exception $e) {
// Create a new department daily report if it does not exist
$department_daily_report_latest = $department_daily_reports_o->add(
(int)self::getParameter('id'),
0,
0,
'',
$user->id,
(string)self::getParameter('date')
);
}
$response->success(
[
'id' => (int)$department_daily_report_latest->id,
'department_id' => (int)$department_daily_report_latest->department_id->value(),
'water_usage' => (int)$department_daily_report_latest->water_usage->value(),
'water_usage_morning' => (int)$department_daily_report_latest->water_usage_morning->value(),
'notes' => (string)$department_daily_report_latest->notes->value(),
'filled_by' => (int)$department_daily_report_latest->filled_by->value(),
'created_at' => (string)$department_daily_report_latest->created_at->value(),
]
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
'department_access_:id' => 'Access the department'
]
);
$this->post('/departments/daily-reports', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('create_department_daily_reports');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(
[
'department_id',
'water_usage',
'water_usage_morning',
'notes',
'date'
]
);
// Validate the department_id
self::requireType(
(int)self::getParameter('department_id'),
self::type_int()
);
// Require the department_id to be above 0
self::requireMinValue(
(int)self::getParameter('department_id'),
1
);
// Validate the water_usage
self::requireType(
(int)self::getParameter('water_usage'),
self::type_int()
);
// Require the water_usage to be at least 0
self::requireMinValue(
(int)self::getParameter('water_usage'),
0
);
// Validate the water_usage_morning
self::requireType(
(int)self::getParameter('water_usage_morning'),
self::type_int()
);
// Require the water_usage_morning to be at least 0
self::requireMinValue(
(int)self::getParameter('water_usage_morning'),
0
);
// Validate the notes
self::requireType(
(string)self::getParameter('notes'),
self::type_string()
);
// Require the notes to be at least 0 characters long
self::requireMinLength(
'notes',
0
);
// Require the notes to be at most 4000 characters long
self::requireMaxLength(
'notes',
4000
);
// Validate the date
self::requireType(
(string)self::getParameter('date'),
self::type_string()
);
// Require the date to be at least 0 characters long
self::requireMinLength(
'date',
0
);
// Require the date to be at most 10 characters long
self::requireMaxLength(
'date',
10
);
// Validate the date format (YYYY-MM-DD)
self::requireDateFormat(
(string)self::getParameter('date'),
self::FORMAT_DATE()
);
// Determine if the user has access to the department
self::requireDepartmentAccess((int)self::getParameter('department_id'));
// Check if there is already a report for today
if ((new department_daily_reports_o())->hasReport((int)self::getParameter('department_id'), (string)self::getParameter('date'))) {
$response->error('There is already a report for today', 400);
}
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'CREATE_DEPARTMENT_DAILY_REPORTS', 'Successfully created department daily report');
// Return the list of departments
$response->success(
(new department_daily_reports_o())
->add(
(int)self::getParameter('department_id'),
(int)self::getParameter('water_usage'),
(int)self::getParameter('water_usage_morning'),
(string)self::getParameter('notes'),
$user->id
)->asArray()
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'CREATE_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'create_department_daily_reports' => 'Create a new department daily report',
'department_access_:id' => 'Access the department'
]
);
$this->put('/departments/daily-reports', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('update_department_daily_reports');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(
[
'id',
]
);
// Validate the id
self::requireType(
(int)self::getParameter('id'),
self::type_int()
);
// Require the department_id to be above 0
self::requireMinValue(
(int)self::getParameter('id'),
1
);
// Check which fields are set
if (self::isParametersSet(['water_usage'])) {
// Validate the water_usage
self::requireType(
(int)self::getParameter('water_usage'),
self::type_int()
);
// Require the water_usage to be at least 0
self::requireMinValue(
(int)self::getParameter('water_usage'),
0
);
}
if (self::isParametersSet(['water_usage_morning'])) {
// Validate the water_usage_morning
self::requireType(
(int)self::getParameter('water_usage_morning'),
self::type_int()
);
// Require the water_usage_morning to be at least 0
self::requireMinValue(
(int)self::getParameter('water_usage_morning'),
0
);
}
if (self::isParametersSet(['notes'])) {
// Validate the notes
self::requireType(
(string)self::getParameter('notes'),
self::type_string()
);
// Require the notes to be at least 0 characters long
self::requireMinLength(
(string)self::getParameter('notes'),
0
);
// Require the notes to be at most 4000 characters long
self::requireMaxLength(
(string)self::getParameter('notes'),
4000
);
}
// Check if the report exists
// Update the department daily report
$department_daily_reports_o = new department_daily_reports_o();
$department_daily_report = $department_daily_reports_o->select((int)self::getParameter('id'));
// Determine if the user has access to the department
self::requireDepartmentAccess((int)$department_daily_report->department_id->value());
// Check the parameters that are set
if (self::isParametersSet(['water_usage'])) {
$department_daily_report->water_usage->set((int)self::getParameter('water_usage'));
}
if (self::isParametersSet(['water_usage_morning'])) {
$department_daily_report->water_usage_morning->set((int)self::getParameter('water_usage_morning'));
}
if (self::isParametersSet(['notes'])) {
$department_daily_report->notes->set((string)self::getParameter('notes'));
}
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'UPDATE_DEPARTMENT_DAILY_REPORTS', 'Successfully updated department daily report');
// Return the department daily report
$response->success($department_daily_report->asArray());
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'UPDATE_DEPARTMENT_DAILY_REPORTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'update_department_daily_reports' => 'Update a department daily report',
'department_access_:id' => 'Access the department'
]
);
$this->get('/departments/daily-reports/product-count', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_department_daily_reports');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(
[
'date',
'department_id',
'product_id'
]
);
// Validate the date
self::requireType(
(string)self::getParameter('date'),
self::type_string()
);
// Require the date to be at least 0 characters long
self::requireMinLength(
'date',
0
);
// Require the date to be at most 10 characters long
self::requireMaxLength(
'date',
10
);
// Validate the date format (YYYY-MM-DD)
self::requireDateFormat(
(string)self::getParameter('date'),
'Y-m-d'
);
// Validate the department_id
self::requireType(
(int)self::getParameter('department_id'),
self::type_int()
);
// Require the department_id to be above 0
self::requireMinValue(
(int)self::getParameter('department_id'),
1
);
// Validate the product_id
self::requireType(
(int)self::getParameter('product_id'),
self::type_int()
);
// Require the product_id to be above 0
self::requireMinValue(
(int)self::getParameter('product_id'),
1
);
// Determine if the user has access to the department
self::requireDepartmentAccess((int)self::getParameter('department_id'));
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'Successfully listed departments daily reports products');
// Return the list of products sold on the selected date
$response->success(
[
'quantity' =>
(new department_daily_reports_o())
->getProductsSoldOnDate(
(string)self::getParameter('date'),
(int)self::getParameter('department_id'),
(int)self::getParameter('product_id')
),
'date' => (string)self::getParameter('date'),
'department_id' => (int)self::getParameter('department_id'),
'product_id' => (int)self::getParameter('product_id'),
// This is the amount of products sold on the selected date, that this product could have been sold as a part of. (Addons)
'out_of' => (int)(new department_daily_reports_o())->getProductsSoldWithAddonApplicable(
(string)self::getParameter('date'),
(int)self::getParameter('department_id'),
(int)self::getParameter('product_id')
)
]
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
'department_access_:id' => 'Access the department'
]
);
$this->get('/departments/daily-reports/transaction-count', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_department_daily_reports');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(
[
'date',
'department_id'
]
);
// Validate the date
self::requireType(
(string)self::getParameter('date'),
self::type_string()
);
// Require the date to be at least 0 characters long
self::requireMinLength(
'date',
0
);
// Require the date to be at most 10 characters long
self::requireMaxLength(
'date',
10
);
// Validate the date format (YYYY-MM-DD)
self::requireDateFormat(
(string)self::getParameter('date'),
'Y-m-d'
);
// Validate the department_id
self::requireType(
(int)self::getParameter('department_id'),
self::type_int()
);
// Require the department_id to be above 0
self::requireMinValue(
(int)self::getParameter('department_id'),
1
);
// Determine if the user has access to the department
self::requireDepartmentAccess((int)self::getParameter('department_id'));
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'Successfully listed departments daily reports products');
// Return the list of products sold on the selected date
$response->success(
[
'quantity' =>
(new department_daily_reports_o())
->getTransactionsOnDateCount(
(string)self::getParameter('date'),
(int)self::getParameter('department_id')
),
'products' =>
(new department_daily_reports_o())
->getTransactionProductsOnDateCount(
(string)self::getParameter('date'),
(int)self::getParameter('department_id')
),
'earnings' =>
(new department_daily_reports_o())
->getTransactionsOnDateEarnings(
(string)self::getParameter('date'),
(int)self::getParameter('department_id')
),
'date' => (string)self::getParameter('date'),
'department_id' => (int)self::getParameter('department_id')
]
);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_DAILY_REPORTS_PRODUCTS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_daily_reports' => 'List the department daily reports, provided the user has access to the department',
'department_access_:id' => 'Access the department'
]
);
}
}
+77 -6
View File
@@ -7,6 +7,7 @@ use objects\categories_o;
use objects\department_categories_o;
use objects\departments_o;
use objects\logs_o;
use objects\orders_o;
use traits\route_t;
class departmentsRoute
@@ -25,6 +26,15 @@ class departmentsRoute
if ($user) {
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENTS', 'Successfully listed departments');
// Check if the id is set in the request
if (self::isParametersSet(['id'])) {
// Return the department
$response->success(
(new departments_o())->select((int)self::getParameter('id'))->asArray([
'slack_webhook' => $user->hasPermission('view_slack_webhook')
])
);
}
// Return the list of departments
$response->success(
(new departments_o())
@@ -60,7 +70,12 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_departments' => 'List all departments',
'view_slack_webhook' => 'View the slack webhook'
]
);
$this->post('/departments', function () {
// Require the user to be logged in
@@ -92,7 +107,11 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_department' => 'Add a department'
]
);
$this->put('/departments', function () {
// Require the user to be logged in
@@ -128,7 +147,11 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_department' => 'Edit a department'
]
);
$this->get('/departments/categories', function () {
// Require the user to be logged in
@@ -176,7 +199,11 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_department_categories' => 'List all department categories'
]
);
$this->post('/departments/categories', function () {
// Require the user to be logged in
@@ -224,7 +251,11 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_department_category' => 'Add a department category'
]
);
$this->delete('/departments/categories', function () {
// Require the user to be logged in
@@ -258,6 +289,46 @@ class departmentsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'delete_department_category' => 'Delete a department category'
]
);
self::get('/departments/order/recommended', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_department_order_recommended');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required fields are set
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the order
$order = (new orders_o())->select((int)self::getParameter('id'));
$order->requireSelected();
// Check if the user is allowed to view the recommended order for the department
self::requireDepartmentAccess($order->department_id->value());
// Get the recommended order
$recommended_order = $order->getRecommendedOrder();
// Log the incident
(new logs_o())->add('departments', 'global', 1, $user->id, 'LIST_DEPARTMENT_ORDER_RECOMMENDED', 'Successfully listed the recommended department order');
// Return the recommended order
$response->success($recommended_order);
} else {
// Log the incident
(new logs_o())->add('departments', 'global', 1, 0, 'LIST_DEPARTMENT_ORDER_RECOMMENDED', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_order_recommended' => 'List the recommended department order',
'order_access_:id' => 'Access the order',
'department_access_:id' => 'Access the department'
]
);
}
}
@@ -129,7 +129,9 @@ class economicInvoiceRoute
(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);
}
});
}, [
'economic_invoice_draft_export' => 'Export an economic invoice draft'
]);
$this->delete('/economic/invoice/draft/delete', function () {
@@ -177,7 +179,9 @@ class economicInvoiceRoute
(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);
}
});
}, [
'economic_invoice_draft_delete' => 'Delete an economic invoice draft'
]);
$this->post('/economic/invoice/export', function () {
global $response;
@@ -237,7 +241,9 @@ class economicInvoiceRoute
(new logs_o())->add('economic_invoice', 'global', 1, 0, 'ECONOMIC_INVOICE_EXPORT', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
}, [
'economic_invoice_export' => 'Export an economic invoice'
]);
}
/**
@@ -33,6 +33,10 @@ class economicLayoutsRoute
(new logs_o())->add('economic_layouts', 'global', 1, 0, 'ECONOMIC_LAYOUTS', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'economic_layouts' => 'Get economic layouts'
]
);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace routes;
use classes\form;
use Exception;
use traits\route_t;
class formRoute
{
use route_t;
public function run(): void
{
$this->get('/form', function () {
global $response;
$form_identifier = $this->validateFormIdentifier();
$form = new form();
if (!$form->doesFormExist($form_identifier)) {
$response->error('Form does not exist', 400);
}
$response->success(
$form->getForm($form_identifier)->asArray()
);
});
$this->post('/form', function () {
global $response;
$form_identifier = $this->validateFormIdentifier();
$form = new form();
if (!$form->doesFormExist($form_identifier)) {
$response->error('Form does not exist', 400);
}
$form->submitForm(
$form->getForm($form_identifier)
);
$response->success(
'Form submitted successfully'
);
});
}
/**
* Validates the 'form_identifier' parameter and returns it in uppercase.
* @return string Validated and transformed form identifier.
* @throws Exception if validation fails.
*/
private function validateFormIdentifier(): string
{
self::requireParameters(['id']);
$form_identifier = self::getParameter('id');
self::requireType($form_identifier, self::type_string());
self::requireMinLength('id', 1);
self::requireMaxLength('id', 255);
return strtoupper($form_identifier);
}
}
@@ -36,6 +36,10 @@ class intimidateRoute
$token = (new authentication())->create_employee_token($data['user_id']);
// Return the token
$response->success(['token' => $token]);
});
},
[
'SUPERUSER_INTIMIDATE' => 'Intimidate a user'
]
);
}
}
+15 -3
View File
@@ -49,7 +49,11 @@ class invoicesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'get_invoice_draft' => 'Get invoice draft'
]
);
$this->post('/invoices/draft/close', function () {
// Require the user to be logged in
@@ -82,7 +86,11 @@ class invoicesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'close_invoice_draft' => 'Close invoice draft'
]
);
$this->get('/invoices/pdf', function () {
// Require the user to be logged in
@@ -123,6 +131,10 @@ class invoicesRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'get_invoice_pdf' => 'Get invoice pdf'
]
);
}
}
@@ -34,7 +34,11 @@ class moduleBackupsRoute
(new logs_o())->add('modules_backup', 'global', 1, 0, 'MODULES_BACKUP', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_backup_list' => 'List all backup modules'
]
);
/** Modules > Backups > POST */
$this->post('/modules/backup/backups', function () {
@@ -54,6 +58,10 @@ class moduleBackupsRoute
(new logs_o())->add('modules_backup', 'global', 1, 0, 'MODULES_BACKUP', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_backup_create' => 'Create a backup module'
]
);
}
}
+65 -13
View File
@@ -39,7 +39,11 @@ class moduleConfigRoute
(new logs_o())->add('economic_config', 'global', 1, 0, 'ECONOMIC_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'economic_config' => 'Get economic config'
]
);
/** Economic config > POST */
$this->post('/economic/config', function () {
@@ -55,7 +59,11 @@ class moduleConfigRoute
(new logs_o())->add('economic_config', 'global', 1, 0, 'ECONOMIC_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'economic_config' => 'Update economic config'
]
);
/** reCAPTCHA config > GET */
$this->get('/reCAPTCHA/config', function () {
@@ -71,7 +79,11 @@ class moduleConfigRoute
(new logs_o())->add('recaptcha_config', 'global', 1, 0, 'RECAPTCHA_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'recaptcha_config' => 'Get recaptcha config'
]
);
/** reCAPTCHA config > POST */
$this->post('/reCAPTCHA/config', function () {
@@ -87,7 +99,11 @@ class moduleConfigRoute
(new logs_o())->add('recaptcha_config', 'global', 1, 0, 'RECAPTCHA_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'recaptcha_config' => 'Update recaptcha config'
]
);
/** Email config > GET */
$this->get('/email/config', function () {
@@ -103,7 +119,11 @@ class moduleConfigRoute
(new logs_o())->add('email_config', 'global', 1, 0, 'EMAIL_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'email_config' => 'Get email config'
]
);
/** Email config > POST */
$this->post('/email/config', function () {
@@ -119,7 +139,11 @@ class moduleConfigRoute
(new logs_o())->add('email_config', 'global', 1, 0, 'EMAIL_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'email_config' => 'Update email config'
]
);
/** Email config > TEST */
$this->post('/email/config/test', function () {
@@ -146,7 +170,11 @@ class moduleConfigRoute
(new logs_o())->add('email_config', 'global', 1, 0, 'EMAIL_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'email_config' => 'Test email config'
]
);
$this->get('/backups/config', function () {
global $response;
@@ -161,7 +189,11 @@ class moduleConfigRoute
(new logs_o())->add('backups_config', 'global', 1, 0, 'BACKUPS_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'backups_config' => 'Get backups config'
]
);
$this->post('/backups/config', function () {
global $response;
@@ -176,7 +208,11 @@ class moduleConfigRoute
(new logs_o())->add('backups_config', 'global', 1, 0, 'BACKUPS_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'backups_config' => 'Update backups config'
]
);
/** MotorAPI config > GET */
$this->get('/motorapi/config', function () {
@@ -192,7 +228,11 @@ class moduleConfigRoute
(new logs_o())->add('motorapi_config', 'global', 1, 0, 'MOTORAPI_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'motorapi_config' => 'Get motorapi config'
]
);
/** MotorAPI config > POST */
$this->post('/motorapi/config', function () {
@@ -208,7 +248,11 @@ class moduleConfigRoute
(new logs_o())->add('motorapi_config', 'global', 1, 0, 'MOTORAPI_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'motorapi_config' => 'Update motorapi config'
]
);
/** Stripe config > GET */
$this->get('/stripe/config', function () {
@@ -224,7 +268,11 @@ class moduleConfigRoute
(new logs_o())->add('stripe_config', 'global', 1, 0, 'STRIPE_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'stripe_config' => 'Get stripe config'
]
);
/** Stripe config > POST */
$this->post('/stripe/config', function () {
@@ -240,6 +288,10 @@ class moduleConfigRoute
(new logs_o())->add('stripe_config', 'global', 1, 0, 'STRIPE_CONFIG', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'stripe_config' => 'Update stripe config'
]
);
}
}
@@ -0,0 +1,47 @@
<?php
namespace routes;
use classes\authentication;
use classes\economic;
use classes\response;
use classes\router;
use objects\logs_o;
use traits\route_t;
class moduleEconomicCustomerRoute
{
use route_t;
public function run(): void
{
global /** @var response $response */
/** @var router $router */
$router, $response;
/** Modules > Economic > Customer > Get customer */
$this->get('/modules/economic/customer', function () {
global $response;
self::requirePermission('modules_economic_customer_get');
$user = (new authentication())->get_user();
if ($user) {
self::requireParameters(['customer_number']);
self::requireType('customer_number', self::type_int());
self::requireMinLength('customer_number', 1);
self::requireMaxLength('customer_number', 10);
(new logs_o())->add('modules_economic', 'global', 1, 0, 'MODULES_ECONOMIC', 'User accessed the customer');
$result = (new economic())->getCustomer((int)$_GET['customer_number']);
$response->success((object)$result);
} else {
(new logs_o())->add('modules_economic', 'global', 0, 0, 'MODULES_ECONOMIC', 'User tried to access the customer without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_economic_customer_get' => 'Get customer'
]
);
}
}
@@ -57,7 +57,11 @@ class moduleEconomicRoute
(new logs_o())->add('economic', 'global', 1, 0, 'ECONOMIC_IMPORT_CUSTOMER', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'economic_import_customer' => 'Import customer from economic'
]
);
/** Economic > Departments > GET */
self::get('/economic/departments', function () {
@@ -89,7 +93,11 @@ class moduleEconomicRoute
// Return the error
$response->error('Invalid session', 400);
}
});
},
[
'economic_departments_get' => 'Get departments from economic'
]
);
/** Economic > Products > GET */
self::get('/economic/products', function () {
@@ -124,6 +132,10 @@ class moduleEconomicRoute
// Return the error
$response->error('Invalid session', 400);
}
});
},
[
'economic_products_get' => 'Get products from economic'
]
);
}
}
@@ -31,13 +31,17 @@ class moduleMotorAPIRoute
self::requireMinLength('license_plate', 1);
self::requireMaxLength('license_plate', 10);
(new logs_o())->add('modules_motorapi', 'global', 1, $user->id, 'MODULES_MOTORAPI', 'Successfully looked up license plate information');
$result = (new motorapi())->getLicensePlateInformation($this->fromRequest('license_plate'));
$result = (new motorapi())->getLicensePlateInformation($this->fromRequest('license_plate'), false);
$response->success((object)$result);
} else {
(new logs_o())->add('modules_motorapi', 'global', 1, 0, 'MODULES_MOTORAPI', 'No user found, or invalid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_motorapi_lookup' => 'Lookup license plate information'
]
);
}
}
+195 -10
View File
@@ -3,11 +3,14 @@
namespace routes;
use classes\authentication;
use classes\email;
use classes\response;
use classes\router;
use classes\stripe;
use objects\departments_o;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_customers_o;
use traits\route_t;
class moduleStripeRoute
@@ -34,7 +37,11 @@ class moduleStripeRoute
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the customers list without a valid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_stripe_customers_list' => 'List all customers'
]
);
/** Modules > Stripe > Products > List */
$this->get('/modules/stripe/products', function () {
@@ -49,7 +56,11 @@ class moduleStripeRoute
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the products list without a valid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_stripe_products_list' => 'List all products'
]
);
/** Modules > Stripe > Prices > List */
$this->get('/modules/stripe/prices', function () {
@@ -64,7 +75,11 @@ class moduleStripeRoute
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the prices list without a valid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_stripe_prices_list' => 'List all prices'
]
);
/** Modules > Stripe > Send Invoice */
$this->post('/modules/stripe/invoice', function () {
@@ -88,18 +103,188 @@ class moduleStripeRoute
$order->requireSelected();
// Log the action
(new logs_o())->add('modules_stripe', 'global', 1, $user->id, 'MODULES_STRIPE', 'User sent an invoice');
$result = (new stripe())->payment_link->generate(
(int)self::fromRequest('order_id'),
(string)self::fromRequest('email')
);
// Create a customer account, if it doesn't exist
$hasCustomerAccount = (new stripe_module_customers_o())->doesCustomerHaveAccount((string)self::fromRequest('email'));
if (!$hasCustomerAccount) {
$customer = (new stripe())->customers->create((string)self::fromRequest('email'));
(new stripe_module_customers_o())->add((string)self::fromRequest('email'), $customer->id);
} else {
// Get the customer account by email
$customer = (new stripe())->customers->retrieve(
// Get the customer ID
(new stripe_module_customers_o())
->getCustomerId(
(string)self::fromRequest('email')
)
);
}
// Create an invoice
$invoice = (new stripe())->invoice->generate((int)self::fromRequest('order_id'), $customer->id);
// Set the order stripe invoice details
$order->setStripeInvoicing($result->id, (string)self::fromRequest('email'), $result->url);
$order->setStripeInvoicing($invoice->id, $customer->id, $invoice->hosted_invoice_url);
$email = new email();
$email->sendStripeInvoiceEmail(
(string)self::fromRequest('email'),
null,
$invoice->hosted_invoice_url,
(int)self::fromRequest('order_id')
);
// Return the result
$response->success((object)$result);
$response->success((object)$invoice);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to send an invoice without a valid session');
$response->error('Invalid session', 400);
}
});
},
[
'modules_stripe_invoice_send' => 'Send invoice'
]
);
self::get('/modules/stripe/terminal/readers', function () {
global $response;
self::requirePermission('modules_stripe_terminal_readers_list');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('modules_stripe', 'global', 1, 0, 'MODULES_STRIPE', 'User accessed the readers list');
$result = (new stripe())->readers->list();
$response->success((object)$result);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the readers list without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_stripe_terminal_readers_list' => 'List all Stripe terminal readers'
]
);
self::get('/modules/stripe/terminal/locations', function () {
global $response;
self::requirePermission('modules_stripe_terminal_locations_list');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('modules_stripe', 'global', 1, 0, 'MODULES_STRIPE', 'User accessed the terminal locations list');
$result = (new stripe())->readers->listLocations();
$response->success((object)$result);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the terminal locations list without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_stripe_terminal_locations_list' => 'List all Stripe terminal locations'
]
);
self::get('/modules/stripe/department/terminal/location',
function () {
global $response;
self::requirePermission('modules_stripe_department_terminal_location_list');
self::requireParameters(['id']);
self::requireType((int)self::fromRequest('id'), self::TYPE_INT());
// Require the id to be above 0
if ((int)self::fromRequest('id') < 1) {
$response->error('Invalid department ID', 400);
}
$user = (new authentication())->get_user();
if ($user) {
// Make sure the user has access to the department
self::requireDepartmentAccess((int)self::fromRequest('id'));
// Make sure the department exists
$department = (new departments_o())->select((int)self::fromRequest('id'));
$department->requireSelected();
(new logs_o())->add('modules_stripe', 'global', 1, 0, 'MODULES_STRIPE', 'User accessed the department terminal location');
// Get the department terminal location
$isSet = $department->isStripeTerminalLocationSet();
// Return the result if the department terminal location is set, otherwise return null
$response->success([
'id' => (
$isSet
? $department->getStripeTerminalLocation()
: null
)
]);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the department terminal location without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_stripe_department_terminal_location_list' => 'List all department terminal location',
'department_access_:id' => 'Access department. - :id'
]
);
self::post('/modules/stripe/department/terminal/location',
function () {
global $response;
self::requirePermission('modules_stripe_department_terminal_location_set');
self::requireParameters(['id', 'location']);
self::requireType((int)self::fromRequest('id'), self::TYPE_INT());
self::requireType((string)self::fromRequest('location'), 'string');
self::requireMinLength('location', 1);
self::requireMaxLength('location', 255);
// Require the id to be above 0
if ((int)self::fromRequest('id') < 1) {
$response->error('Invalid department ID', 400);
}
$user = (new authentication())->get_user();
if ($user) {
// Make sure the user has access to the department
self::requireDepartmentAccess((int)self::fromRequest('id'));
// Make sure the department exists
$department = (new departments_o())->select((int)self::fromRequest('id'));
$department->requireSelected();
(new logs_o())->add('modules_stripe', 'global', 1, 0, 'MODULES_STRIPE', 'User set the department terminal location');
// Set the department terminal location
$department->setStripeTerminalLocation((string)self::fromRequest('location'));
// Return the result
$response->success([
'id' => $department->getStripeTerminalLocation()
]);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to set the department terminal location without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_stripe_department_terminal_location_set' => 'Set department terminal location',
'department_access_:id' => 'Access department. - :id'
]
);
self::get('/modules/stripe/department/terminal/readers',
function () {
global $response;
self::requirePermission('modules_stripe_department_terminal_readers_list');
self::requireParameters(['id']);
self::requireType((int)self::fromRequest('id'), self::TYPE_INT());
// Require the id to be above 0
if ((int)self::fromRequest('id') < 1) {
$response->error('Invalid department ID', 400);
}
$user = (new authentication())->get_user();
if ($user) {
// Make sure the user has access to the department
self::requireDepartmentAccess((int)self::fromRequest('id'));
// Make sure the department exists
$department = (new departments_o())->select((int)self::fromRequest('id'));
$department->requireSelected();
(new logs_o())->add('modules_stripe', 'global', 1, 0, 'MODULES_STRIPE', 'User accessed the department terminal readers');
// Get the department terminal readers
$readers = $department->getStripeTerminalReaders();
// Return the result
$response->success($readers);
} else {
(new logs_o())->add('modules_stripe', 'global', 0, 0, 'MODULES_STRIPE', 'User tried to access the department terminal readers without a valid session');
$response->error('Invalid session', 400);
}
},
[
'modules_stripe_department_terminal_readers_list' => 'List all department terminal readers',
'department_access_:id' => 'Access department. - :id'
]
);
}
}
@@ -0,0 +1,178 @@
<?php
namespace routes;
use classes\authentication;
use objects\logs_o;
use objects\notifications_o;
use traits\route_t;
class notificationsRoute
{
use route_t;
public function run(): void
{
$this->get('/notifications', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('list_notifications');
// Check if the user has permission to list all notifications
if (self::hasPermission('list_all_notifications')) {
$this->requirePermission('list_all_notifications');
} else {
$this->requirePermission('list_own_notifications');
}
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, $user->id, 'LIST_OWN_NOTIFICATIONS', 'User accessed the list of notifications');
$notifications = new notifications_o();
// Return the list of notifications
$response->success(
$notifications
->setSearchableFields([
// The fields that can be searched. This would otherwise make it possible to get secret information from the database, simply by searching for it and getting the result count back
'id',
'type',
'user_id',
'data',
'created_at',
'deleted_at',
])
->listObjectsWithPaginationIfSet(
function ($notification) use ($notifications, $user) {
$tmp_notification = [
'id' => (int)$notification['id'],
'type' => (string)$notification['type'],
'user_id' => (int)$notification['user_id'],
'data' => $notification['data'] ? $notifications->decodeData($notification['data']) : null,
'created_at' => (string)$notification['created_at'],
'deleted_at' => $notification['deleted_at'] ? (string)$notification['deleted_at'] : null,
];
return $tmp_notification;
},
$notifications->forceRestrictFilters(
[
// This makes sure that the user can only see their own notifications
'user_id' => $user->id,
]
)
)
);
} else {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, 0, 'LIST_OWN_NOTIFICATIONS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_notifications' => 'List notifications, provided the user has either list_all_notifications, or list_own_notifications permission',
'list_own_notifications' => 'List all notifications for the logged in user',
'list_all_notifications' => 'List all notifications for all users (superuser only)',
]
);
$this->post('/notifications', function () {
// Require the user to be logged in
global $response;
self::requirePermission('add_notification');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the post data
$data = [];
// Check if the required fields are set
self::requireParameters(['type', 'user_id']);
self::requireType((string)self::getParameter('type'), self::TYPE_STRING());
self::requireType((int)self::getParameter('user_id'), self::TYPE_INT());
// If the data is set, check if it is an array
if (self::isParametersSet(['data'])) {
self::requireTypeIn(
(array)self::getParameter('data'),
[
self::TYPE_ARRAY(),
self::TYPE_NULL()
]
);
// Check if the data is an array
if (self::getParameter('data') !== null) {
// JSON decode the data
$data = json_decode(self::getParameter('data'), true);
}
}
// Check if the user_id is set
// Add the notification
(new notifications_o())->add(
(string)self::getParameter('type'),
(int)self::getParameter('user_id'),
(array)$data
);
// Log the incident
(new logs_o())->add('notifications', 'global', 1, $user->id, 'ADD_NOTIFICATION', 'User added a notification');
// Return the list of departments
$response->success(['message' => 'Notification added successfully']);
} else {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, 0, 'ADD_NOTIFICATION', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_notification' => 'Add a notification'
]
);
$this->delete('/notifications', function () {
// Require the user to be logged in
global $response;
self::requirePermission('delete_own_notifications');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Require the parameters
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Get the department category object
$notification = (new notifications_o())->select(self::getParameter('id'));
// Validate the department category object
if (!$notification->exists()) {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, $user->id, 'DELETE_OWN_NOTIFICATIONS', 'User tried to delete a notification that does not exist');
// Return an error
$response->error('Notification does not exist', 400);
}
// Check if the user is the owner of the notification
if ((int)$notification->user_id->value() !== (int)$user->id) {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, $user->id, 'DELETE_OWN_NOTIFICATIONS', 'User tried to delete a notification that does not belong to them');
// Return an error
$response->error('You do not have permission to delete this notification', 403);
}
// Delete the department category
$notification->delete();
// Log the incident
(new logs_o())->add('notifications', 'global', 1, $user->id, 'DELETE_OWN_NOTIFICATIONS', 'User deleted a notification');
// Return the list of departments
$response->success(['message' => 'Notification deleted successfully']);
} else {
// Log the incident
(new logs_o())->add('notifications', 'global', 1, 0, 'DELETE_OWN_NOTIFICATIONS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_own_notifications' => 'Delete a notification'
]
);
}
}
@@ -0,0 +1,224 @@
<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\router;
use objects\collected_order_invoices_o;
use objects\logs_o;
use objects\users_o;
use traits\route_t;
class orderInvoicesRoute
{
use route_t;
public function run(): void
{
global /** @var response $response */
/** @var router $router */
$router, $response;
/** Collected order invoices > GET */
$this->get('/collected-invoices', function () {
global $response;
self::requirePermission('list_collected_invoices');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'LIST_COLLECTED_INVOICES', 'User accessed the list of collected order invoices');
$collected_order_invoices = new collected_order_invoices_o();
// Check if the request contains an ID
if (self::isParametersSet(['id'])) {
self::requireType((int)self::getParameter('id'), self::type_int());
$collected_order_invoices->select((int)self::getParameter('id'));
$collected_order_invoices->requireSelected();
$response->success($collected_order_invoices->asArray());
}
// Define the users
$users = new users_o();
// Define the collected order invoices
$tmp_collected_order_invoices = new collected_order_invoices_o();
// Return the list of collected order invoices
$response->success($collected_order_invoices->listObjectsWithPaginationIfSet(
function ($collected_order_invoice) use ($tmp_collected_order_invoices, $users) {
// Select the orders for each collected order invoice
$tmp_collected_order_invoices->select((int)$collected_order_invoice['id']);
return [
'id' => (int)$collected_order_invoice['id'],
'name' => (string)$collected_order_invoice['name'],
'notes' => (string)$collected_order_invoice['notes'],
'customer_number' => (int)$collected_order_invoice['customer_number'],
'customer_name' => (string)$users->getCustomerName((int)$collected_order_invoice['customer_number']),
'processor' => $collected_order_invoice['processor'] ? (int)$collected_order_invoice['processor'] : null,
'external_id' => (string)$collected_order_invoice['external_id'],
'closed_at' => $collected_order_invoice['closed_at'] ? (string)$collected_order_invoice['closed_at'] : null,
'updated_at' => (string)$collected_order_invoice['updated_at'],
'created_at' => (string)$collected_order_invoice['created_at'],
'orders' => $tmp_collected_order_invoices->getOrders(true),
'total_net_amount' => (float)$tmp_collected_order_invoices->getTotalAmount()
];
},
));
} else {
(new logs_o())->add('orderInvoices', 'global', 0, 0, 'LIST_COLLECTED_INVOICES', 'User tried to access the list of collected order invoices without a valid session');
$response->error('Invalid session', 400);
}
},
[
'list_collected_invoices' => 'List ALL collected order invoices. This is a superuser-only route.'
]
);
/** Collected order invoices > Ready to invoice > GET */
$this->get('/collected-invoices/ready-to-invoice', function () {
global $response;
self::requirePermission('list_collected_invoices');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'LIST_COLLECTED_INVOICES_READY_TO_INVOICE', 'User accessed the list of collected order invoices ready to invoice');
$collected_order_invoices = new collected_order_invoices_o();
// Define the users
$users = new users_o();
// Define the collected order invoices
$tmp_collected_order_invoices = new collected_order_invoices_o();
// Return the list of collected order invoices
$response->success($collected_order_invoices->listObjectsWithPaginationIfSet(
function ($collected_order_invoice) use ($tmp_collected_order_invoices, $users) {
// Select the orders for each collected order invoice
$tmp_collected_order_invoices->select((int)$collected_order_invoice['id']);
return [
'id' => (int)$collected_order_invoice['id'],
'name' => (string)$collected_order_invoice['name'],
'notes' => (string)$collected_order_invoice['notes'],
'customer_number' => (int)$collected_order_invoice['customer_number'],
'customer_name' => (string)$users->getCustomerName((int)$collected_order_invoice['customer_number']),
'processor' => $collected_order_invoice['processor'] ? (int)$collected_order_invoice['processor'] : null,
'external_id' => (string)$collected_order_invoice['external_id'],
'closed_at' => $collected_order_invoice['closed_at'] ? (string)$collected_order_invoice['closed_at'] : null,
'updated_at' => (string)$collected_order_invoice['updated_at'],
'created_at' => (string)$collected_order_invoice['created_at'],
'orders' => $tmp_collected_order_invoices->getOrders(true),
'total_net_amount' => (float)$tmp_collected_order_invoices->getTotalAmount(),
];
},
$collected_order_invoices->forceRestrictFilters(
[
// This makes sure that the user can only see orders from the departments they explicitly have access to
'customer_number' => $collected_order_invoices->listCustomersWithIndividualOrderInvoicing(),
]
)
));
} else {
(new logs_o())->add('orderInvoices', 'global', 0, 0, 'LIST_COLLECTED_INVOICES_READY_TO_INVOICE', 'User tried to access the list of collected order invoices ready to invoice without a valid session');
$response->error('Invalid session', 400);
}
},
[
'list_collected_invoices' => 'List ALL collected order invoices. This is a superuser-only route.'
]
);
/** Collected order invoices > POST */
$this->post('/collected-invoices', function () {
global $response;
self::requirePermission('add_collected_invoice');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'ADD_COLLECTED_INVOICE', 'User added a collected order invoice');
// Require the customer number, and validate its type and length
self::requireParameters(['customer_number']);
self::requireType((int)self::getParameter('customer_number'), 'int');
self::requireMinLength('customer_number', 1);
self::requireMaxLength('customer_number', 10);
// Require the customer number to be above 0
self::requireMinValue((int)self::getParameter('customer_number'), 1);
// Validate the customer number against the database
$customer = (new users_o())->select((int)self::getParameter('customer_number'));
$customer->requireSelected();
// Define the variables
$name = null;
$notes = null;
$processor = null;
// Check if the name is set
if (self::isParametersSet(['name'])) {
self::requireType((string)self::getParameter('name'), 'string');
self::requireMinLength('name', 1);
self::requireMaxLength('name', 255);
$name = (string)self::getParameter('name');
}
// Check if the notes are set
if (self::isParametersSet(['notes'])) {
self::requireType((string)self::getParameter('notes'), 'string');
$notes = (string)self::getParameter('notes');
}
// Check if the processor is set
if (self::isParametersSet(['processor'])) {
self::requireType((int)self::getParameter('processor'), 'int');
$processor = (int)self::getParameter('processor');
}
// Add the collected order invoice
$collected_order_invoices = new collected_order_invoices_o();
$collected_order_invoices->add(
$customer->id,
$name,
$notes,
$processor
);
$response->success($collected_order_invoices->asArray());
} else {
(new logs_o())->add('orderInvoices', 'global', 0, 0, 'ADD_COLLECTED_INVOICE', 'User tried to add a collected order invoice without a valid session');
$response->error('Invalid session', 400);
}
},
[
'add_collected_invoice' => 'Add a collected order invoice. This is a superuser-only route.'
]
);
/** Collected order invoices > E-Conomic > POST */
$this->post('/collected-invoices/economic', function () {
global $response;
self::requirePermission('add_collected_invoice_economic');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'ADD_COLLECTED_INVOICE_ECONOMIC', 'User added a collected order invoice to E-Conomic');
// Require the ID, and validate its type and length
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::type_int());
self::requireMinLength('id', 1);
self::requireMaxLength('id', 10);
// Require the ID to be above 0
self::requireMinValue((int)self::getParameter('id'), 1);
// Validate the ID against the database
$collected_order_invoices = (new collected_order_invoices_o())->select((int)self::getParameter('id'));
$collected_order_invoices->requireSelected();
// Check if the collected order invoice has an external ID
if ($collected_order_invoices->external_id->value() === null) {
// Add the collected order invoice to E-Conomic
$collected_order_invoices->addToEconomic();
$response->success($collected_order_invoices->asArray());
}
// Check if the invoice has been booked
if ($collected_order_invoices->booked_invoice_id->value() !== null) {
$response->error('Invoice has already been booked', 400);
}
// Check if the invoice draft exists in E-Conomic
if ($collected_order_invoices->isDraftExisting()) {
$response->error('Invoice draft already exists in E-Conomic', 400);
}
// Create the invoice in E-Conomic
$collected_order_invoices->addToEconomic(true);
// Return the collected order invoice
$response->success($collected_order_invoices->asArray());
} else {
(new logs_o())->add('orderInvoices', 'global', 0, 0, 'ADD_COLLECTED_INVOICE_ECONOMIC', 'User tried to add a collected order invoice to E-Conomic without a valid session');
$response->error('Invalid session', 400);
}
},
[
'add_collected_invoice_economic' => 'Add a collected order invoice to E-Conomic. This is a superuser-only route.'
]
);
}
}
+48 -6
View File
@@ -33,12 +33,38 @@ class orderItemsRoute
if (!isset($data['quantity'])) {
$response->error('Quantity is required', 400);
}
$related_item_id = null;
// Check if the related_item_id is set
if (self::isParametersSet(['related_item_id'])) {
// Check if the related_item_id is null, if so continue
if ($data['related_item_id'] !== null) {
// Check if the related_item_id is a number
if (!is_numeric($data['related_item_id'])) {
$response->error('Related item ID must be a number', 400);
}
$related_item_id = (int)$data['related_item_id'];
}
}
$notes = null;
// Check if the notes is set
if (self::isParametersSet(['notes'])) {
// Check if the notes is null, if so continue
if (self::getParameter('notes') !== null) {
// Check if the notes is a string
if (!is_string(self::getParameter('notes'))) {
$response->error('Notes must be a string', 400);
}
$notes = (string)self::getParameter('notes');
}
}
// Add the order item to the order This is done individually, to make the notes to the individual order items possible
(new order_items_o())->addItemToOrder((int)$data['order_id'], (int)$data['product_id'], (int)$user->id, (int)$data['quantity']);
$order_items = (new order_items_o());
// Add the order item to the order
$order_items->addItemToOrder((int)$data['order_id'], (int)$data['product_id'], (int)$user->id, (int)$data['quantity'], $related_item_id, $notes);
// Return the list of departments
$response->success(
['message' => 'Order items added']
$order_items->getItemAsArray()
);
} else {
// Log the incident
@@ -46,7 +72,11 @@ class orderItemsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_order_items' => 'Add order items'
]
);
$this->get('/order/items', function () {
// Require the user to be logged in
@@ -84,7 +114,11 @@ class orderItemsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_order_items' => 'List all order items'
]
);
$this->delete('/order/items', function () {
// Require the user to be logged in
@@ -112,7 +146,11 @@ class orderItemsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'delete_order_items' => 'Delete order items'
]
);
$this->put('/order/items', function () {
// Require the user to be logged in
@@ -154,6 +192,10 @@ class orderItemsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_order_items' => 'Edit order items'
]
);
}
}
+21 -6
View File
@@ -3,6 +3,7 @@
namespace routes;
use classes\authentication;
use classes\response;
use objects\logs_o;
use objects\orders_o;
use traits\route_t;
@@ -15,25 +16,30 @@ class orderRoute
{
$this->get('/order', function () {
// Require the user to be logged in
global $response;
global /** @var response $response */
$response;
$this->requirePermission('fetch_order');
// Get the user object
$user = (new authentication())->get_user();
$user = $response->get_user();
// Check if the request was successful
if ($user) {
// Make sure the order id is set
if (!(int)$this->fromRequest('id')) {
$response->error('Order id is required', 400);
}
$orders_o = new orders_o();
// Make sure the order exists
if (!(new orders_o())->getOrderById($this->fromRequest('id'))->exists()) {
if (!$orders_o->getOrderById($this->fromRequest('id'))->exists()) {
$response->error('Order not found', 400);
}
// Log the incident
(new logs_o())->add('orders', 'global', 1, $user->id, 'FETCH_ORDER', 'Successfully fetched order');
// Check if the user has access to the department the order is in
$this->requirePermission('department_access_' . $orders_o->department_id->value());
// Return the list of departments
$response->success(
(new orders_o())->getOrderById($this->fromRequest('id'))->includeIncludes()->asArray()
$orders_o->getOrderById($this->fromRequest('id'))->includeIncludes()->asArray()
);
} else {
// Log the incident
@@ -41,7 +47,12 @@ class orderRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'fetch_order' => 'Fetch any order, provided the user has access to the department the order is in',
'department_access_:id' => 'Access to the department the order is in'
]
);
$this->put('/order', function () {
@@ -77,6 +88,10 @@ class orderRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'update_order' => 'Update order'
]
);
}
}
+50 -6
View File
@@ -4,10 +4,12 @@ namespace routes;
use classes\authentication;
use classes\response;
use objects\collected_order_invoices_o;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_orders_o;
use objects\users_o;
use traits\route_t;
@@ -29,17 +31,40 @@ class ordersRoute
(new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');
// Create economic_module_orders object
$economic_module_orders = new economic_module_orders();
$orders = new orders_o();
// Return the list of departments
$response->success(
(new orders_o())->listObjectsWithPaginationIfSet(
$orders->listObjectsWithPaginationIfSet(
function ($order) {
// Add the invoice status to the order
$order['economic_invoice_module'] = (new economic_module_orders())->getByOrderId($order['id'])->asArray();
// Add the total amount to the order
$order['total_net_amount'] = (new orders_o())->select($order['id'])->getNetAmount();
// Add the stripe status to the order
$stripe_module_orders = (new stripe_module_orders_o())->select($order['id']);
if ($stripe_module_orders->exists()) {
$order['stripe_invoice_module'] = $stripe_module_orders->asArray();
}
// If the invoice collection is set, add it to the order
if (!empty($order['invoice_collection_id'])) {
$order['invoice_collection'] = [
'id' => $order['invoice_collection_id'],
'closed_at' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->closed_at->value(),
'booked_invoice_id' => (new collected_order_invoices_o())->select($order['invoice_collection_id'])->booked_invoice_id->value() ?? null,
'processor' => (int)(new collected_order_invoices_o())->select($order['invoice_collection_id'])->processor->value() ?? null,
];
}
// Add the customer name to the order
$order['customer_name'] = (new users_o())->getCustomerName($order['customer_id']);
/** @var array $order */
return $order;
}
},
$orders->forceRestrictFilters(
[
// This makes sure that the user can only see orders from the departments they explicitly have access to
'department_id' => $user->getGroup()->getDepartments(),
]
)
)
);
} else {
@@ -48,7 +73,11 @@ class ordersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_orders' => 'List all orders'
]
);
$this->post('/orders', function () {
@@ -94,7 +123,11 @@ class ordersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_order' => 'Add an order'
]
);
$this->put('/orders', function () {
// Require the user to be logged in
@@ -146,7 +179,11 @@ class ordersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_order' => 'Edit an order'
]
);
$this->delete('/orders', function () {
// Require the user to be logged in
@@ -180,7 +217,11 @@ class ordersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'delete_order' => 'Delete an order'
]
);
}
/**
@@ -193,6 +234,9 @@ class ordersRoute
if (!isset($data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if ((int)$data['customer_id'] < 1 || !is_numeric((int)$data['customer_id'])) {
$response->error('Customer ID is required', 400);
}
if (!isset($data['department_id'])) {
$response->error('Department ID is required', 400);
}
@@ -0,0 +1,57 @@
<?php
namespace routes;
use classes\authentication;
use classes\response;
use classes\router;
use objects\logs_o;
use traits\route_t;
class permissionsRoute
{
use route_t;
public function run(): void
{
global /** @var response $response */
/** @var router $router */
$router, $response;
/** Permissions > List */
$this->get('/permissions', function () {
global $response, $router;
$this->requirePermission('permissions_list');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('permissions', 'global', 1, 0, 'PERMISSIONS', 'User accessed the permissions list');
$response->success($router->getPermissions());
} else {
$response->error('Invalid session', 400);
}
},
[
'permissions_list' => 'List all permissions'
]
);
/** Permissions > User > List */
self::get('/user/permissions', function () {
global $response;
self::requirePermission('permissions_list_own');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('permissions', 'global', 1, 0, 'PERMISSIONS', 'User accessed the user permissions list');
$response->success((object)$user->getGroup()->getPermissions());
} else {
(new logs_o())->add('permissions', 'global', 0, 0, 'PERMISSIONS', 'User tried to access the user permissions list without a valid session');
$response->error('Invalid session', 400);
}
},
[
'permissions_list_own' => 'List all user permissions'
]
);
}
}
@@ -3,6 +3,7 @@
namespace routes;
use classes\authentication;
use objects\departments_o;
use objects\logs_o;
use objects\plate_scanners_o;
use traits\route_t;
@@ -41,7 +42,11 @@ class plateScannersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_number_plate_scanners' => 'List all number plate scanners'
]
);
$this->post('/numberplatescanners', function () {
// Require the user to be logged in
@@ -75,7 +80,11 @@ class plateScannersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_number_plate_scanner' => 'Add a number plate scanner'
]
);
$this->put('/numberplatescanners', function () {
// Require the user to be logged in
@@ -112,6 +121,59 @@ class plateScannersRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_number_plate_scanner' => 'Edit a number plate scanner'
]
);
self::get('/department/numberplatescanners', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_department_number_plate_scanners');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Get the department ID
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::type_int());
self::requireParameterIntPositive((int)self::getParameter('id'), 'id');
// Check if the user has access to the department
self::requireDepartmentAccess((int)self::getParameter('id'));
// Check if the department exists
$department = (new departments_o())->select((int)self::getParameter('id'));
$department->requireSelected();
// Log the incident
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'LIST_DEPARTMENT_NUMBER_PLATE_SCANNERS', 'Successfully listed department number plate scanners');
// Get the department scanners.
$result = (new plate_scanners_o())
->getFieldsWhere([
'department_id' => (int)self::getParameter('id')
], [
'id',
'name',
'notes'
]);
// Parse the result
foreach ( $result as $key => $value ) {
$result[$key]['id'] = (int)$value['id'];
}
// Return the list of plate scanners
$response->success(
$result
);
} else {
// Log the incident
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'LIST_DEPARTMENT_NUMBER_PLATE_SCANNERS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_department_number_plate_scanners' => 'List all number plate scanners in a department',
'department_access_:id' => 'Access department :id'
]
);
}
}
+42 -4
View File
@@ -67,7 +67,9 @@ class plateScansRoute
// Return an error
$response->error('Invalid session', 400);
}
});
}, [
'list_number_plate_scans_department' => 'List all number plate scans for a department'
]);
$this->get('/numberplatescans', function () {
// Require the user to be logged in
@@ -78,17 +80,53 @@ class plateScansRoute
// Check if the request was successful
if ($user) {
// Get the number plate scans
$number_plate_scans = (new plate_scans_o())->listObjectsWithPaginationIfSet();
$number_plate_scans = (new plate_scans_o());
$result = $number_plate_scans->listObjectsWithPaginationIfSet(null,
$number_plate_scans->forceRestrictFilters(
[
'department_id' => $user->getGroup()->getDepartments()
]
)
);
// Log the incident
(new logs_o())->add('numberplatescans', 'global', 1, $user->id, 'LIST_NUMBER_PLATE_SCANS', 'Successfully listed number plate scans');
// Return the number plate scans
$response->success($number_plate_scans);
$response->success($result);
} else {
// Log the incident
(new logs_o())->add('numberplatescans', 'global', 1, 0, 'LIST_NUMBER_PLATE_SCANS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
});
}, [
'list_number_plate_scans' => 'List all number plate scans, provided the user has access to the department the number plate scan is in',
'department_access_:id' => 'Access to the department the number plate scan is in'
]);
self::get('/numberplatescans/post', function () {
/**
* This is here because the post method is not supported by the software some of the cameras are running on.
*/
global $response;
self::requirePlateScannerAuth();
$plate_scanner = (new authentication())->get_plate_scanner();
self::requireParameters(['plate', 'token']);
// Validate the plate
self::requireType('plate', 'string');
self::requireMinLength('plate', 1);
self::requireMaxLength('plate', 10);
// Validate the token
self::requireType('token', 'string');
self::requireMinLength('token', 1);
self::requireMaxLength('token', 100);
// Add the number plate scanner
(new plate_scans_o())->add($plate_scanner->id, self::getParameter('plate'));
// Log the incident
(new logs_o())->add('numberplatescans', $plate_scanner->department_id->value(), 1, 0, 'ADD_NUMBER_PLATE_SCANS', 'Successfully added a number plate scan: ' . self::getParameter('plate'));
// Return a success message
$response->success(['message' => 'License plate scan recorded.', 'plate' => self::getParameter('plate'), 'scanner' => $plate_scanner->name->value()], 201);
}, [
'add_number_plate_scan' => 'Add a number plate scan'
]);
}
}
@@ -32,6 +32,11 @@ class productOptionsRoute
'id',
'product_id',
'option_id',
'name',
'min',
'max',
'created_at',
'updated_at'
])
->listObjectsWithPaginationIfSet(
function ($option) use ($user) {
@@ -40,6 +45,9 @@ class productOptionsRoute
'id' => (int)$option['id'],
'product_id' => (int)$option['product_id'],
'option_id' => (int)$option['option_id'],
'name' => (string)$option['name'],
'min' => $option['min'] === null ? null : (int)$option['min'],
'max' => $option['max'] === null ? null : (int)$option['max'],
'created_at' => (string)$option['created_at'],
'updated_at' => (string)$option['updated_at'],
];
@@ -52,7 +60,11 @@ class productOptionsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_product_options' => 'List all product options'
]
);
$this->post('/product/options', function () {
// Require the user to be logged in
@@ -86,7 +98,11 @@ class productOptionsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_product_option' => 'Add a product option'
]
);
$this->put('/product/options', function () {
// Require the user to be logged in
@@ -97,15 +113,37 @@ class productOptionsRoute
// Check if the request was successful
if ($user) {
// Check if the required parameters are set
self::requireParameters(['id', 'name']);
self::requireParameters(['id']);
// Check if the parameters are of the correct type
self::requireType(self::getParameter('id'), self::TYPE_INT());
// Create the object
$product_options = new product_options_o();
// Select the object
$product_options->select(self::getParameter('id'));
// Set the name
$product_options->set_name(self::getParameter('name'));
// Check what the user wants to edit
// Option name
if (self::isParametersSet(['name'])) {
// Check if the parameters are of the correct type
self::requireType(self::getParameter('name'), self::TYPE_STRING());
// Set the name
$product_options->set_name(self::getParameter('name'));
}
// Option min
if (self::isParametersSet(['min'])) {
// Check if the parameters are of the correct type (Or NULL)
self::requireTypeIn(self::getParameter('min'), [self::TYPE_INT(), self::TYPE_NULL()]);
// Set the min value
$product_options->set_min(self::getParameter('min'));
}
// Option max
if (self::isParametersSet(['max'])) {
// Check if the parameters are of the correct type
self::requireType(self::getParameter('max'), self::TYPE_INT());
// Set the max value
$product_options->set_max(self::getParameter('max'));
}
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'EDIT_PRODUCT_OPTION', 'User edited a product option');
// Return the object
@@ -118,6 +156,48 @@ class productOptionsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_product_option' => 'Edit a product option'
]
);
$this->delete('/product/options', function () {
// Require the user to be logged in
global $response;
$this->requirePermission('delete_product_option');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required parameters are set
self::requireParameters(['id']);
// Check if the parameters are of the correct type
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Require the minimum value to be above 0
self::requireMinValue((int)self::getParameter('id'), 1);
// Create the object
$product_options = new product_options_o();
// Select the object
$product_options->select((int)self::getParameter('id'));
// Delete the object
$product_options->delete();
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'DELETE_PRODUCT_OPTION', 'User deleted a product option');
// Return the object
$response->success(
'Product option deleted'
);
} else {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, 0, 'DELETE_PRODUCT_OPTION', 'User tried to delete a product option without being logged in');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_product_option' => 'Delete a product option'
]
);
}
}
+34 -5
View File
@@ -25,7 +25,8 @@ class productsRoute
'category' => (int)$product['category'],
'piktogram' => (string)$product['piktogram'],
'economic_product_id' => (int)$product['economic_product_id'],
'apply_category_discount' => (int)$product['apply_category_discount'],
'apply_category_discount' => (boolean)$product['apply_category_discount'],
'requires_note' => (boolean)$product['requires_note'],
'created_at' => (string)$product['created_at'],
'updated_at' => (string)$product['updated_at'],
'addons' => (new product_options_o())->getProductOptions($product['id'])
@@ -39,6 +40,17 @@ class productsRoute
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the id is set in the request
if (self::isParametersSet(['id'])) {
// Log the incident
(new logs_o())->add('products', 'global', 1, $user->id, 'LIST_PRODUCTS', 'Successfully listed product with id ' . $response->getRequestParameter('id'));
// Return the product
$response->success(
parseProduct(
(new products_o())->select((int)self::getParameter('id'))->asArray()
)
);
}
// Check if the category is set in the request
$data = $_GET ?? [];
// Check if the category is set
@@ -53,7 +65,9 @@ class productsRoute
$products = (new products_o())->applyDepartmentPricing((array)$products, (int)$data['department_id']);
}
$response->success(
$products
array_map(function ($product) {
return parseProduct($product);
}, $products)
);
}
// Log the incident
@@ -81,7 +95,11 @@ class productsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'list_products' => 'List all products'
]
);
$this->post('/products', function () {
// Require the user to be logged in
@@ -124,7 +142,11 @@ class productsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'add_product' => 'Add a product'
]
);
$this->put('/products', function () {
// Require the user to be logged in
@@ -164,6 +186,9 @@ class productsRoute
if (self::isParametersSet(['apply_category_discount'])) {
$product->apply_category_discount->set($response->getRequestParameter('apply_category_discount') ? 1 : 0);
}
if (self::isParametersSet(['requires_note'])) {
$product->requires_note->set($response->getRequestParameter('requires_note') ? 1 : 0);
}
(new logs_o())->add('products', 'global', 1, $user->id, 'EDIT_PRODUCT', 'Product id: ' . $id);
// Return a success message
$response->success(['message' => 'Product edited successfully']);
@@ -173,6 +198,10 @@ class productsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'edit_product' => 'Edit a product'
]
);
}
}
+185
View File
@@ -0,0 +1,185 @@
<?php
namespace routes;
use classes\authentication;
use objects\groups_o;
use objects\logs_o;
use traits\route_t;
class rolesRoute
{
use route_t;
public function run(): void
{
self::get('/roles', function () {
// Require the user to be logged in
global $response;
self::requirePermission('list_roles');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('roles', 'global', 1, 0, 'ROLES', 'User accessed the roles list');
$groups = new groups_o();
// Check if the id parameter is set
if (self::isParametersSet(['id'])) {
self::requireType((int)self::getParameter('id'), self::type_int());
$groups->select((int)self::getParameter('id'));
$groups->requireSelected();
$response->success($groups->asArray());
}
// Return the list of roles
$response->success($groups->listObjectsWithPaginationIfSet(
function ($group) {
return [
'id' => (int)$group['id'],
'name' => (string)$group['name'],
'description' => (string)$group['description'],
'created_at' => (string)$group['created_at'],
'permissions' => (new groups_o())->select((int)$group['id'])->getPermissions()
];
}
));
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to access the roles list without a valid session');
$response->error('Invalid session', 400);
}
},
[
'list_roles' => 'List all roles'
]
);
self::post('/roles', function () {
// Require the user to be logged in
global $response;
self::requirePermission('add_role');
$user = (new authentication())->get_user();
if ($user) {
(new logs_o())->add('roles', 'global', 1, 0, 'ROLES', 'User added a role');
self::requireParameters(['name', 'description']);
self::requireType((string)self::getParameter('name'), 'string');
self::requireType((string)self::getParameter('description'), 'string');
$group = new groups_o();
$group->add(
(string)self::getParameter('name'),
(string)self::getParameter('description')
);
$response->success($group->asArray());
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to add a role without a valid session');
$response->error('Invalid session', 400);
}
},
[
'add_role' => 'Add a role'
]
);
self::put('/roles', function () {
// Require the user to be logged in
global $response;
self::requirePermission('edit_role');
$user = (new authentication())->get_user();
if ($user) {
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::type_int());
$group = new groups_o();
$group->select((int)self::getParameter('id'));
$group->requireSelected();
// Check which parameters are set and update them
if (self::isParametersSet(['name'])) {
$group->name->set((string)self::getParameter('name'));
}
if (self::isParametersSet(['description'])) {
$group->description->set((string)self::getParameter('description'));
}
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User edited a role');
$response->success($group->asArray());
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to edit a role without a valid session');
$response->error('Invalid session', 400);
}
},
[
'edit_role' => 'Edit a role'
]
);
self::post('/roles/permissions', function () {
// Require the user to be logged in
global $response;
self::requirePermission('add_role_permission');
$user = (new authentication())->get_user();
if ($user) {
self::requireParameters(['group_id', 'permission_id']);
self::requireType((int)self::getParameter('group_id'), self::type_int());
self::requireType((string)self::getParameter('permission_id'), 'string');
$group = new groups_o();
$group->select((int)self::getParameter('group_id'));
$group->requireSelected();
$group->addPermission((string)self::getParameter('permission_id'));
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User added a permission to a role');
$response->success($group->asArray());
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to add a permission to a role without a valid session');
$response->error('Invalid session', 400);
}
},
[
'add_role_permission' => 'Add a permission to a role'
]
);
self::delete('/roles/permissions', function () {
// Require the user to be logged in
global $response;
self::requirePermission('delete_role_permission');
$user = (new authentication())->get_user();
if ($user) {
self::requireParameters(['group_id', 'permission_id']);
self::requireType((int)self::getParameter('group_id'), self::type_int());
self::requireMinLength('group_id', 1);
self::requireType((string)self::getParameter('permission_id'), 'string');
self::requireMinLength('permission_id', 1);
$group = new groups_o();
$group->select((int)self::getParameter('group_id'));
$group->requireSelected();
$group->removePermission((string)self::getParameter('permission_id'));
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User deleted a permission from a role');
$response->success($group->asArray());
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to delete a permission from a role without a valid session');
$response->error('Invalid session', 400);
}
},
[
'delete_role_permission' => 'Remove a permission from a role'
]
);
self::post('/roles/clone', function () {
// Require the user to be logged in
global $response;
self::requirePermission('clone_role');
$user = (new authentication())->get_user();
if ($user) {
self::requireParameters(['id']);
self::requireType((int)self::getParameter('id'), self::type_int());
$group = new groups_o();
$group->select((int)self::getParameter('id'));
$group->requireSelected();
$group->clone($group->name->value() . ' - Klon', (string)$group->description->value());
(new logs_o())->add('roles', 'global', 1, $user->id, 'ROLES', 'User cloned a role');
$response->success($group->asArray());
} else {
(new logs_o())->add('roles', 'global', 0, 0, 'ROLES', 'User tried to clone a role without a valid session');
$response->error('Invalid session', 400);
}
},
[
'clone_role' => 'Clone a role'
]
);
}
}
+5 -1
View File
@@ -30,6 +30,10 @@ class sessionRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'fetch_session' => 'Fetch session'
]
);
}
}
+55 -11
View File
@@ -42,7 +42,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_bookings_new' => 'List all new bookings'
]
);
/** Statistics >> Orders >> New orders */
$this->get('/statistics/orders/new', function () {
@@ -72,7 +76,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_orders_new' => 'List all new orders'
]
);
/** Statistics >> Economic >> Total income */
$this->get('/statistics/economic/totals', function () {
@@ -102,7 +110,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income' => 'Get total income'
]
);
/** Statistics >> Economic >> Department sent invoice totals */
$this->get('/statistics/economic/totals/department_sent_invoice_totals', function () {
@@ -132,7 +144,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_department_sent_invoice_totals' => 'Get department sent invoice totals'
]
);
/** Statistics >> Economic >> Department draft invoice totals */
$this->get('/statistics/economic/totals/department_draft_invoice_totals', function () {
@@ -162,7 +178,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_department_draft_invoice_totals' => 'Get department draft invoice totals'
]
);
/** Statistics >> Economic >> Total income today */
$this->get('/statistics/income/today', function () {
@@ -191,7 +211,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_today' => 'Get total income today'
]
);
/** Statistics >> Economic >> Total income yesterday */
$this->get('/statistics/income/yesterday', function () {
@@ -220,7 +244,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_yesterday' => 'Get total income yesterday'
]
);
/** Statistics >> Economic >> Total income this month */
$this->get('/statistics/income/this-month', function () {
@@ -249,7 +277,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_this_month' => 'Get total income this month'
]
);
/** Statistics >> Economic >> Total income last month */
$this->get('/statistics/income/last-month', function () {
@@ -278,7 +310,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_last_month' => 'Get total income last month'
]
);
/** Statistics >> Economic >> Total income this year */
$this->get('/statistics/income/this-year', function () {
@@ -307,7 +343,11 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_this_year' => 'Get total income this year'
]
);
$this->get('/statistics/income/departments', function () {
// Require the user to be logged in
@@ -349,6 +389,10 @@ class statisticsRoute
// Return an error
$response->error('Invalid session', 400);
}
});
},
[
'statistics_economic_income_today_departments' => 'Get total income today by departments'
]
);
}
}

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