@@ -160,6 +160,24 @@ class response implements response_i
|
||||
return $data[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all request parameters
|
||||
* @return array
|
||||
*/
|
||||
public function getAllRequestParameters(): array
|
||||
{
|
||||
$data = [];
|
||||
// Get the request data if the method is POST, PUT or PATCH
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
}
|
||||
// Get the request data if the method is GET, DELETE or OPTIONS
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
$data = $_GET;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks if a request parameter is set.
|
||||
* This is different from getRequestParameter because this function allows for empty & null values.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace classes;
|
||||
|
||||
require_once WD . '/modules/selfserve/selfserve_c.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
|
||||
use Exception;
|
||||
use interfaces\universal_module_i;
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\selfserve_c;
|
||||
|
||||
class selfserve implements universal_module_i
|
||||
{
|
||||
/**
|
||||
* The configuration of the module
|
||||
* @var selfserve_c
|
||||
*/
|
||||
public selfserve_c $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = new selfserve_c();
|
||||
}
|
||||
|
||||
public static function getInstance(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function lane(int $lane_id): selfserve_lane
|
||||
{
|
||||
return new selfserve_lane($lane_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws Exception If the module is not enabled
|
||||
*/
|
||||
public function requireModuleEnabled(): void
|
||||
{
|
||||
if (!(bool)$this->config->enabled->getVariableValue()) {
|
||||
throw new Exception('selfserve module is not enabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@ require_once 'classes/attachments.php';
|
||||
require_once 'classes/attachment_store.php';
|
||||
require_once 'classes/virkdata.php';
|
||||
require_once 'classes/shelly.php';
|
||||
require_once 'classes/selfserve.php';
|
||||
|
||||
/**
|
||||
* Modules
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace attachments\helpers;
|
||||
|
||||
class attachment_content
|
||||
{
|
||||
const OTHER_TYPE_WASH_CERTIFICATE = 'WASH_CERTIFICATE';
|
||||
public ?string $image; // Used to store the attachment object name, in the attachment store.
|
||||
public ?string $document; // Used to store the attachment object name, in the attachment store.
|
||||
public ?attachment_relation $relation; // Used to store the attachment relation object.
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\classes;
|
||||
require_once WD . '/modules/selfserve/interfaces/selfserve_lane_i.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_cache_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_mode_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_state_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_status_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_port_controller_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_command_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_timer_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_license_plate_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_customer_number_t.php';
|
||||
require_once WD . '/modules/selfserve/traits/selfserve_lane_invoice_t.php';
|
||||
|
||||
use modules\selfserve\interfaces\selfserve_lane_i;
|
||||
use modules\selfserve\traits\selfserve_lane_cache_t;
|
||||
use modules\selfserve\traits\selfserve_lane_command_t;
|
||||
use modules\selfserve\traits\selfserve_lane_customer_number_t;
|
||||
use modules\selfserve\traits\selfserve_lane_invoice_t;
|
||||
use modules\selfserve\traits\selfserve_lane_license_plate_t;
|
||||
use modules\selfserve\traits\selfserve_lane_mode_t;
|
||||
use modules\selfserve\traits\selfserve_lane_port_controller_t;
|
||||
use modules\selfserve\traits\selfserve_lane_state_t;
|
||||
use modules\selfserve\traits\selfserve_lane_status_t;
|
||||
use modules\selfserve\traits\selfserve_lane_timer_t;
|
||||
|
||||
class selfserve_lane implements selfserve_lane_i
|
||||
{
|
||||
use selfserve_lane_status_t,
|
||||
selfserve_lane_mode_t,
|
||||
selfserve_lane_state_t,
|
||||
selfserve_lane_cache_t,
|
||||
selfserve_lane_port_controller_t,
|
||||
selfserve_lane_command_t,
|
||||
selfserve_lane_timer_t,
|
||||
selfserve_lane_license_plate_t,
|
||||
selfserve_lane_customer_number_t,
|
||||
selfserve_lane_invoice_t;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(int $lane_id)
|
||||
{
|
||||
$this->id = $lane_id;
|
||||
$this->department_lane = (new \objects\department_lanes_o())->select($lane_id);
|
||||
// Raise exception if department lane not found
|
||||
if (!$this->department_lane->exists()) {
|
||||
throw new \Exception("Department lane not found for lane ID {$lane_id}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The lane ID
|
||||
* @var int $id
|
||||
* @see \objects\department_lanes_o
|
||||
*/
|
||||
public int $id;
|
||||
/**
|
||||
* Bypass customer number validation flag
|
||||
* @var bool $bypass_customer_number_validation
|
||||
*/
|
||||
public bool $bypass_customer_number_validation = false;
|
||||
/**
|
||||
* Department lane object
|
||||
* @var \objects\department_lanes_o|null $department_lane
|
||||
*/
|
||||
public ?\objects\department_lanes_o $department_lane = null;
|
||||
|
||||
public function isBypassCustomerNumberValidation(): bool
|
||||
{
|
||||
return $this->bypass_customer_number_validation;
|
||||
}
|
||||
|
||||
public function setBypassCustomerNumberValidation(bool $bypass): void
|
||||
{
|
||||
$this->bypass_customer_number_validation = $bypass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\classes;
|
||||
|
||||
class selfserve_lane_command_arguments
|
||||
{
|
||||
public ?string $license_plate = null;
|
||||
public ?int $customer_number = null;
|
||||
|
||||
/**
|
||||
* Set the license plate of the vehicle currently in the lane
|
||||
* @param string|null $license_plate
|
||||
* @return $this
|
||||
*/
|
||||
public function setLicensePlate(?string $license_plate): self
|
||||
{
|
||||
$this->license_plate = $license_plate ? strtoupper(trim($license_plate)) : null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCustomerNumber(?int $customer_number): self
|
||||
{
|
||||
$this->customer_number = $customer_number;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setParameters($params): self
|
||||
{
|
||||
if (is_array($params)) {
|
||||
if (array_key_exists('license_plate', $params)) {
|
||||
$this->setLicensePlate($params['license_plate']);
|
||||
}
|
||||
if (array_key_exists('customer_number', $params)) {
|
||||
$this->setCustomerNumber($params['customer_number']);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\config;
|
||||
|
||||
use Exception;
|
||||
use traits\module_config_variable;
|
||||
|
||||
class selfserve_enabled_c
|
||||
{
|
||||
use module_config_variable;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::setupConfigVariable(
|
||||
'selfserve',
|
||||
'enabled',
|
||||
'bool',
|
||||
true,
|
||||
null,
|
||||
'Whether the selfserve module is enabled or not',
|
||||
'1',
|
||||
false,
|
||||
'false'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\config;
|
||||
|
||||
use Exception;
|
||||
use traits\module_config_variable;
|
||||
|
||||
class selfserve_minute_product_c
|
||||
{
|
||||
use module_config_variable;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::setupConfigVariable(
|
||||
'selfserve',
|
||||
'minute_product',
|
||||
'int',
|
||||
true,
|
||||
null,
|
||||
'The product ID used for minute-based self-serve billing',
|
||||
'1',
|
||||
false,
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\helpers;
|
||||
|
||||
enum selfserve_lane_command
|
||||
{
|
||||
case START; // command to start the lane
|
||||
case STOP; // command to stop the lane
|
||||
case RESET; // command to reset the lane
|
||||
|
||||
public static function tryFrom(string $commandParam): ?selfserve_lane_command
|
||||
{
|
||||
return match (strtoupper($commandParam)) {
|
||||
'START' => selfserve_lane_command::START,
|
||||
'STOP' => selfserve_lane_command::STOP,
|
||||
'RESET' => selfserve_lane_command::RESET,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
public function equals(selfserve_lane_command $command): bool
|
||||
{
|
||||
return $this === $command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\helpers;
|
||||
|
||||
enum selfserve_lane_mode
|
||||
{
|
||||
case MANUAL; // The wash is in manual mode (Machine is disabled)
|
||||
case AUTOMATIC; // The wash is in automatic mode (Machine is enabled, provided the lane supports it)
|
||||
|
||||
public function equals(selfserve_lane_mode $mode): bool
|
||||
{
|
||||
return $this === $mode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\helpers;
|
||||
|
||||
enum selfserve_lane_port
|
||||
{
|
||||
|
||||
case ENTRANCE;
|
||||
case EXIT;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\helpers;
|
||||
|
||||
enum selfserve_lane_state
|
||||
{
|
||||
case ENTRANCE_PORT_OPEN_QUEUED; // The wash is queued
|
||||
case ENTRANCE_PORT_OPEN; // The entrance gate is open
|
||||
case MACHINE_RELAY_ON_QUEUED; // The machine relay is queued to turn on. (applies to supported lanes)
|
||||
case MACHINE_RELAY_ON; // The machine relay is on. (applies to supported lanes)
|
||||
case MACHINE_RELAY_OFF_QUEUED; // The machine relay is queued to turn off. (applies to supported lanes)
|
||||
case MACHINE_RELAY_OFF; // The machine relay is off. (applies to supported lanes)
|
||||
case IN_WASH; // The vehicle is in the wash
|
||||
case EXIT_PORT_OPEN_QUEUED; // The exit gate is queued
|
||||
case EXIT_PORT_OPEN; // The exit gate is open
|
||||
case IDLE; // The lane is idle
|
||||
case FAULT; // The lane is in a fault state
|
||||
case MAINTENANCE; // The lane is under maintenance
|
||||
case CLOSED; // The lane is closed
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\helpers;
|
||||
|
||||
enum selfserve_lane_status
|
||||
{
|
||||
|
||||
case OCCUPIED; // the lane is occupied
|
||||
case FAULT; // the lane is in a fault state
|
||||
case AVAILABLE; // lane is available
|
||||
case MAINTENANCE; // lane is under maintenance
|
||||
case CLOSED; // the lane is closed
|
||||
case RESERVED; // the lane is reserved
|
||||
|
||||
public function equals(selfserve_lane_status $status): bool
|
||||
{
|
||||
return $this === $status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\interfaces;
|
||||
|
||||
|
||||
interface selfserve_lane_i
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve;
|
||||
require_once WD . '/modules/selfserve/config/selfserve_enabled_c.php';
|
||||
require_once WD . '/modules/selfserve/config/selfserve_minute_product_c.php';
|
||||
|
||||
use modules\selfserve\config\selfserve_enabled_c;
|
||||
use modules\selfserve\config\selfserve_minute_product_c;
|
||||
use traits\module_config_t;
|
||||
|
||||
class selfserve_c
|
||||
{
|
||||
use module_config_t;
|
||||
|
||||
/**
|
||||
* The status of the module
|
||||
* @var selfserve_enabled_c $enabled
|
||||
*/
|
||||
public selfserve_enabled_c $enabled;
|
||||
/**
|
||||
* The product ID used for minute-based self-serve billing
|
||||
* @var selfserve_minute_product_c $minute_product
|
||||
*/
|
||||
public selfserve_minute_product_c $minute_product;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setupConfig('selfserve');
|
||||
$this->allowUpdate([
|
||||
selfserve_enabled_c::class,
|
||||
selfserve_minute_product_c::class
|
||||
]);
|
||||
$this->enabled = new selfserve_enabled_c();
|
||||
$this->minute_product = new selfserve_minute_product_c();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
|
||||
trait selfserve_lane_cache_t
|
||||
{
|
||||
const CACHE_SELFSERVE_PREFIX = 'selfserve_lane_';
|
||||
const CACHE_SELFSERVE_LANE_KEY_STATUS = self::CACHE_SELFSERVE_PREFIX . 'status';
|
||||
const CACHE_SELFSERVE_LANE_KEY_STATE = self::CACHE_SELFSERVE_PREFIX . 'state';
|
||||
const CACHE_SELFSERVE_LANE_KEY_MODE = self::CACHE_SELFSERVE_PREFIX . 'mode';
|
||||
const CACHE_SELFSERVE_LANE_KEY_WASH_START_TIME = self::CACHE_SELFSERVE_PREFIX . 'wash_start_time';
|
||||
const CACHE_SELFSERVE_LANE_KEY_CUSTOMER_NUMBER = self::CACHE_SELFSERVE_PREFIX . 'customer_number';
|
||||
const CACHE_SELFSERVE_LANE_KEY_LICENSE_PLATE = self::CACHE_SELFSERVE_PREFIX . 'license_plate';
|
||||
|
||||
|
||||
/**
|
||||
* Get the cache key for the lane
|
||||
* @param int $laneId The ID of the lane
|
||||
* @param string $property The property to get the cache key for (self::CACHE_SELFSERVE_LANE_KEY_STATUS, self::CACHE_SELFSERVE_LANE_KEY_STATE, self::CACHE_SELFSERVE_LANE_KEY_MODE, ...)
|
||||
* @return string The cache key for the lane
|
||||
*/
|
||||
protected function getLaneCacheKey(int $laneId, string $property): string
|
||||
{
|
||||
return $property . '_' . $laneId;
|
||||
}
|
||||
/**
|
||||
* Set the cache for a lane property
|
||||
* @param int $laneId The ID of the lane
|
||||
* @param string $property The property to set the cache for (self::CACHE_SELFSERVE_LANE_KEY_STATUS, self::CACHE_SELFSERVE_LANE_KEY_STATE, self::CACHE_SELFSERVE_LANE_KEY_MODE, ...)
|
||||
* @param mixed $value The value to set in the cache
|
||||
* @return $this;
|
||||
*/
|
||||
public function setLaneCache(int $laneId, string $property, mixed $value): self
|
||||
{
|
||||
redis->set($this->getLaneCacheKey($laneId, $property), serialize($value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache for a lane property
|
||||
* @param int $laneId The ID of the lane
|
||||
* @param string $property The property to get the cache for (self::CACHE_SELFSERVE_LANE_KEY_STATUS, self::CACHE_SELFSERVE_LANE_KEY_STATE, self::CACHE_SELFSERVE_LANE_KEY_MODE, ...)
|
||||
* @param string|null $class The class name of the expected return type
|
||||
* @return mixed The value from the cache
|
||||
*/
|
||||
public function getLaneCache(int $laneId, string $property, string $class = null): mixed
|
||||
{
|
||||
$cachedValue = redis->get($this->getLaneCacheKey($laneId, $property));
|
||||
// If the cached value is not found, return null
|
||||
if ($cachedValue === null) {
|
||||
return null;
|
||||
}
|
||||
$value = unserialize($cachedValue);
|
||||
// If a class is provided, ensure the value is of that class
|
||||
if ($class !== null && !($value instanceof $class)) {
|
||||
throw new \UnexpectedValueException("Cached value for lane ID {$laneId} and property {$property} is not of type {$class}");
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* Clear the cache for a lane property
|
||||
* @param int $laneId The ID of the lane
|
||||
* @param string $property The property to clear the cache for (self::CACHE_SELFSERVE_LANE_KEY_STATUS, self::CACHE_SELFSERVE_LANE_KEY_STATE, self::CACHE_SELFSERVE_LANE_KEY_MODE, ...)
|
||||
* @return $this;
|
||||
*/
|
||||
public function clearLaneCache(int $laneId, string $property): self
|
||||
{
|
||||
redis->delete($this->getLaneCacheKey($laneId, $property));
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_command.php';
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_mode.php';
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_port.php';
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_state.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane_command_arguments.php';
|
||||
|
||||
use Exception;
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\classes\selfserve_lane_command_arguments;
|
||||
use modules\selfserve\helpers\selfserve_lane_command;
|
||||
use modules\selfserve\helpers\selfserve_lane_mode;
|
||||
use modules\selfserve\helpers\selfserve_lane_port;
|
||||
use modules\selfserve\helpers\selfserve_lane_state;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
use objects\users_o;
|
||||
|
||||
trait selfserve_lane_command_t
|
||||
{
|
||||
/**
|
||||
* Execute a command on a self-serve lane
|
||||
* @param selfserve_lane_command $command The command to execute
|
||||
* @param selfserve_lane_command_arguments $arguments The arguments for the command
|
||||
* @return selfserve_lane|selfserve_lane_command_t
|
||||
* @throws Exception If the command cannot be executed
|
||||
*/
|
||||
public function execute(selfserve_lane_command $command, selfserve_lane_command_arguments $arguments): self
|
||||
{
|
||||
// Handle the command
|
||||
switch ($command) {
|
||||
case selfserve_lane_command::START:
|
||||
// Require lane to be available before starting
|
||||
if (!$this->getLaneStatus()->equals(selfserve_lane_status::AVAILABLE)) throw new \RuntimeException("Cannot start lane: Lane is not available.");
|
||||
// Require the customer number
|
||||
if (empty($customer_number = $arguments->customer_number)) throw new \InvalidArgumentException("Customer number is required to start the lane.");
|
||||
// Require the license plate
|
||||
if (empty($license_plate = $arguments->license_plate)) throw new \InvalidArgumentException("License plate is required to start the lane.");
|
||||
// Validate customer number
|
||||
if (!is_numeric($customer_number) || (int)$customer_number <= 0) throw new \InvalidArgumentException("Invalid customer number: " . $customer_number);
|
||||
if (!(new users_o())->getUserByCustomerNumber((int)$customer_number)->exists()) throw new \InvalidArgumentException("Customer number does not exist: " . $customer_number);
|
||||
// Set the customer number and license plate
|
||||
$this->setCustomerNumber($customer_number);
|
||||
$this->setLicensePlate($license_plate);
|
||||
// Set the lane status to OCCUPIED when started
|
||||
$this->setLaneStatus(selfserve_lane_status::OCCUPIED);
|
||||
// Set the lane state to IN_WASH
|
||||
$this->setLaneState(selfserve_lane_state::IN_WASH);
|
||||
// Open the entrance port
|
||||
$this->open(selfserve_lane_port::ENTRANCE);
|
||||
// Start the wash timer
|
||||
$this->setWashStartTime(time());
|
||||
break;
|
||||
case selfserve_lane_command::STOP:
|
||||
// Require lane to be occupied before stopping
|
||||
if (!$this->getLaneStatus()->equals(selfserve_lane_status::OCCUPIED)) throw new \RuntimeException("Cannot stop lane: Lane is not occupied.");
|
||||
// Check if the customer number equals the argument customer number
|
||||
if (($this->getCustomerNumber() !== $arguments->customer_number) && !$this->isBypassCustomerNumberValidation()) {
|
||||
throw new \InvalidArgumentException("Customer number mismatch: Lane customer number " . $this->getCustomerNumber() . " does not match argument customer number " . $arguments->customer_number);
|
||||
}
|
||||
// Invoice the customer
|
||||
$this->invoice();
|
||||
// Open the exit port
|
||||
$this->open(selfserve_lane_port::EXIT);
|
||||
// Reset the lane
|
||||
self::execute(selfserve_lane_command::RESET, new selfserve_lane_command_arguments());
|
||||
break;
|
||||
case selfserve_lane_command::RESET:
|
||||
// Reset the lane to default states
|
||||
$this->setLaneStatus(selfserve_lane_status::AVAILABLE);
|
||||
$this->setLaneMode(selfserve_lane_mode::MANUAL);
|
||||
$this->setLaneState(selfserve_lane_state::IDLE);
|
||||
$this->setWashStartTime(self::DEFAULT_WASH_START_TIME);
|
||||
$this->setCustomerNumber(self::DEFAULT_CUSTOMER_NUMBER);
|
||||
$this->setLicensePlate(self::DEFAULT_LICENSE_PLATE);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown command: " . $command->name);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
|
||||
trait selfserve_lane_customer_number_t
|
||||
{
|
||||
const DEFAULT_CUSTOMER_NUMBER = null;
|
||||
/**
|
||||
* The billable customer number associated with the current wash
|
||||
* @var int|null $customer_number
|
||||
*/
|
||||
public ?int $customer_number = null;
|
||||
/**
|
||||
* Get the customer number associated with the current wash
|
||||
* @return int|null The customer number, or null if not set
|
||||
*/
|
||||
public function getCustomerNumber(): ?int
|
||||
{
|
||||
$customer_number = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_CUSTOMER_NUMBER);
|
||||
if (!empty($customer_number)) {
|
||||
$this->customer_number = $customer_number;
|
||||
}
|
||||
return $this->customer_number;
|
||||
}
|
||||
/**
|
||||
* Set the customer number associated with the current wash
|
||||
* @param int|null $customer_number The customer number, or null to clear
|
||||
* @return selfserve_lane_customer_number_t|selfserve_lane
|
||||
*/
|
||||
public function setCustomerNumber(?int $customer_number): self
|
||||
{
|
||||
$this->customer_number = $customer_number;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_CUSTOMER_NUMBER, $this->customer_number);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
|
||||
use classes\selfserve;
|
||||
use Exception;
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
use objects\order_items_o;
|
||||
use objects\orders_o;
|
||||
|
||||
trait selfserve_lane_invoice_t
|
||||
{
|
||||
/**
|
||||
* The product ID for minute-based billing
|
||||
* @var int|null $minute_billing_product_id
|
||||
*/
|
||||
public ?int $minute_billing_product_id = null;
|
||||
/**
|
||||
* Get the minute billing product ID
|
||||
* @return int|null The product ID for minute-based billing, or null if not set
|
||||
*/
|
||||
public function getMinuteBillingProductId(): ?int
|
||||
{
|
||||
$minute_billing_product_id = selfserve::getInstance()
|
||||
->config
|
||||
->minute_product
|
||||
->getVariableValue();
|
||||
if (!empty($minute_billing_product_id)) {
|
||||
$this->minute_billing_product_id = $minute_billing_product_id;
|
||||
}
|
||||
return $this->minute_billing_product_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice for minute-based billing
|
||||
* @return bool True on success, false on failure
|
||||
* @throws Exception if lane ID is not set, lane is not occupied, customer number or license plate is not set, or product ID is not set
|
||||
*/
|
||||
public function invoice(): bool
|
||||
{
|
||||
if (empty($this->id)) throw new \Exception("Lane ID is not set.");
|
||||
if ($this->getLaneStatus() !== selfserve_lane_status::OCCUPIED) throw new \Exception("Lane ID {$this->id} is not occupied; cannot invoice.");
|
||||
if (empty($this->getCustomerNumber())) throw new \Exception("Customer number is not set for lane ID {$this->id}.");
|
||||
if (empty($this->getLicensePlate())) throw new \Exception("License plate is not set for lane ID {$this->id}.");
|
||||
if (empty($product_id = $this->getMinuteBillingProductId())) throw new \Exception("Minute billing product ID is not set.");
|
||||
// Calculate minutes used
|
||||
$minutes = $this->getElapsedWashTime() / 60; // Convert seconds to minutes
|
||||
$minutes = (int)ceil($minutes); // Round up to nearest whole minute
|
||||
if ($minutes <= 0) throw new \Exception("No minutes to bill for lane ID {$this->id}.");
|
||||
$amount = $minutes; // Assuming 1 unit per minute, adjust as needed
|
||||
// Create invoice order
|
||||
$order = (new orders_o())->add(
|
||||
$this->getCustomerNumber(),
|
||||
2285, // System User ID
|
||||
'',
|
||||
'',
|
||||
(int)$this->department_lane->department->value(),
|
||||
(string)$this->getLicensePlate()
|
||||
);
|
||||
// Add product to order
|
||||
$order_items = new order_items_o();
|
||||
$order_items->addItemToOrder(
|
||||
$order->id,
|
||||
$product_id,
|
||||
2285, // System User ID
|
||||
$amount,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
|
||||
trait selfserve_lane_license_plate_t
|
||||
{
|
||||
const DEFAULT_LICENSE_PLATE = null;
|
||||
/**
|
||||
* The license plate of the vehicle currently in the lane
|
||||
* @var string|null $license_plate
|
||||
*/
|
||||
public ?string $license_plate = null;
|
||||
/**
|
||||
* Get the license plate of the vehicle currently in the lane
|
||||
* @return string|null The license plate, or null if not set
|
||||
*/
|
||||
public function getLicensePlate(): ?string
|
||||
{
|
||||
$license_plate = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_LICENSE_PLATE);
|
||||
if (!empty($license_plate)) {
|
||||
$this->license_plate = $license_plate;
|
||||
}
|
||||
return $this->license_plate;
|
||||
}
|
||||
/**
|
||||
* Set the license plate of the vehicle currently in the lane
|
||||
* @param string|null $license_plate The license plate, or null to clear
|
||||
* @return selfserve_lane_license_plate_t|selfserve_lane
|
||||
*/
|
||||
public function setLicensePlate(?string $license_plate): self
|
||||
{
|
||||
$this->license_plate = $license_plate;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_LICENSE_PLATE, $this->license_plate);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_mode.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_mode;
|
||||
|
||||
trait selfserve_lane_mode_t
|
||||
{
|
||||
/**
|
||||
* @enum selfserve_lane_mode
|
||||
* @description The mode of the lane, which can be one of the following values:
|
||||
* @values MANUAL, AUTOMATIC
|
||||
* @var selfserve_lane_mode $mode
|
||||
*/
|
||||
public selfserve_lane_mode $mode;
|
||||
|
||||
/**
|
||||
* Get the current mode of the self-serve lane
|
||||
* @return selfserve_lane_mode The current mode of the self-serve lane
|
||||
*/
|
||||
public function getLaneMode(): selfserve_lane_mode
|
||||
{
|
||||
$mode = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_MODE, selfserve_lane_mode::class);
|
||||
if (!empty($mode)) {
|
||||
$this->mode = $mode;
|
||||
}
|
||||
else if (empty($this->mode)) {
|
||||
// If not found in cache, default to MANUAL
|
||||
$this->mode = selfserve_lane_mode::MANUAL;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_MODE, $this->mode);
|
||||
}
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current mode of the self-serve lane
|
||||
* @param selfserve_lane_mode $mode
|
||||
* @return selfserve_lane_mode_t|selfserve_lane
|
||||
*/
|
||||
public function setLaneMode(selfserve_lane_mode $mode): self
|
||||
{
|
||||
$this->mode = $mode;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_MODE, $this->mode);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_port.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
|
||||
use classes\shelly;
|
||||
use modules\selfserve\helpers\selfserve_lane_port;
|
||||
use modules\selfserve\helpers\selfserve_lane_state;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
use modules\shelly\helpers\shelly_device_switch;
|
||||
use modules\shelly\helpers\shelly_request_body_get_states;
|
||||
|
||||
trait selfserve_lane_port_controller_t
|
||||
{
|
||||
/**
|
||||
* Open the lane port
|
||||
* @param selfserve_lane_port $port The port to open (ENTRANCE or EXIT)
|
||||
* @return bool True if the port was successfully opened, false otherwise
|
||||
* @throws \Exception If an invalid port is specified, or if the lane is not in a state to open the port
|
||||
*/
|
||||
public function open(selfserve_lane_port $port): bool
|
||||
{
|
||||
// Require department lane object
|
||||
if (empty($this->department_lane)) throw new \Exception("Department lane object not found for lane ID {$this->id}");
|
||||
// Check lane status
|
||||
if ($this->status->equals(selfserve_lane_status::CLOSED)) throw new \Exception("Cannot open port on CLOSED lane");
|
||||
if ($this->status->equals(selfserve_lane_status::MAINTENANCE)) throw new \Exception("Cannot open port on MAINTENANCE lane");
|
||||
if ($this->status->equals(selfserve_lane_status::FAULT)) throw new \Exception("Cannot open port on FAULT lane");
|
||||
// Get the relay ID based on the port
|
||||
$relay_id = match ($port) {
|
||||
selfserve_lane_port::ENTRANCE => $this->department_lane->relay_out_id->value(),
|
||||
selfserve_lane_port::EXIT => $this->department_lane->relay_in_id->value(),
|
||||
default => throw new \Exception("Invalid port specified, must be ENTRANCE or EXIT"),
|
||||
};
|
||||
// Make sure relay ID is valid
|
||||
if (empty($relay_id)) {
|
||||
throw new \Exception("Invalid relay ID for port {$port->name}");
|
||||
}
|
||||
// Mark the lane state as opening the port
|
||||
$new_state = match ($port) {
|
||||
selfserve_lane_port::ENTRANCE => selfserve_lane_state::ENTRANCE_PORT_OPEN_QUEUED,
|
||||
selfserve_lane_port::EXIT => selfserve_lane_state::EXIT_PORT_OPEN_QUEUED,
|
||||
default => throw new \Exception("Invalid port specified, must be ENTRANCE or EXIT"),
|
||||
};
|
||||
$this->setLaneState($new_state);
|
||||
// Open the relay
|
||||
$this->shellyOpenPort($port);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the Shelly relay for the specified port
|
||||
* @param selfserve_lane_port $port The port to open (ENTRANCE or EXIT)
|
||||
* @return bool True if the relay was successfully opened
|
||||
* @throws \Exception If an invalid port is specified or if the relay ID is invalid
|
||||
*/
|
||||
public function shellyOpenPort(selfserve_lane_port $port): bool
|
||||
{
|
||||
// Get the relay ID based on the port
|
||||
$relay_id = match ($port) {
|
||||
selfserve_lane_port::ENTRANCE => $this->department_lane->relay_out_id->value(),
|
||||
selfserve_lane_port::EXIT => $this->department_lane->relay_in_id->value(),
|
||||
default => throw new \Exception("Invalid port specified, must be ENTRANCE or EXIT"),
|
||||
};
|
||||
// Make sure relay ID is valid
|
||||
if (empty($relay_id)) {
|
||||
throw new \Exception("Invalid relay ID for port {$port->name}");
|
||||
}
|
||||
$shelly = new shelly();
|
||||
$shelly->requireModuleEnabled();
|
||||
$shelly->requireValidSecretKey();
|
||||
$parameters = new shelly_request_body_get_states();
|
||||
$parameters->ids = [$relay_id];
|
||||
$parameters->select = ['status'];
|
||||
$result = $shelly->sendPostRequest('/v2/devices/api/get', (array)$parameters);
|
||||
// Wait 1 second
|
||||
sleep(1);
|
||||
// Format the result
|
||||
$result = array_map(function ($device) {
|
||||
return (new shelly_device_switch())->populate($device);
|
||||
}, $result);
|
||||
// Open the switch
|
||||
foreach ($result as $device) {
|
||||
$device->switch(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_state.php';
|
||||
|
||||
use modules\selfserve\helpers\selfserve_lane_state;
|
||||
|
||||
trait selfserve_lane_state_t
|
||||
{
|
||||
/**
|
||||
* @enum selfserve_lane_state
|
||||
* @description The operational state of the lane, which can be one of the following values:
|
||||
* @values ENTRANCE_PORT_OPEN_QUEUED, ENTRANCE_PORT_OPEN, MACHINE_RELAY_ON_QUEUED, MACHINE_RELAY_ON, MACHINE_RELAY_OFF_QUEUED, MACHINE_RELAY_OFF, IN_WASH, EXIT_PORT_OPEN_QUEUED, EXIT_PORT_OPEN, IDLE, FAULT, MAINTENANCE, CLOSED
|
||||
* @var selfserve_lane_state $state
|
||||
*/
|
||||
public selfserve_lane_state $state;
|
||||
|
||||
/**
|
||||
* Get the current operational state of the self-serve lane
|
||||
* @return selfserve_lane_state The current operational state of the self-serve lane
|
||||
*/
|
||||
public function getLaneState(): selfserve_lane_state
|
||||
{
|
||||
$state = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATE, selfserve_lane_state::class);
|
||||
if (!empty($state)) {
|
||||
$this->state = $state;
|
||||
}
|
||||
else if (empty($this->state)) {
|
||||
// If not found in cache, default to MANUAL
|
||||
$this->state = selfserve_lane_state::IDLE;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATE, $this->state);
|
||||
}
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current operational state of the self-serve lane
|
||||
* @param selfserve_lane_state $state The new operational state of the self-serve lane
|
||||
* @return selfserve_lane_state_t
|
||||
*/
|
||||
public function setLaneState(selfserve_lane_state $state): self
|
||||
{
|
||||
$this->state = $state;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATE, $this->state);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
|
||||
trait selfserve_lane_status_t
|
||||
{
|
||||
/**
|
||||
* @enum selfserve_lane_status
|
||||
* @description The lane availability status, which can be one of the following values:
|
||||
* @values OCCUPIED, FAULT, AVAILABLE, MAINTENANCE, CLOSED, RESERVED
|
||||
* @var selfserve_lane_status $status
|
||||
*/
|
||||
public selfserve_lane_status $status;
|
||||
|
||||
/**
|
||||
* Get the current status of the self-serve lane
|
||||
* @return selfserve_lane_status The current status of the self-serve lane
|
||||
*/
|
||||
public function getLaneStatus(): selfserve_lane_status
|
||||
{
|
||||
$status = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATUS, selfserve_lane_status::class);
|
||||
if (!empty($status)) {
|
||||
$this->status = $status;
|
||||
}
|
||||
else if (empty($this->status)) {
|
||||
// If not found in cache, default to AVAILABLE
|
||||
$this->status = selfserve_lane_status::AVAILABLE;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATUS, $this->status);
|
||||
}
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current status of the self-serve lane
|
||||
* @param selfserve_lane_status $status The new status of the self-serve lane
|
||||
* @return selfserve_lane_status_t|selfserve_lane
|
||||
*/
|
||||
public function setLaneStatus(selfserve_lane_status $status): self
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_STATUS, $this->status);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace modules\selfserve\traits;
|
||||
require_once WD . '/modules/selfserve/helpers/selfserve_lane_status.php';
|
||||
require_once WD . '/modules/selfserve/classes/selfserve_lane.php';
|
||||
use modules\selfserve\classes\selfserve_lane;
|
||||
use modules\selfserve\helpers\selfserve_lane_status;
|
||||
|
||||
trait selfserve_lane_timer_t
|
||||
{
|
||||
const DEFAULT_WASH_START_TIME = null;
|
||||
/**
|
||||
* The timestamp of the start of the current wash cycle
|
||||
* @var int|null $wash_start_time
|
||||
*/
|
||||
public ?int $wash_start_time = null;
|
||||
/**
|
||||
* Get the wash start time
|
||||
* @return int|null The timestamp of the start of the current wash cycle, or null if not set
|
||||
*/
|
||||
public function getWashStartTime(): ?int
|
||||
{
|
||||
$wash_start_time = $this->getLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_WASH_START_TIME);
|
||||
if (!empty($wash_start_time)) {
|
||||
$this->wash_start_time = $wash_start_time;
|
||||
}
|
||||
return $this->wash_start_time;
|
||||
}
|
||||
/**
|
||||
* Set the wash start time
|
||||
* @param int|null $timestamp The timestamp of the start of the current wash cycle, or null to clear
|
||||
* @return selfserve_lane_timer_t|selfserve_lane
|
||||
*/
|
||||
public function setWashStartTime(?int $timestamp): self
|
||||
{
|
||||
$this->wash_start_time = $timestamp;
|
||||
$this->setLaneCache($this->id, self::CACHE_SELFSERVE_LANE_KEY_WASH_START_TIME, $this->wash_start_time);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elapsed time since the wash started
|
||||
* @return int|null The elapsed time in seconds since the wash started, or null if wash has not started
|
||||
*/
|
||||
public function getElapsedWashTime(): ?int
|
||||
{
|
||||
$wash_start_time = $this->getWashStartTime();
|
||||
if ($wash_start_time === null) {
|
||||
return null; // Wash has not started
|
||||
}
|
||||
return time() - $wash_start_time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace objects;
|
||||
|
||||
use classes\db;
|
||||
use classes\object_property;
|
||||
use Exception;
|
||||
use traits\db_object_t;
|
||||
|
||||
class accounts_o extends db
|
||||
{
|
||||
use db_object_t;
|
||||
/**
|
||||
* The phone number
|
||||
* @var object_property $phone The phone number
|
||||
*/
|
||||
public object_property $phone;
|
||||
/**
|
||||
* The country code (e.g. 31 for +31)
|
||||
* @var object_property $country_code The country code (e.g. 31 for +31)
|
||||
*/
|
||||
public object_property $country_code;
|
||||
/**
|
||||
* The account name
|
||||
* @var object_property $name The account name
|
||||
*/
|
||||
public object_property $name;
|
||||
/**
|
||||
* The profile picture property
|
||||
* @var object_property $profile_picture The profile picture property
|
||||
*/
|
||||
public object_property $profile_picture;
|
||||
/**
|
||||
* The enabled property
|
||||
* @var object_property $enabled The enabled property
|
||||
*/
|
||||
public object_property $enabled;
|
||||
/**
|
||||
* The (last) seen at date
|
||||
* @var object_property $seen_at The seen at date
|
||||
*/
|
||||
public object_property $seen_at;
|
||||
/**
|
||||
* The created at date
|
||||
* @var object_property $created_at The created at date
|
||||
*/
|
||||
public object_property $created_at;
|
||||
/**
|
||||
* The updated at date
|
||||
* @var object_property $updated_at The updated at date
|
||||
*/
|
||||
public object_property $updated_at;
|
||||
/**
|
||||
* The deleted at date
|
||||
* @var object_property $deleted_at The deleted at date
|
||||
*/
|
||||
public object_property $deleted_at;
|
||||
|
||||
public function structure(): void
|
||||
{
|
||||
$this->setTable('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new account
|
||||
* @param array $data The account data
|
||||
* @return int The account id
|
||||
* @throws Exception If the account was not created successfully
|
||||
*/
|
||||
public function add(array $data): int
|
||||
{
|
||||
global /** @var db $db */
|
||||
$db;
|
||||
// Set the default values
|
||||
$data_default = [
|
||||
'phone' => 0,
|
||||
'country_code' => 45,
|
||||
'name' => '',
|
||||
'profile_picture' => null,
|
||||
];
|
||||
// Merge the default values with the data
|
||||
$data = array_merge($data_default, $data);
|
||||
// Sanitize the input
|
||||
$data['phone'] = (int)$db->escape_string($data['phone']);
|
||||
$data['country_code'] = (int)$data['country_code'];
|
||||
$data['name'] = (string)$db->escape_string($data['name']);
|
||||
$data['profile_picture'] = $data['profile_picture'] ? (int)$data['profile_picture'] : null;
|
||||
// Add the object
|
||||
$tmp_id = self::add_object($data);
|
||||
if (!$tmp_id) {
|
||||
throw new Exception('The account was not created successfully.');
|
||||
}
|
||||
return $tmp_id;
|
||||
}
|
||||
|
||||
public function getObjectProperties(): void
|
||||
{
|
||||
$this->phone = new object_property($this->table, $this->id, 'phone', 'int', false);
|
||||
$this->country_code = new object_property($this->table, $this->id, 'country_code', 'int', false);
|
||||
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
|
||||
$this->profile_picture = new object_property($this->table, $this->id, 'profile_picture', 'int', false);
|
||||
$this->enabled = new object_property($this->table, $this->id, 'enabled', 'bool', false);
|
||||
$this->seen_at = new object_property($this->table, $this->id, 'seen_at', 'datetime', false);
|
||||
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'datetime', false);
|
||||
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'datetime', false);
|
||||
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'datetime', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the cached object when it is changed
|
||||
* @throws Exception If no object is selected
|
||||
*/
|
||||
public function objectChanged(): void
|
||||
{
|
||||
self::requireSelected();
|
||||
// Invalidate the cache
|
||||
self::deleteCached(self::$asArrayCacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object as an array
|
||||
* @return array The object as an array
|
||||
* @throws Exception If no object is selected
|
||||
*/
|
||||
public function asArray(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
$cached = self::getCached(self::$asArrayCacheKey);
|
||||
// If the object is cached, return it
|
||||
if (!empty($cached)) return $cached;
|
||||
// Convert the object to an array
|
||||
$array = [
|
||||
'id' => (int)$this->id,
|
||||
'phone' => (int)$this->phone->value(),
|
||||
'country_code' => (int)$this->country_code->value(),
|
||||
'name' => (string)$this->name->value(),
|
||||
'profile_picture' => (int)$this->profile_picture->value(),
|
||||
'enabled' => (bool)$this->enabled->value(),
|
||||
'seen_at' => $this->seen_at->value(),
|
||||
'created_at' => $this->created_at->value(),
|
||||
'updated_at' => $this->updated_at->value(),
|
||||
//'deleted_at' => $this->deleted_at->value(),
|
||||
];
|
||||
// Cache the object
|
||||
self::cache(self::$asArrayCacheKey, $array);
|
||||
self::setCachedExpiration(self::$asArrayCacheKey, self::$asArrayCacheExpiration);
|
||||
// Return the object as an array
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
@@ -503,6 +503,11 @@ class customer_vehicles_o extends db
|
||||
// Convert the array of transaction ids to a comma separated string
|
||||
$orders = '';
|
||||
foreach ($transaction_ids as $transaction_id) {
|
||||
// Check if the transaction is included in the invoicing.
|
||||
$tmp = (new orders_o())->select((int)$transaction_id);
|
||||
if (!$tmp->isIncludedInInvoicing()) {
|
||||
continue; // The transaction is not included in the invoicing, skip it
|
||||
}
|
||||
$orders .= (int)$transaction_id . ',';
|
||||
}
|
||||
// Remove the last comma
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace objects;
|
||||
|
||||
use classes\db;
|
||||
use classes\object_property;
|
||||
use classes\selfserve;
|
||||
use Exception;
|
||||
use traits\db_object_t;
|
||||
|
||||
@@ -15,6 +16,7 @@ class department_lanes_o extends db
|
||||
public object_property $name; // The lane name
|
||||
public object_property $relay_in_id; // The Shelly relay for the entrance port (if applicable)
|
||||
public object_property $relay_out_id; // The Shelly relay for the exit port (if applicable)
|
||||
public object_property $relay_machine_id; // The Shelly relay for the machine (if applicable)
|
||||
public object_property $created_at;
|
||||
public object_property $updated_at;
|
||||
public object_property $deleted_at;
|
||||
@@ -25,16 +27,24 @@ class department_lanes_o extends db
|
||||
$this->setTable('department_lanes');
|
||||
}
|
||||
|
||||
public function getLaneStatus(): \modules\selfserve\helpers\selfserve_lane_status
|
||||
{
|
||||
$selfserve = new selfserve();
|
||||
$lane = $selfserve->lane((int)$this->id);
|
||||
return $lane->getLaneStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a department lane
|
||||
* @param int $department The department id
|
||||
* @param string $name The lane name
|
||||
* @param string|null $relay_in_id The Shelly relay for the entrance port (if applicable)
|
||||
* @param string|null $relay_out_id The Shelly relay for the exit port (if applicable)
|
||||
* @param string|null $relay_machine_id The Shelly relay for the machine (if applicable)
|
||||
* @return department_lanes_o
|
||||
* @throws Exception If the object was not created successfully
|
||||
*/
|
||||
public function add(int $department, string $name, string $relay_in_id = null, string $relay_out_id = null): self
|
||||
public function add(int $department, string $name, string $relay_in_id = null, string $relay_out_id = null, string $relay_machine_id = null): department_lanes_o
|
||||
{
|
||||
global /** @var db $db */
|
||||
$db;
|
||||
@@ -47,12 +57,16 @@ class department_lanes_o extends db
|
||||
if (!is_null($relay_out_id)) {
|
||||
$relay_out_id = $db->escape_string($relay_out_id);
|
||||
}
|
||||
if (!is_null($relay_machine_id)) {
|
||||
$relay_machine_id = $db->escape_string($relay_machine_id);
|
||||
}
|
||||
// Add the object
|
||||
$tmp_id = self::add_object([
|
||||
'department' => $department,
|
||||
'name' => $name,
|
||||
...(!is_null($relay_in_id) ? ['relay_in_id' => $relay_in_id] : []), // If the relay_in_id is null, it will be set to null in the database
|
||||
...(!is_null($relay_out_id) ? ['relay_out_id' => $relay_out_id] : []), // If the relay_out_id is null, it will be set to null in the database
|
||||
...(!is_null($relay_machine_id) ? ['relay_machine_id' => $relay_machine_id] : []), // If the relay_machine_id is null, it will be set to null in the database
|
||||
]);
|
||||
$this->id = $tmp_id;
|
||||
self::getObjectProperties();
|
||||
@@ -66,6 +80,7 @@ class department_lanes_o extends db
|
||||
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
|
||||
$this->relay_in_id = new object_property($this->table, $this->id, 'relay_in_id', 'string', false);
|
||||
$this->relay_out_id = new object_property($this->table, $this->id, 'relay_out_id', 'string', false);
|
||||
$this->relay_machine_id = new object_property($this->table, $this->id, 'relay_machine_id', 'string', false);
|
||||
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
|
||||
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp', false);
|
||||
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
|
||||
@@ -84,8 +99,31 @@ class department_lanes_o extends db
|
||||
'name' => (string)$this->name->value(),
|
||||
'relay_in_id' => (string)$this->relay_in_id->value(),
|
||||
'relay_out_id' => (string)$this->relay_out_id->value(),
|
||||
'relay_machine_id' => (string)$this->relay_machine_id->value(),
|
||||
'created_at' => (string)$this->created_at->value(),
|
||||
'updated_at' => (string)$this->updated_at->value(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @return department_lanes_o[] An array of department lane objects for the specified department
|
||||
*/
|
||||
public function getDepartmentLanes(int $department_id): array
|
||||
{
|
||||
$lanes = [];
|
||||
$department_lanes = self::getFieldsWhere(
|
||||
[
|
||||
'department' => $department_id,
|
||||
'deleted_at' => null
|
||||
],
|
||||
[
|
||||
'id',
|
||||
],
|
||||
);
|
||||
foreach ( $department_lanes as $department_lane ) {
|
||||
$lanes[] = (new department_lanes_o())->select((int)$department_lane['id']);
|
||||
}
|
||||
return $lanes;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,17 @@ class departments_o extends db
|
||||
redis->clear_departments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lanes associated with the department
|
||||
* @retuns department_lanes_o[] The lanes associated with the department
|
||||
* @throws Exception If the department is not selected
|
||||
*/
|
||||
public function getLanes(): array
|
||||
{
|
||||
self::requireSelected();
|
||||
return (new department_lanes_o())->getDepartmentLanes($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a department
|
||||
* @param string $name
|
||||
|
||||
@@ -119,6 +119,39 @@ class order_bookings_o extends db
|
||||
$customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_number->value());
|
||||
$department_array = $department->asArray();
|
||||
$customer_array = $customer->asArray();
|
||||
$message = "";
|
||||
$message .= "Ny booking fra {$customer->getCustomerName($customer_array['customer_number'])} (Kundenr: {$customer_array['customer_number']}, Bookingnr: {$this->id})\n";
|
||||
$message .= "Dato: " . date('d-m-Y H:i', strtotime($this->datetime->value())) . "\n";
|
||||
$message .= "Køretøj: " . $this->reg_1->value() . (!empty($this->reg_2->value()) ? ", " . $this->reg_2->value() : "") . (
|
||||
!empty($this->reg_3->value()) ? ", " . $this->reg_3->value() : ""
|
||||
) ."\n";
|
||||
// If the booking has a note, add it to the message
|
||||
if (!empty($this->note->value())) $message .= "Note: " . $this->note->value() . "\n";
|
||||
// If the booking has items, add them to the message
|
||||
if (!empty($this->items->value())) {
|
||||
$items = array_map(function ($item) {
|
||||
// Get the product name
|
||||
$product = (new products_o())->select((int)$item['id']);
|
||||
if (!$product->exists()) {
|
||||
return null;
|
||||
}
|
||||
$item['name'] = $product->name->value();
|
||||
if (!isset($item['quantity'])) {
|
||||
$item['quantity'] = 1;
|
||||
}
|
||||
return "- " . $item['quantity'] . " x " . $item['name'];
|
||||
}, $this->items->value());
|
||||
$message .= "Ydelser:\n" . implode("\n", array_filter($items)) . "\n";
|
||||
}
|
||||
// If the booking has a reference, add it to the message
|
||||
if (!empty($this->reference->value())) $message .= "Reference: " . $this->reference->value() . "\n";
|
||||
// If the booking has a PO, add it to the message
|
||||
if (!empty($this->po->value())) $message .= "PO: " . $this->po->value() . "\n";
|
||||
if ($this->pickup->value()) $message .= "Afhentning: Ja\n"; else $message .= "Afhentning: Nej\n";
|
||||
// If the booking has a note, add it to the message
|
||||
if (!empty($this->note->value())) $message .= "Note: " . $this->note->value() . "\n";
|
||||
// Add the branding name
|
||||
$message = "*" . $branding->name->value() . "*\n" . $message;
|
||||
/**
|
||||
* Department booking notification
|
||||
*/
|
||||
@@ -169,7 +202,8 @@ class order_bookings_o extends db
|
||||
// Add all the phone numbers from the department
|
||||
...$department->notificationSmsPhoneNumbers()
|
||||
],
|
||||
'New booking from ' . $customer->getCustomerName($customer_array['customer_number']) . ' (' . $this->id . ')',
|
||||
$message,
|
||||
true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
// Log the error
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
namespace objects;
|
||||
|
||||
use attachments\helpers\attachment_content;
|
||||
use classes\db;
|
||||
use classes\email;
|
||||
use classes\pdf_generator;
|
||||
use classes\motorapi;
|
||||
use classes\object_property;
|
||||
use classes\response;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use helpers\xlvask_usage_log;
|
||||
use helpers\xlvask_wash_item;
|
||||
@@ -1376,6 +1380,86 @@ class orders_o extends db
|
||||
return false; // No wash certificate product found in the order items
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and attach a wash certificate directly on an order (without a booking)
|
||||
* @param int|null $safety_seal Optional safety seal number
|
||||
* @param string|null $operator Optional operator/employee name who carried out the wash
|
||||
* @param string|DateTime|null $date Optional date of the wash (defaults to current date)
|
||||
* @throws Exception If the order is not selected or required related objects are missing
|
||||
*/
|
||||
public function generateWashCertificate(int|null $safety_seal = null, string|null $operator = null, $date = null): void
|
||||
{
|
||||
self::requireSelected();
|
||||
// Avoid generating duplicate certificates
|
||||
if ($this->hasWashCertificateAttached()) {
|
||||
return;
|
||||
}
|
||||
// Get the department for the order
|
||||
$department = (new departments_o())->select((int)$this->department_id->value());
|
||||
if (!$department->exists()) {
|
||||
throw new Exception('Department not found');
|
||||
}
|
||||
// Get branding for the department
|
||||
$branding = (new branding_o())->select((int)$department->branding->value());
|
||||
if (!$branding->exists()) {
|
||||
throw new Exception('Branding not found');
|
||||
}
|
||||
// Get the customer for the order
|
||||
$customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_id->value());
|
||||
$department_array = $department->asArray();
|
||||
$department_array['branding'] = $branding->asArray();
|
||||
$customer_array = $customer->asArray();
|
||||
$order_array = $this->asArray();
|
||||
// Format the date as 17:35 02-12-2025
|
||||
$date = ($date instanceof DateTime ? $date : ($date !== null ? new DateTime($date) : new DateTime()));
|
||||
$date_formatted = ($date instanceof DateTime ? $date->format('d-m-Y') : date('d-m-Y'));
|
||||
$time_formatted = ($date instanceof DateTime ? $date->format('H:i') : date('H:i'));
|
||||
|
||||
// Generate PDF
|
||||
$pdf_generator = new pdf_generator();
|
||||
$pdf_generator->add_html(
|
||||
$pdf_generator->templates->getTemplate('wash_certificate')
|
||||
->setCompany([
|
||||
'name' => 'Truck Wash',
|
||||
'address' => 'Letland Allé 2',
|
||||
'zip' => 2630,
|
||||
'city' => 'Taastrup',
|
||||
'phone_prefix' => 45,
|
||||
'phone' => 43717886,
|
||||
'email' => 'cph@truckwash.dk',
|
||||
'website' => 'www.truckwash.dk',
|
||||
'images' => [
|
||||
'logo' => '/truckwash-banner-png.png',
|
||||
'banner' => '/truckwash-banner-png.png',
|
||||
'signature' => '/truckwash-underskrift.png',
|
||||
],
|
||||
])
|
||||
->addData([
|
||||
'booking_number' => $this->id, // Used as document number on the template
|
||||
'seal_number' => ($safety_seal ?? null),
|
||||
'reg_1' => $order_array['reg_1'],
|
||||
'reg_2' => $order_array['reg_2'],
|
||||
'date' => $date_formatted,
|
||||
'time' => $time_formatted,
|
||||
'carried_out_by' => ($operator ?? null),
|
||||
'department_id' => $department->id,
|
||||
'department_name' => $department_array['branding']['name'] ?: $department_array['name'],
|
||||
'department_address' => $department_array['branding']['address'] ?: $department_array['description'],
|
||||
'customer_name' => $customer->getCustomerName($customer_array['customer_number']),
|
||||
'customer_address' => $customer->getCustomerEcocomicData($customer_array['customer_number'])->economic_customer->address ?: '-',
|
||||
'wash_type' => 'ORDER_WASH'
|
||||
])
|
||||
->getHtml()
|
||||
);
|
||||
$pdf_path = $pdf_generator->generate_pdf();
|
||||
|
||||
// Attach PDF to the order
|
||||
$this->addAttachment(new attachment_content((object)[
|
||||
'document' => $pdf_path,
|
||||
'other' => attachment_content::OTHER_TYPE_WASH_CERTIFICATE,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -1414,4 +1498,20 @@ class orders_o extends db
|
||||
self::requireSelected();
|
||||
return !(new departments_o())->select((int)$this->department_id->value())->isExcludedFromInvoicing();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the order is linked to a booking, get the booking object
|
||||
* @return order_bookings_o|null The booking object if linked, null otherwise
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getOrderBooking(): order_bookings_o|null
|
||||
{
|
||||
self::requireSelected();
|
||||
if ((int)$this->booking_id->value() <= 0) {
|
||||
return null;
|
||||
}
|
||||
$booking = new order_bookings_o();
|
||||
$booking->select((int)$this->booking_id->value());
|
||||
return $booking->exists() ? $booking : null;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ class plate_scans_o extends db
|
||||
|
||||
public object_property $plate_scanner_id;
|
||||
public object_property $plate;
|
||||
public object_property $bay_id;
|
||||
|
||||
public function structure(): void
|
||||
{
|
||||
@@ -40,14 +41,19 @@ class plate_scans_o extends db
|
||||
{
|
||||
$this->plate_scanner_id = new object_property($this->table, $this->id, 'plate_scanner_id', 'int');
|
||||
$this->plate = new object_property($this->table, $this->id, 'plate', 'string');
|
||||
$this->bay_id = new object_property($this->table, $this->id, 'bay_id', 'string');
|
||||
}
|
||||
|
||||
public function add(int $plate_scanner_id, string $plate): void
|
||||
public function add(int $plate_scanner_id, string $plate, string $bayId = null): void
|
||||
{
|
||||
global $db, $response;
|
||||
try {
|
||||
// Avoid SQL injection
|
||||
$plate = $db->escape_string($plate);
|
||||
// If the bayId is set, validate it
|
||||
if ($bayId !== null) {
|
||||
$bayId = $db->escape_string($bayId);
|
||||
}
|
||||
// Get the department id from the plate scanner id
|
||||
$sql = "SELECT department_id FROM plate_scanners WHERE id = $plate_scanner_id";
|
||||
$result = $db->query($sql);
|
||||
@@ -59,9 +65,12 @@ class plate_scans_o extends db
|
||||
|
||||
// Get the id of the new record
|
||||
$this->id = $db->insert_id();
|
||||
|
||||
// Set the values of the object properties
|
||||
$this->getObjectProperties();
|
||||
// Set the bayId if provided
|
||||
if ($bayId !== null) {
|
||||
$this->bay_id->set((string)$bayId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response->error($e->getMessage());
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class InvoicingPeriodRoute
|
||||
$this->get('/superuser/invoicing/period/distribution/fixed-pricing', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
//$this->requirePermission('superuser_invoicing_period');
|
||||
$this->requirePermission('superuser_invoicing_period');
|
||||
self::requireParameters([
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
@@ -100,7 +100,7 @@ class InvoicingPeriodRoute
|
||||
$this->get('/superuser/invoicing/period/distribution/wash-subscriptions', function () {
|
||||
// Require the user to be logged in
|
||||
global $response;
|
||||
//$this->requirePermission('superuser_invoicing_period');
|
||||
$this->requirePermission('superuser_invoicing_period');
|
||||
self::requireParameters([
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
@@ -291,6 +291,13 @@ class InvoicingPeriodRoute
|
||||
}
|
||||
$collective_results['subscription_price_department_distribution'][$department_id] += $price;
|
||||
}
|
||||
// Verify that the sum of the department distribution equals the total subscription price for the customer
|
||||
$sum_of_distribution = array_sum($customer['meta']['subscription']['subscription_price_department_distribution']);
|
||||
$difference = $customer['meta']['subscription']['subscription_total'] - $sum_of_distribution;
|
||||
if (abs($difference) > 0.01) {
|
||||
// Throw an error
|
||||
throw new Exception('Subscription price distribution does not equal total subscription price for customer ' . $customer['customer_number'] . '. Difference: ' . $difference);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Parse the department ids to department names
|
||||
@@ -498,6 +505,10 @@ class InvoicingPeriodRoute
|
||||
$department_totals = []; // Array to hold totals per department
|
||||
foreach ( $customer['transactions'] as $transaction ) {
|
||||
$transaction_obj = (new orders_o())->select((int)$transaction['id']);
|
||||
// Check if the transaction is included in invoicing
|
||||
if (!$transaction_obj->isIncludedInInvoicing()) {
|
||||
continue; // Skip transactions not included in invoicing
|
||||
}
|
||||
$transaction['original_price'] = $transaction_obj->getNetAmountForOrderItemsOriginal();
|
||||
$original_price += $transaction['original_price'];
|
||||
// Add the transaction department id to the transaction
|
||||
|
||||
@@ -35,6 +35,7 @@ class departmentLanesRoute
|
||||
'department',
|
||||
'relay_in_id',
|
||||
'relay_out_id',
|
||||
'relay_machine_id',
|
||||
])
|
||||
->listObjectsWithPaginationIfSet(
|
||||
function ($department_lane) use ($user) {
|
||||
@@ -45,6 +46,7 @@ class departmentLanesRoute
|
||||
'name' => (string)$department_lane['name'],
|
||||
'relay_in_id' => (string)$department_lane['relay_in_id'],
|
||||
'relay_out_id' => (string)$department_lane['relay_out_id'],
|
||||
'relay_machine_id' => (string)$department_lane['relay_machine_id'],
|
||||
'created_at' => (string)$department_lane['created_at'],
|
||||
'updated_at' => (string)$department_lane['updated_at'],
|
||||
];
|
||||
@@ -79,11 +81,12 @@ class departmentLanesRoute
|
||||
$department = $response->getRequestParameter('department') ?? null;
|
||||
$relay_in_id = $response->getRequestParameter('relay_in_id') ?? null;
|
||||
$relay_out_id = $response->getRequestParameter('relay_out_id') ?? null;
|
||||
$relay_machine_id = $response->getRequestParameter('relay_machine_id') ?? null;
|
||||
// Remove spaces from the relay_in_id and relay_out_id
|
||||
// Check if the required fields are set
|
||||
if ($name && $department) {
|
||||
// Add the department lane
|
||||
(new department_lanes_o())->add((int)$department, (string)$name, $relay_in_id, $relay_out_id);
|
||||
(new department_lanes_o())->add((int)$department, (string)$name, $relay_in_id, $relay_out_id, $relay_machine_id);
|
||||
// Return a success message
|
||||
$response->success('Department lane added');
|
||||
} else {
|
||||
@@ -120,6 +123,7 @@ class departmentLanesRoute
|
||||
$department = $response->getRequestParameter('department') ?? null;
|
||||
$relay_in_id = $response->getRequestParameter('relay_in_id') ?? null;
|
||||
$relay_out_id = $response->getRequestParameter('relay_out_id') ?? null;
|
||||
$relay_machine_id = $response->getRequestParameter('relay_machine_id') ?? null;
|
||||
|
||||
// Check what fields are set
|
||||
self::requireParameters(['id']);
|
||||
@@ -143,6 +147,9 @@ class departmentLanesRoute
|
||||
if (self::isParametersSet(['relay_out_id'])) {
|
||||
$department_lane->relay_out_id->set((string)$relay_out_id);
|
||||
}
|
||||
if (self::isParametersSet(['relay_machine_id'])) {
|
||||
$department_lane->relay_machine_id->set((string)$relay_machine_id);
|
||||
}
|
||||
// Return a success message
|
||||
$response->success('Department lane updated');
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace routes;
|
||||
use classes\authentication;
|
||||
use classes\recaptcha;
|
||||
use classes\virkdata;
|
||||
use objects\department_lanes_o;
|
||||
use objects\departments_o;
|
||||
use objects\logs_o;
|
||||
use objects\tokens_o;
|
||||
@@ -52,13 +53,42 @@ class guestRoute
|
||||
$this->get('/guest/departments', function () {
|
||||
global $response;
|
||||
$departments = (new departments_o());
|
||||
$response->success($departments->listObjectsWithPaginationIfSet(function ($department_array) {
|
||||
// Check if the department lane status is requested to be included
|
||||
$include_lane_status = false;
|
||||
if (self::isParametersSet(['include_lanes']) && self::getParameter('include_lanes')) {
|
||||
$include_lane_status = true;
|
||||
}
|
||||
$response->success($departments->listObjectsWithPaginationIfSet(function ($department_array) use ($departments, $include_lane_status) {
|
||||
$additional_data = [];
|
||||
// If including lane status, fetch it
|
||||
if ($include_lane_status) {
|
||||
$department = $departments->select((int)$department_array['id']);
|
||||
$additional_data['lanes'] = array_map(
|
||||
/**
|
||||
* @param department_lanes_o $lane
|
||||
* @return array
|
||||
*/
|
||||
function (department_lanes_o $lane) {
|
||||
return [
|
||||
'id' => (int)$lane->id,
|
||||
'name' => (string)$lane->name->value(),
|
||||
'status' => (string)$lane->getLaneStatus()->name,
|
||||
];
|
||||
}, $department->getLanes());
|
||||
}
|
||||
// Only return id and name
|
||||
return [
|
||||
'id' => $department_array['id'],
|
||||
'name' => $department_array['name']
|
||||
'name' => $department_array['name'],
|
||||
'longitude' => $department_array['longitude'],
|
||||
'latitude' => $department_array['latitude'],
|
||||
'address' => $department_array['description'],
|
||||
...$additional_data
|
||||
];
|
||||
}), 200);
|
||||
},
|
||||
$departments->forceRestrictFilters([
|
||||
'visible' => 1, // Only show visible departments, this is to prevent showing internal system departments to the end-user.
|
||||
])), 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace routes;
|
||||
use classes\authentication;
|
||||
use classes\economic;
|
||||
use classes\invoice_store;
|
||||
use objects\collected_order_invoices_o;
|
||||
use objects\economic_module_orders;
|
||||
use objects\logs_o;
|
||||
use objects\orders_o;
|
||||
@@ -104,18 +105,25 @@ class invoicesRoute
|
||||
if (!$this->fromRequest('id')) {
|
||||
$response->error('id parameter is required', 400);
|
||||
}
|
||||
$order_id = (new orders_o())->getOrderByInvoiceId($this->fromRequest('id'))->id;
|
||||
// Check if the user has permission to view the invoice
|
||||
if (!$user->hasAccessToOrder($order_id)) {
|
||||
$response->error('User does not have access to this invoice', 403);
|
||||
// Get the collected order invoice object
|
||||
$collected_order_invoice = (new collected_order_invoices_o())->select((int)$this->fromRequest('id'));
|
||||
// Make sure the user owns the invoice
|
||||
if ((int)$collected_order_invoice->customer_number->value() !== (int)$user->customer_number->value()) {
|
||||
$response->error('You do not have permission to access this invoice', 403);
|
||||
}
|
||||
// Check if the invoice is booked
|
||||
$is_booked = $collected_order_invoice->isBooked();
|
||||
if (!$is_booked) {
|
||||
$response->error('Invoice is not booked yet', 400);
|
||||
}
|
||||
$booked_invoice_id = (int)$collected_order_invoice->booked_invoice_id->value();
|
||||
// Create economic object
|
||||
$economic = new economic();
|
||||
// Get the draft invoice
|
||||
$invoicePathFile = $economic->invoices->pdf->get($this->fromRequest('id'));
|
||||
$invoicePathFile = $economic->invoices->pdf->get($booked_invoice_id);
|
||||
// Add the pdf to the invoice store
|
||||
$invoice_store = new invoice_store();
|
||||
$invoice_store->uploadFile('invoice_' . $this->fromRequest('id') . '.pdf', $invoicePathFile);
|
||||
$invoice_store->uploadFile('invoice_' . $booked_invoice_id . '.pdf', $invoicePathFile);
|
||||
|
||||
// Log the incident
|
||||
(new logs_o())->add('invoices', 'global', 1, $user->id, 'GET_INVOICE_PDF', 'Successfully retrieved invoice pdf');
|
||||
@@ -123,7 +131,7 @@ class invoicesRoute
|
||||
unlink($invoicePathFile);
|
||||
// Return the download link
|
||||
$response->success(
|
||||
['message' => 'Invoice PDF retrieved', 'url' => $invoice_store->getInvoiceDownloadUrl($this->fromRequest('id'))]
|
||||
['message' => 'Invoice PDF retrieved', 'url' => $invoice_store->getInvoiceDownloadUrl($booked_invoice_id)]
|
||||
);
|
||||
} else {
|
||||
// Log the incident
|
||||
|
||||
@@ -669,5 +669,36 @@ class moduleConfigRoute
|
||||
(new logs_o())->add('shelly_config', 'global', 1, 0, 'SHELLY_CONFIG', 'No user found, or invalid session');
|
||||
}
|
||||
});
|
||||
/** Self-Serve config -> GET */
|
||||
$this->get('/selfserve/config', function () {
|
||||
global $response;
|
||||
$this->requirePermission('modules_selfserve_config');
|
||||
$user = (new authentication())->get_user();
|
||||
if ($user) {
|
||||
(new logs_o())->add('selfserve_config', 'global', 1, $user->id, 'SELFSERVE_CONFIG', 'Successfully fetched self-serve config');
|
||||
$response->success(
|
||||
(new \classes\selfserve())->config->getConfigRequest()
|
||||
);
|
||||
} else {
|
||||
(new logs_o())->add('selfserve_config', 'global', 1, 0, 'SELFSERVE_CONFIG', 'No user found, or invalid session');
|
||||
}
|
||||
},
|
||||
[
|
||||
'modules_selfserve_config' => 'Get self-serve config'
|
||||
]
|
||||
);
|
||||
$this->post('/selfserve/config', function () {
|
||||
global $response;
|
||||
$this->requirePermission('modules_selfserve_config');
|
||||
$user = (new authentication())->get_user();
|
||||
if ($user) {
|
||||
(new logs_o())->add('selfserve_config', 'global', 1, $user->id, 'SELFSERVE_CONFIG', 'Successfully updated self-serve config');
|
||||
$response->success(
|
||||
(new \classes\selfserve())->config->postConfigRequest()
|
||||
);
|
||||
} else {
|
||||
(new logs_o())->add('selfserve_config', 'global', 1, 0, 'SELFSERVE_CONFIG', 'No user found, or invalid session');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace routes;
|
||||
|
||||
use classes\authentication;
|
||||
use classes\email;
|
||||
use classes\response;
|
||||
use classes\router;
|
||||
use classes\selfserve;
|
||||
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 moduleSelfServeRoute
|
||||
{
|
||||
use route_t;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
global /** @var response $response */
|
||||
/** @var router $router */
|
||||
$router, $response;
|
||||
|
||||
|
||||
/** Modules > Self Serve > Lane > Status */
|
||||
$this->get('/modules/self-serve/lane/status', function () {
|
||||
global $response;
|
||||
self::requirePermission('modules_selfserve_lane_status_view');
|
||||
$selfserve = new selfserve();
|
||||
$lane = $selfserve->lane(1);
|
||||
$response->success([
|
||||
'id' => $lane->id,
|
||||
'status' => $lane->getLaneStatus()->name,
|
||||
'mode' => $lane->getLaneMode()->name,
|
||||
'state' => $lane->getLaneState()->name,
|
||||
'wash_start_time' => $lane->getWashStartTime(),
|
||||
'elapsed_wash_time' => $lane->getElapsedWashTime(),
|
||||
'license_plate' => $lane->getLicensePlate(),
|
||||
'customer_number' => $lane->getCustomerNumber(),
|
||||
]);
|
||||
},
|
||||
[
|
||||
'modules_selfserve_lane_status_view' => 'View self-serve lane status',
|
||||
]
|
||||
);
|
||||
|
||||
/** Modules > Self Serve > Lane > Command */
|
||||
$this->post('/modules/self-serve/lane/command', function () {
|
||||
global $response;
|
||||
self::requirePermission('modules_selfserve_lane_command_execute');
|
||||
$selfserve = new selfserve();
|
||||
// Get the request user
|
||||
$user = (new authentication())->get_user();
|
||||
$param_lane_id = 'lane_id';
|
||||
$param_command = 'command';
|
||||
// Validate parameters
|
||||
self::requireParameters([$param_lane_id, $param_command]);
|
||||
$lane_id = (int)$this->getParameter($param_lane_id);
|
||||
self::requireType($lane_id, self::type_int());
|
||||
self::requireMinValue($lane_id, 1);
|
||||
$commandParam = (string)$this->getParameter($param_command);
|
||||
self::requireType($commandParam, self::type_string());
|
||||
// Get the lane and command
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
// If the user has the bypass permission, set the lane to bypass customer number validation
|
||||
if (self::hasPermission('modules_selfserve_lane_command_bypass_customer_number_validation')) {
|
||||
$lane->setBypassCustomerNumberValidation(true);
|
||||
}
|
||||
$command = \modules\selfserve\helpers\selfserve_lane_command::tryFrom($commandParam);
|
||||
if ($command === null) {
|
||||
$response->error("Invalid command: " . $commandParam);
|
||||
}
|
||||
// Require permissions for specific commands
|
||||
switch ($command) {
|
||||
case \modules\selfserve\helpers\selfserve_lane_command::START:
|
||||
self::requirePermission('modules_selfserve_lane_command_execute_start');
|
||||
break;
|
||||
case \modules\selfserve\helpers\selfserve_lane_command::STOP:
|
||||
self::requirePermission('modules_selfserve_lane_command_execute_stop');
|
||||
break;
|
||||
case \modules\selfserve\helpers\selfserve_lane_command::RESET:
|
||||
self::requirePermission('modules_selfserve_lane_command_execute_reset');
|
||||
break;
|
||||
}
|
||||
// Execute the command
|
||||
try {
|
||||
$args = new \modules\selfserve\classes\selfserve_lane_command_arguments();
|
||||
$args->setParameters([
|
||||
...$this->getParametersAsArray(), // Pass all parameters
|
||||
'customer_number' => (int)$user->customer_number->value(), // Get customer number from request user
|
||||
]);
|
||||
$lane->execute($command, $args);
|
||||
$response->success([
|
||||
'id' => $lane->id,
|
||||
'status' => $lane->getLaneStatus()->name,
|
||||
'mode' => $lane->getLaneMode()->name,
|
||||
'state' => $lane->getLaneState()->name,
|
||||
'wash_start_time' => $lane->getWashStartTime(),
|
||||
'elapsed_wash_time' => $lane->getElapsedWashTime(),
|
||||
'license_plate' => $lane->getLicensePlate(),
|
||||
'customer_number' => $lane->getCustomerNumber(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error("Failed to execute command: " . $e->getMessage());
|
||||
}
|
||||
},
|
||||
[
|
||||
'modules_selfserve_lane_command_execute' => 'Execute self-serve lane command. This is required together with specific command permissions below.',
|
||||
'modules_selfserve_lane_command_execute_start' => 'Execute self-serve lane START command',
|
||||
'modules_selfserve_lane_command_execute_stop' => 'Execute self-serve lane STOP command',
|
||||
'modules_selfserve_lane_command_execute_reset' => 'Execute self-serve lane RESET command',
|
||||
'modules_selfserve_lane_command_bypass_customer_number_validation' => 'Bypass customer number validation when executing commands',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,68 @@ class orderRoute
|
||||
]
|
||||
);
|
||||
|
||||
$this->post('/order/wash-certificate', function () {
|
||||
// Require the user to be logged in and have permission
|
||||
global /** @var response $response */
|
||||
$response;
|
||||
$this->requirePermission('complete_bookings');
|
||||
// Get the user
|
||||
$user = (new authentication())->get_user();
|
||||
if (!$user) {
|
||||
(new logs_o())->add('orders', 'global', 1, 0, 'GENERATE_ORDER_WASH_CERTIFICATE', 'No user found, or invalid session');
|
||||
$response->error('Invalid session', 400);
|
||||
}
|
||||
|
||||
// Parse request body
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
if (!isset($data['id']) || !(int)$data['id']) {
|
||||
$response->error('Order id is required', 400);
|
||||
}
|
||||
|
||||
$orders_o = new orders_o();
|
||||
$orders_o->select((int)$data['id']);
|
||||
if (!$orders_o->exists()) {
|
||||
$response->error('Order not found', 400);
|
||||
}
|
||||
|
||||
// Check department access
|
||||
self::requireDepartmentAccess($orders_o->department_id->value());
|
||||
|
||||
// Collect optional params
|
||||
$safety_seal = null;
|
||||
if (isset($data['safety_seal']) && $data['safety_seal'] !== '') {
|
||||
$safety_seal = (int)$data['safety_seal'];
|
||||
}
|
||||
$operator = isset($data['operator']) && $data['operator'] !== '' ? (string)$data['operator'] : (string)$user->display_name->value();
|
||||
// Set the date to the creation date of the order if not provided
|
||||
if (isset($data['date']) && $data['date'] !== '') {
|
||||
$date = date('Y-m-d H:i:s', strtotime($data['date']));
|
||||
} else {
|
||||
$date = date('Y-m-d H:i:s', strtotime($orders_o->created_at->value()));
|
||||
}
|
||||
// Determine if already exists
|
||||
$already_exists = $orders_o->hasWashCertificateAttached();
|
||||
// Generate (no-op if already exists)
|
||||
$orders_o->generateWashCertificate($safety_seal, $operator, $date);
|
||||
$now_exists = $orders_o->hasWashCertificateAttached();
|
||||
// If a booking is associated, send the wash certificate email to the customer
|
||||
if ($orders_o->booking_id->value()) {
|
||||
$order_booking = $orders_o->getOrderBooking();
|
||||
$order_booking?->sendWashCertificateToCustomer();
|
||||
}
|
||||
|
||||
(new logs_o())->add('orders', $orders_o->department_id->value(), 1, $user->id, 'GENERATE_ORDER_WASH_CERTIFICATE', 'Wash certificate ' . ($already_exists ? 'already existed' : 'generated'));
|
||||
|
||||
$response->success([
|
||||
'order_id' => (int)$orders_o->id,
|
||||
'created' => !$already_exists && $now_exists,
|
||||
'already_existed' => $already_exists,
|
||||
]);
|
||||
}, [
|
||||
'complete_bookings' => 'Generate and attach a wash certificate PDF to an order',
|
||||
'department_access_:id' => 'Access to the department the order is in'
|
||||
]);
|
||||
|
||||
/**
|
||||
* $this->put('/order', function () {
|
||||
*
|
||||
|
||||
@@ -23,6 +23,20 @@ class plateScansRoute
|
||||
$this->requirePlateScannerAuth();
|
||||
// Get the plate scanner object
|
||||
$plate_scanner = (new authentication())->get_plate_scanner();
|
||||
/**
|
||||
* Define the optional request body parameters
|
||||
* - plate: The license plate to scan (string, required)
|
||||
* - bayId: The wash bay id (int, optional) - XLVASK lanes only
|
||||
*/
|
||||
$bayId = null;
|
||||
// Validate the bayId if set
|
||||
$bayIdParameterKey = 'bay_id';
|
||||
if (self::isParametersSet([$bayIdParameterKey])) {
|
||||
$bayId = self::getParameter($bayIdParameterKey);
|
||||
self::requireType($bayId, self::type_string());
|
||||
self::requireMinLength($bayIdParameterKey, 1);
|
||||
self::requireMaxLength($bayIdParameterKey, 255);
|
||||
}
|
||||
// Get the post data
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
// Check if the required fields are set
|
||||
@@ -30,11 +44,11 @@ class plateScansRoute
|
||||
$response->error('Missing required body parameter plate', 400);
|
||||
}
|
||||
// Add the number plate scanner
|
||||
(new plate_scans_o())->add($plate_scanner->id, $data['plate']);
|
||||
(new plate_scans_o())->add($plate_scanner->id, $data['plate'], $bayId);
|
||||
// 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: ' . $data['plate']);
|
||||
// Return a success message
|
||||
$response->success(['message' => 'License plate scan recorded.', 'plate' => $data['plate'], 'scanner' => $plate_scanner->name->value()], 201);
|
||||
$response->success(['message' => 'License plate scan recorded.', 'plate' => $data['plate'], 'scanner' => $plate_scanner->name->value(), 'bay_id' => $bayId], 201);
|
||||
});
|
||||
|
||||
$this->post('/numberplatescans/department', function () {
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace routes;
|
||||
use classes\economic;
|
||||
use classes\router;
|
||||
use classes\shelly;
|
||||
use classes\slack;
|
||||
use classes\virkdata;
|
||||
use modules\shelly\helpers\shelly_device_switch;
|
||||
use modules\shelly\helpers\shelly_request_body_get_states;
|
||||
@@ -18,6 +19,20 @@ class workerRoute
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$this->get('/worker/version', function () {
|
||||
global /** @var router $router */
|
||||
$response, $router;
|
||||
$response->success(['version' => redis->get('worker_target_version') ?? 'unknown'] );
|
||||
});
|
||||
$this->get('/worker/update-version', function () {
|
||||
global /** @var router $router */
|
||||
$response, $router;
|
||||
self::requirePermission('worker_update_version');
|
||||
self::requireParameters(['version']);
|
||||
$version = (string)self::getParameter('version');
|
||||
redis->set('worker_target_version', $version);
|
||||
$response->success(['message' => 'Version update functionality is not yet implemented.']);
|
||||
});
|
||||
$this->get('/worker/status', function () {
|
||||
global /** @var router $router */
|
||||
$response, $router;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"dev": {
|
||||
"api_url": "https://api.truckwash.dk:4433",
|
||||
"registration_number_1": "AB12345",
|
||||
"registration_number_2": "EC21233",
|
||||
"registration_number_3": "EC21234",
|
||||
"customer_number": "12345679",
|
||||
"customer_id": "1952",
|
||||
"bay_guid_1": "df640a07-918a-465e-abf7-c27f983d8a37",
|
||||
"department_id": "12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
### GET Invoice PDF
|
||||
@order_id = 12345
|
||||
GET {{api_url}}/invoices/pdf
|
||||
Accept: application/json
|
||||
Authorization: Bearer {{auth_token_superuser}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": {{order_id}}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
### POST /numberplatescans
|
||||
POST /numberplatescans HTTP/1.1
|
||||
Host: truckwashdev.maintenancemode.cloud
|
||||
Authorization: Bearer 2d9cc49436b716c69abb39e152f0024e6a64734d131d77eb4f87b5b5aeb542fd
|
||||
@plate = {{registration_number_1}}
|
||||
@bay_id = {{bay_guid_1}}
|
||||
POST {{api_url}}/numberplatescans
|
||||
Accept: application/json
|
||||
Authorization: Bearer {{auth_token_license_plate_scanner}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"plate": "AB123"
|
||||
"plate": "{{plate}}",
|
||||
"bay_id": "{{bay_id}}"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
### GET Invoice PDF
|
||||
@order_id = 9
|
||||
GET {{api_url}}/orders/potential-duplicates
|
||||
Accept: application/json
|
||||
Authorization: Bearer {{auth_token_superuser}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": {{order_id}}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
### POST /modules/self-serve/lane/command START
|
||||
@lane_id = 1
|
||||
POST {{api_url}}/modules/self-serve/lane/command
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"command": "START",
|
||||
"customer_number": {{customer_number}},
|
||||
"license_plate": "{{registration_number_1}}",
|
||||
"lane_id": {{lane_id}}
|
||||
}
|
||||
|
||||
|
||||
### POST /modules/self-serve/lane/command STOP
|
||||
POST {{api_url}}/modules/self-serve/lane/command
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{auth_token_superuser}}
|
||||
|
||||
{
|
||||
"command": "STOP",
|
||||
"lane_id": {{lane_id}}
|
||||
}
|
||||
|
||||
### POST /modules/self-serve/lane/command RESET
|
||||
POST {{api_url}}/modules/self-serve/lane/command
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{auth_token_superuser}}
|
||||
|
||||
{
|
||||
"command": "RESET",
|
||||
"lane_id": {{lane_id}}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ use objects\users_o;
|
||||
|
||||
trait db_object_t
|
||||
{
|
||||
public static string $asArrayCacheKey = 'as_array'; // The cache key for the asArray function
|
||||
public static int $cashierNameCacheExpiration = 300; // The cashier name cache expiration time, in seconds. Default is 1 day (86400 seconds).
|
||||
public static int $economicCustomerNameCacheExpiration = 86400; // The economic customer name cache expiration time, in seconds. Default is 1 day (86400 seconds).
|
||||
public static int $asArrayCacheExpiration = 600; // The id of the object in the database
|
||||
|
||||
@@ -249,6 +249,16 @@ trait route_t
|
||||
return self::hasPermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters as an array from the request
|
||||
* @return array
|
||||
*/
|
||||
public function getParametersAsArray(): array
|
||||
{
|
||||
global $response;
|
||||
return $response->getAllRequestParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Require permission
|
||||
* @param string $permission
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
### POST request to /account/auth/phone
|
||||
POST https://api.truckwash.dk:4433/account/auth/phone
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"phone": 42331128,
|
||||
"country_code": 45
|
||||
}
|
||||
|
||||
### GET request to order bookings (list all)
|
||||
GET https://api.truckwash.dk:4433/order-bookings
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer e9d4359673de64f6a9bc4231bf50ac9f5726ad5b3ee6fcad5f8ebbb20647c3b8
|
||||
|
||||
### GET request to order bookings (specific ID)
|
||||
GET https://api.truckwash.dk:4433/order-bookings?id=1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer e9d4359673de64f6a9bc4231bf50ac9f5726ad5b3ee6fcad5f8ebbb20647c3b8
|
||||
|
||||
### PUT request to order bookings (update specific ID)
|
||||
PUT https://api.truckwash.dk:4433/order-bookings
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer e9d4359673de64f6a9bc4231bf50ac9f5726ad5b3ee6fcad5f8ebbb20647c3b8
|
||||
|
||||
{
|
||||
"id": 4,
|
||||
"note": "Updated note 3",
|
||||
"reference": "Updated ref 3",
|
||||
"reg_2": null
|
||||
}
|
||||
|
||||
### POST request to complete an order booking
|
||||
POST https://api.truckwash.dk:4433/order-bookings/complete
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer e9d4359673de64f6a9bc4231bf50ac9f5726ad5b3ee6fcad5f8ebbb20647c3b8
|
||||
|
||||
{
|
||||
"id": 3,
|
||||
"safety_seal": 123456
|
||||
}
|
||||
|
||||
###
|
||||
### POST request to order bookings
|
||||
POST https://api.truckwash.dk:4433/order-bookings
|
||||
Accept: application/json, text/plain, */*
|
||||
Accept-Language: en-GB,en;q=0.6
|
||||
Authorization: Bearer e9d4359673de64f6a9bc4231bf50ac9f5726ad5b3ee6fcad5f8ebbb20647c3b8
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"customer_number": 12345679,
|
||||
"department": 2,
|
||||
"reg_1": "EC21233",
|
||||
"reg_2": null,
|
||||
"reg_3": "Abc",
|
||||
"datetime": "2025-11-11",
|
||||
"note": "Note 123",
|
||||
"reference": "Reference 123",
|
||||
"po": "PO 123",
|
||||
"pickup": true,
|
||||
"items": [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Forvogn",
|
||||
"description": " ",
|
||||
"price": 649,
|
||||
"subscription_allowed": true,
|
||||
"category": 6,
|
||||
"piktogram": "05",
|
||||
"economic_product_id": 5,
|
||||
"apply_category_discount": true,
|
||||
"requires_note": false,
|
||||
"created_at": "2024-12-09 14:31:48",
|
||||
"updated_at": "2025-10-28 13:25:21",
|
||||
"addons": [],
|
||||
"is_wash": true,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 10,
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Indvendig vask Forvogn",
|
||||
"description": " ",
|
||||
"price": 399,
|
||||
"subscription_allowed": 0,
|
||||
"category": "2",
|
||||
"piktogram": "",
|
||||
"economic_product_id": "11",
|
||||
"apply_category_discount": true,
|
||||
"requires_note": false,
|
||||
"is_wash": true,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 1022,
|
||||
"created_at": "2024-12-09 14:31:49",
|
||||
"updated_at": "2025-10-28 13:56:30",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"name": "Undervognskyld pr. Enhed",
|
||||
"description": " ",
|
||||
"price": 79,
|
||||
"subscription_allowed": 1,
|
||||
"category": "4",
|
||||
"piktogram": "",
|
||||
"economic_product_id": "23",
|
||||
"apply_category_discount": false,
|
||||
"requires_note": false,
|
||||
"is_wash": false,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 1018,
|
||||
"created_at": "2024-12-09 14:31:49",
|
||||
"updated_at": "2025-10-28 13:54:41",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "Fælg Flex pr. Enhed",
|
||||
"description": " ",
|
||||
"price": 39,
|
||||
"subscription_allowed": 0,
|
||||
"category": "4",
|
||||
"piktogram": "",
|
||||
"economic_product_id": "40",
|
||||
"apply_category_discount": false,
|
||||
"requires_note": false,
|
||||
"is_wash": false,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 1019,
|
||||
"created_at": "2024-12-09 14:31:50",
|
||||
"updated_at": "2025-10-28 13:55:00",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "Spot Free- Lastbil",
|
||||
"description": " ",
|
||||
"price": 39,
|
||||
"subscription_allowed": 1,
|
||||
"category": "4",
|
||||
"piktogram": "",
|
||||
"economic_product_id": "33",
|
||||
"apply_category_discount": false,
|
||||
"requires_note": false,
|
||||
"is_wash": false,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 1017,
|
||||
"created_at": "2024-12-09 14:31:49",
|
||||
"updated_at": "2025-10-28 13:54:25",
|
||||
"quantity": 3
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"name": "Double Duty - Kemi",
|
||||
"description": " ",
|
||||
"price": 239,
|
||||
"subscription_allowed": 0,
|
||||
"category": "4",
|
||||
"piktogram": "",
|
||||
"economic_product_id": "24",
|
||||
"apply_category_discount": false,
|
||||
"requires_note": true,
|
||||
"is_wash": false,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 1021,
|
||||
"created_at": "2024-12-09 14:31:49",
|
||||
"updated_at": "2025-10-28 13:55:58",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Trailer",
|
||||
"description": "",
|
||||
"price": 599,
|
||||
"subscription_allowed": 1,
|
||||
"category": "6",
|
||||
"piktogram": "02",
|
||||
"economic_product_id": "2",
|
||||
"apply_category_discount": true,
|
||||
"requires_note": false,
|
||||
"is_wash": true,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 50,
|
||||
"created_at": "2024-12-03 09:07:48",
|
||||
"updated_at": "2025-10-28 13:25:52",
|
||||
"quantity": 5
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Dolly",
|
||||
"description": "",
|
||||
"price": 275,
|
||||
"subscription_allowed": 0,
|
||||
"category": "6",
|
||||
"piktogram": "04",
|
||||
"economic_product_id": "4",
|
||||
"apply_category_discount": true,
|
||||
"requires_note": false,
|
||||
"is_wash": true,
|
||||
"display_in_booking_form": true,
|
||||
"order_priority": 90,
|
||||
"created_at": "2024-12-06 14:36:23",
|
||||
"updated_at": "2025-10-28 13:26:24",
|
||||
"quantity": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user