Add multiple XLVask parsers, extend department and order selection logic, and add usage synchronization endpoint
This commit is contained in:
@@ -0,0 +1,512 @@
|
||||
<?php
|
||||
|
||||
namespace helpers;
|
||||
|
||||
use Exception;
|
||||
use objects\departments_o;
|
||||
use objects\orders_o;
|
||||
|
||||
class xlvask_usage_log
|
||||
{
|
||||
/**
|
||||
* Example of a usage log object:
|
||||
* [WashId] => 8cb0b5a7-ab4b-4dd5-b805-869733c23e54
|
||||
* [CustomerId] => 59440200
|
||||
* [Customer] => DITOBUS EXCURSIONS A/S
|
||||
* [VatNumber] => 31171520
|
||||
* [Location] => Hvidovre
|
||||
* [Hall] => Hvidovre_1
|
||||
* [HallId] => 845d29a1-a7d2-4e3b-bbc3-2b13242d744a
|
||||
* [StartTime] => 2025-06-02T15:20:14.950
|
||||
* [FinishTime] => 2025-06-02T15:25:55.377
|
||||
* [RegistrationNumber] => CZ92915
|
||||
* [VehicleType] => Bus/autocamper, M
|
||||
* [IdentificationType] => LPR
|
||||
* [IdentificationId] => CZ92915
|
||||
* [Info] => CZ92915
|
||||
* [Updated] =>
|
||||
* [Prepaid] =>
|
||||
* [FinishStatus] => 1
|
||||
* [CustomerGuid] => 21ba156a-b2d2-44be-8398-4b67d66003d6
|
||||
* [VehicleId] => e6679209-e52f-4376-9660-d56cb46f523b
|
||||
* [WashItems] => Array[washItem] => Array
|
||||
*/
|
||||
|
||||
/**
|
||||
* The wash ID of the usage log entry
|
||||
* @var int|string|null $WashId
|
||||
*/
|
||||
public int|string|null $WashId;
|
||||
/**
|
||||
* The ID of the customer (XLVask ID)
|
||||
* @var int|string|null $CustomerId
|
||||
*/
|
||||
public int|string|null $CustomerId;
|
||||
/**
|
||||
* The name of the customer
|
||||
* @var int|string|null $Customer
|
||||
*/
|
||||
public int|string|null $Customer;
|
||||
/**
|
||||
* The VAT number of the customer
|
||||
* @var int|string|null $VatNumber
|
||||
*/
|
||||
public int|string|null $VatNumber;
|
||||
/**
|
||||
* The location of the wash
|
||||
* @var int|string|null $Location
|
||||
*/
|
||||
public int|string|null $Location;
|
||||
/**
|
||||
* The hall where the wash took place
|
||||
* @var int|string|null $Hall
|
||||
*/
|
||||
public int|string|null $Hall;
|
||||
/**
|
||||
* The ID of the hall where the wash took place
|
||||
* @var int|string|null $HallId
|
||||
*/
|
||||
public int|string|null $HallId;
|
||||
/**
|
||||
* The start time of the wash
|
||||
* @var string|int|null $StartTime
|
||||
*/
|
||||
public string|int|null $StartTime;
|
||||
/**
|
||||
* The finish time of the wash
|
||||
* @var string|int|null $FinishTime
|
||||
*/
|
||||
public string|int|null $FinishTime;
|
||||
/**
|
||||
* The registration number of the vehicle
|
||||
* @var int|string|null $RegistrationNumber
|
||||
*/
|
||||
public int|string|null $RegistrationNumber;
|
||||
/**
|
||||
* The type of the vehicle
|
||||
* @var string|int|null $VehicleType
|
||||
*/
|
||||
public string|int|null $VehicleType;
|
||||
/**
|
||||
* The type of identification used for the wash
|
||||
* @var string|int|null $IdentificationType
|
||||
*/
|
||||
public string|int|null $IdentificationType;
|
||||
/**
|
||||
* The ID of the identification used for the wash
|
||||
* @var string|int|null $IdentificationId
|
||||
*/
|
||||
public string|int|null $IdentificationId;
|
||||
/**
|
||||
* Additional information about the wash
|
||||
* @var string|int|null $Info
|
||||
*/
|
||||
public string|int|null $Info;
|
||||
/**
|
||||
* The last updated time of the wash entry
|
||||
* @var string|int|null $Updated
|
||||
*/
|
||||
public string|int|null $Updated;
|
||||
/**
|
||||
* Indicates if the wash was prepaid
|
||||
* @var int|string|null $Prepaid
|
||||
*/
|
||||
public int|string|null $Prepaid;
|
||||
/**
|
||||
* The finish status of the wash
|
||||
* @var int|string|null $FinishStatus
|
||||
*/
|
||||
public int|string|null $FinishStatus;
|
||||
/**
|
||||
* The GUID of the customer in XL Vask
|
||||
* @var string|int|null $CustomerGuid
|
||||
*/
|
||||
public string|int|null $CustomerGuid;
|
||||
/**
|
||||
* The ID of the vehicle associated with the wash
|
||||
* @var string|int|null $vehicleId
|
||||
*/
|
||||
public string|int|null $VehicleId;
|
||||
/**
|
||||
* The wash items associated with the wash
|
||||
* @var array[washItem] $washItems
|
||||
* @see xlvask_wash_item
|
||||
*/
|
||||
public array $WashItems;
|
||||
|
||||
protected string $default_string = 'DEFAULT_STRING_1';
|
||||
protected string $default_int = 'DEFAULT_INT_1';
|
||||
protected string $default_bool = 'DEFAULT_BOOL_1';
|
||||
protected string $default_int_nullable = 'DEFAULT_INT_NULLABLE_1';
|
||||
protected string $default_string_nullable = 'DEFAULT_STRING_NULLABLE_1';
|
||||
protected string $default_bool_nullable = 'DEFAULT_BOOL_NULLABLE_1';
|
||||
|
||||
/**
|
||||
* Constructor to initialize the customer object with default values
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->default_string = 'DEFAULT_STRING_1';
|
||||
$this->default_int = 'DEFAULT_INT_1';
|
||||
$this->default_bool = 'DEFAULT_BOOL_1';
|
||||
$this->default_int_nullable = 'DEFAULT_INT_NULLABLE_1';
|
||||
$this->default_string_nullable = 'DEFAULT_STRING_NULLABLE_1';
|
||||
$this->default_bool_nullable = 'DEFAULT_BOOL_NULLABLE_1';
|
||||
// Initialize the customer object with default values
|
||||
$this::reset();
|
||||
$this::setProperties($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this object to its default values.
|
||||
* This method clears all properties of the usage log object,
|
||||
* effectively resetting it to a clean state.
|
||||
* @return void
|
||||
*/
|
||||
private function reset(): void
|
||||
{
|
||||
$this->WashId = $this->default_string;
|
||||
$this->CustomerId = $this->default_string;
|
||||
$this->Customer = $this->default_string;
|
||||
$this->VatNumber = $this->default_string;
|
||||
$this->Location = $this->default_string;
|
||||
$this->Hall = $this->default_string;
|
||||
$this->HallId = $this->default_string;
|
||||
$this->StartTime = $this->default_string;
|
||||
$this->FinishTime = $this->default_string;
|
||||
$this->RegistrationNumber = $this->default_string;
|
||||
$this->VehicleType = $this->default_string;
|
||||
$this->IdentificationType = $this->default_string;
|
||||
$this->IdentificationId = $this->default_string;
|
||||
$this->Info = $this->default_string;
|
||||
$this->Updated = $this->default_string_nullable;
|
||||
$this->Prepaid = $this->default_bool;
|
||||
$this->FinishStatus = $this->default_int;
|
||||
$this->CustomerGuid = $this->default_string;
|
||||
$this->VehicleId = $this->default_string;
|
||||
$this->WashItems = []; // Initialize as an empty array
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties of the object based on the provided data.
|
||||
* @note This method DOES NOT reset the object before setting the properties.
|
||||
* @param array $data
|
||||
* @return self
|
||||
* @throws Exception
|
||||
* @see reset()
|
||||
*/
|
||||
public function setProperties(array $data): self
|
||||
{
|
||||
$default_property_values = [
|
||||
'WashId' => $this->default_string,
|
||||
'CustomerId' => $this->default_string,
|
||||
'Customer' => $this->default_string,
|
||||
'VatNumber' => $this->default_string,
|
||||
'Location' => $this->default_string,
|
||||
'Hall' => $this->default_string,
|
||||
'HallId' => $this->default_string,
|
||||
'StartTime' => $this->default_string,
|
||||
'FinishTime' => $this->default_string,
|
||||
'RegistrationNumber' => $this->default_string,
|
||||
'VehicleType' => $this->default_string,
|
||||
'IdentificationType' => $this->default_string,
|
||||
'IdentificationId' => $this->default_string,
|
||||
'Info' => $this->default_string,
|
||||
'Updated' => $this->default_string_nullable,
|
||||
'Prepaid' => $this->default_bool,
|
||||
'FinishStatus' => $this->default_int,
|
||||
'CustomerGuid' => $this->default_string,
|
||||
'VehicleId' => $this->default_string,
|
||||
];
|
||||
foreach ( $data as $key => $value ) {
|
||||
if (property_exists(self::class, $key)) {
|
||||
switch ($default_property_values[$key] ?? null) {
|
||||
case $this->default_string:
|
||||
case $this->default_string_nullable:
|
||||
$nullable = $default_property_values[$key] === $this->default_string_nullable;
|
||||
// Check if the value is a string or can be converted to a string
|
||||
if (is_string($value) || is_numeric($value)) {
|
||||
$value = (string)$value; // Ensure the value is a string
|
||||
} elseif ($nullable) {
|
||||
$value = null; // If nullable, set to null
|
||||
} else {
|
||||
$value = ''; // If not nullable, set to empty string
|
||||
}
|
||||
break;
|
||||
case $this->default_int:
|
||||
case $this->default_int_nullable:
|
||||
$nullable = $default_property_values[$key] === $this->default_int_nullable;
|
||||
// Check if the value is numeric and convert it to an integer or null
|
||||
$value = is_numeric($value) ? (int)$value : ($nullable ? null : 0); // Ensure the value is an integer or null
|
||||
break;
|
||||
case $this->default_bool:
|
||||
case $this->default_bool_nullable:
|
||||
$nullable = $default_property_values[$key] === $this->default_bool_nullable;
|
||||
// Check if the value is a boolean or can be converted to a boolean
|
||||
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
if ($value === null && !$nullable) {
|
||||
$value = false; // If the value is not a boolean and not nullable, set it to false
|
||||
}
|
||||
break;
|
||||
case null:
|
||||
break;
|
||||
default:
|
||||
// If the value is not a default string, keep it as is
|
||||
echo "No conversion needed for property '$key' with value '$value'." . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
// Set the property value
|
||||
$this->{$key} = $value;
|
||||
} else {
|
||||
// If the property does not exist, throw an exception
|
||||
// Debug print all properties of the class
|
||||
//$properties = get_class_vars(self::class);
|
||||
//echo "Available properties in " . self::class . ": " . implode(', ', array_keys($properties)) . PHP_EOL;
|
||||
throw new Exception("Property '$key' does not exist in " . self::class);
|
||||
}
|
||||
}
|
||||
// If the washItems property is set, ensure it is an array
|
||||
if (isset($data['WashItems']) && is_array($data['WashItems'])) {
|
||||
//echo 'Setting WashItems with ' . count($this->WashItems) . ' items.' . PHP_EOL;
|
||||
$this->WashItems = self::generateWashItems($data['WashItems']);
|
||||
//print_r($this->WashItems);
|
||||
} else {
|
||||
//echo 'No washItems provided or not an array. Initializing as empty array.' . PHP_EOL;
|
||||
//print_r($data);
|
||||
}
|
||||
// After setting all properties, nullify nullable properties
|
||||
$this->unsetNullifiableProperties();
|
||||
// Return the populated object
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate wash items from the provided array.
|
||||
* This method takes an array of wash items and converts them into instances of xlvask_wash_item.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function generateWashItems(array $washItems): array
|
||||
{
|
||||
// Generate wash items from the provided array
|
||||
$generatedItems = [];
|
||||
foreach ( $washItems as $item ) {
|
||||
if (is_array($item)) {
|
||||
$generatedItems[] = new xlvask_wash_item($item);
|
||||
} elseif ($item instanceof xlvask_wash_item) {
|
||||
$generatedItems[] = $item; // If it's already an instance, add it
|
||||
} else {
|
||||
throw new Exception("Invalid wash item type: " . gettype($item));
|
||||
}
|
||||
}
|
||||
// Sort the generated items with the sortWashItems method
|
||||
return self::sortWashItems($generatedItems);
|
||||
}
|
||||
|
||||
private static function sortWashItems(array $generatedItems): array
|
||||
{
|
||||
// Make sure the primary wash item is always first
|
||||
$primaryItems = [
|
||||
'Stor bil',
|
||||
'Lille bil',
|
||||
];
|
||||
usort($generatedItems, function ($a, $b) use ($primaryItems) {
|
||||
// Check if the item is a primary item
|
||||
$aIsPrimary = in_array($a->OriginalProductName, $primaryItems, true);
|
||||
$bIsPrimary = in_array($b->OriginalProductName, $primaryItems, true);
|
||||
// If both are primary items, keep their order
|
||||
if ($aIsPrimary && $bIsPrimary) {
|
||||
return 0;
|
||||
}
|
||||
// If only one is a primary item, it should come first
|
||||
return $aIsPrimary ? -1 : 1;
|
||||
});
|
||||
// Return the sorted array of wash items
|
||||
return $generatedItems;
|
||||
}
|
||||
|
||||
private function unsetNullifiableProperties(): void
|
||||
{
|
||||
// Unset properties that are null or empty strings
|
||||
$properties = [
|
||||
'WashId', 'CustomerId', 'Customer', 'VatNumber', 'Location',
|
||||
'Hall', 'HallId', 'StartTime', 'FinishTime', 'RegistrationNumber',
|
||||
'VehicleType', 'IdentificationType', 'IdentificationId', 'Info',
|
||||
'Updated', 'Prepaid', 'FinishStatus', 'CustomerGuid', 'VehicleId'
|
||||
];
|
||||
foreach ( $properties as $property ) {
|
||||
if ($this->isEmptyOrDefault($this->{$property})) {
|
||||
$tmp_value = $this->{$property};
|
||||
if ($tmp_value === $this->default_string || $tmp_value === $this->default_string_nullable) {
|
||||
$this->{$property} = ''; // Set to null if it matches the default string
|
||||
} elseif ($tmp_value === $this->default_int || $tmp_value === $this->default_int_nullable) {
|
||||
if ($tmp_value === $this->default_int_nullable) {
|
||||
$this->{$property} = null; // Set to null if it matches the default int nullable
|
||||
} else {
|
||||
$this->{$property} = 0; // Set to 0 if it matches the default int
|
||||
}
|
||||
} elseif ($tmp_value === $this->default_bool || $tmp_value === $this->default_bool_nullable) {
|
||||
$this->{$property} = false; // Set to null if it matches the default bool
|
||||
} else {
|
||||
$this->{$property} = null; // Otherwise, set to null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isEmptyOrDefault($param): bool
|
||||
{
|
||||
// Check if the parameter is empty or matches the default values
|
||||
return $param === null || $param === '' || $param === $this->default_string || $param === $this->default_int || $param === $this->default_bool || $param === $this->default_int_nullable || $param === $this->default_string_nullable || $param === $this->default_bool_nullable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the customer object to an array representation.
|
||||
* This method returns an associative array containing all properties of the object.
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return (array)$this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted date of the wash start time.
|
||||
* @return string The formatted date of the wash start time. YYYY-MM-DD HH:MM:SS format.
|
||||
* @note Returns an empty string if StartTime is not set.
|
||||
*/
|
||||
public function getFormattedDate(): string
|
||||
{
|
||||
// Format the date in a human-readable format
|
||||
if ($this->StartTime) {
|
||||
return date('Y-m-d H:i:s', strtotime($this->StartTime));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted finish date of the wash.
|
||||
* @return string The formatted finish date of the wash. YYYY-MM-DD HH:MM:SS format.
|
||||
* @note Returns an empty string if FinishTime is not set.
|
||||
*/
|
||||
public function getFormattedFinishDate(): string
|
||||
{
|
||||
// Format the finish date in a human-readable format
|
||||
if ($this->FinishTime) {
|
||||
return date('Y-m-d H:i:s', strtotime($this->FinishTime));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total price of the wash.
|
||||
* This method calculates the total price of the wash by summing up the prices of all wash items.
|
||||
* @return float The total price of the wash.
|
||||
* @throws Exception If any of the wash items do not have a valid price.
|
||||
*/
|
||||
public function getTotalPrice(): float
|
||||
{
|
||||
// Calculate the total price of the wash by summing up the prices of all wash items
|
||||
$totalPrice = 0.0;
|
||||
foreach ( $this->WashItems as $item ) {
|
||||
/** @var xlvask_wash_item $item */
|
||||
if (isset($item->PriceIncVat) && is_numeric($item->PriceIncVat)) {
|
||||
// The reason why we use the PriceIncVat is that it is the final price, and the price without VAT is not always available / correct.
|
||||
$totalPrice += ((float)$item->PriceIncVat - (float)$item->Vat);
|
||||
} else {
|
||||
//print_r($item);
|
||||
throw new Exception("Invalid price for wash item: " . json_encode($item));
|
||||
}
|
||||
}
|
||||
return $totalPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the wash is completed.
|
||||
* @note This method checks the FinishStatus property to determine if the wash is completed.
|
||||
* * A wash is considered completed if the FinishStatus is set to 1.
|
||||
* @return bool
|
||||
*/
|
||||
public function isCompleted(): bool
|
||||
{
|
||||
// Check if the wash is completed based on the FinishStatus
|
||||
return $this->FinishStatus === 1; // Assuming 1 indicates a completed wash
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary wash item from the usage log.
|
||||
* This method returns the first wash item in the WashItems array as the primary item.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getPrimaryItem(): xlvask_wash_item
|
||||
{
|
||||
// Return the first wash item as the primary item
|
||||
if (isset($this->WashItems[0]) && $this->WashItems[0] instanceof xlvask_wash_item) {
|
||||
return $this->WashItems[0];
|
||||
}
|
||||
throw new Exception("No primary wash item found in the usage log.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the wash linked to an order?
|
||||
* @return bool
|
||||
* @note This method checks if an order exists with the "wash_id" field matching the WashId of this usage log.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function isLinkedToOrder(): bool
|
||||
{
|
||||
// Check if the wash is linked to an order by checking the "wash_id" field in the orders table
|
||||
$order = (new orders_o())->selectByWashId($this->WashId);
|
||||
return !empty($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order associated with this wash.
|
||||
* @return orders_o|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getOrder(): ?orders_o
|
||||
{
|
||||
// Get the order associated with this wash by checking the "wash_id" field in the orders table
|
||||
$order = (new orders_o())->selectByWashId($this->WashId);
|
||||
if ($order) {
|
||||
return $order;
|
||||
}
|
||||
return null; // Return null if no order is found
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the department associated with this wash.
|
||||
* @throws Exception If the department cannot be determined.
|
||||
*/
|
||||
public function getDepartment(): ?departments_o
|
||||
{
|
||||
// Since the department / hall XLVASK API endpoints are not available,
|
||||
// We will try to get the department from the hall name.
|
||||
// These are formatted as "DepartmentName_HallNumber" (E.g. "Hvidovre_1").
|
||||
if (!empty($this->Hall)) {
|
||||
$parts = explode('_', $this->Hall);
|
||||
if (count($parts) > 1) {
|
||||
$departmentName = $parts[0];
|
||||
return (new departments_o())->selectByName($departmentName);
|
||||
}
|
||||
}
|
||||
// If the hall is not set or does not contain a department name, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getLane(): ?int
|
||||
{
|
||||
// Since the department / hall XLVASK API endpoints are not available,
|
||||
// We will try to get the lane from the hall name.
|
||||
// These are formatted as "DepartmentName_HallNumber" (E.g. "Hvidovre_1").
|
||||
if (!empty($this->Hall)) {
|
||||
$parts = explode('_', $this->Hall);
|
||||
if (count($parts) > 1 && is_numeric($parts[1])) {
|
||||
return (int)$parts[1]; // Return the lane number as an integer
|
||||
}
|
||||
}
|
||||
// If the hall is not set or does not contain a lane number, return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user