Add multiple XLVask parsers, extend department and order selection logic, and add usage synchronization endpoint

This commit is contained in:
Jepp9350
2025-06-10 14:52:47 +02:00
parent af75d38af9
commit 39e24465c4
23 changed files with 1676 additions and 7 deletions
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_2_b_rstevask extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('2_b_rstevask');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(63); // 2 Børstevask product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_3_b_rstevask extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('3_b_rstevask');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_eu_spejl extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('EU spejl');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Other irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_halleje extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Halleje');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(62); // Halleje product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_ht_chassis extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('HT chassis');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_ht_osc_sider_verst extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('ht_osc_sider_verst');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_ht_sider extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('HT sider');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Other irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_ikke_ht_dysebom_bag extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Ikke HT dysebom bag');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Other irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_ikke_ht_dysebom_p_front extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Ikke HT dysebom p\u00e5 front');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Other irrelevant product ID
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_lift extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('lift');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Irrelevant product ID for lift
}
}
@@ -0,0 +1,26 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_parser_s_be_dysebom extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('s_be_dysebom');
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
return (new products_o())->select(64); // Other irrelevant product ID
}
}
@@ -0,0 +1,44 @@
<?php
namespace helpers;
use Exception;
use objects\product_options_o;
use objects\products_o;
class xlvask_parser_skylning_med_ro extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Skylning med RO'); // This is the name of Spot free product(s)
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
// This parser is used for the "Skylning med RO" product, which is a spot-free rinse.
// There are two different products that can be used for this, depending on the vehicle type.
// Spot Free- Varevogn (23)
// Spot Free- Lastbil (24)
// This means that we need to check the vehicle type and return the appropriate product.
$primary_product = $xlvask_usage_log->getPrimaryItem()->getProduct($xlvask_usage_log);
$primary_product_id = $primary_product->id;
// Get the available addons for the primary product
$product_options = new product_options_o();
if ($product_options->isOptionAllowedOnType(23, $primary_product_id)) {
// If the primary product is a varevogn (van), return the Spot Free- Varevogn product
return (new products_o())->select(23); // Spot Free- Varevogn product ID
} elseif ($product_options->isOptionAllowedOnType(24, $primary_product_id)) {
// If the primary product is a lastbil (truck), return the Spot Free- Lastbil product
return (new products_o())->select(24); // Spot Free- Lastbil product ID
}
// Throw an exception if the vehicle type is not supported
throw new Exception('Unsupported vehicle type for Skylning med RO product.');
}
}
@@ -0,0 +1,229 @@
<?php
namespace helpers;
use objects\products_o;
class xlvask_parser_stor_bil extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Stor bil');
}
/**
* @inheritDoc
* @throws \Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
//echo 'Attempting to parse Stor Bil product: ' . $this->OriginalProductName . PHP_EOL;
//echo ' Step 1: Validating wash item and usage log.' . PHP_EOL;
$registrationNumber = $xlvask_usage_log->RegistrationNumber;
$vehicleType = $xlvask_usage_log->VehicleType; // E.g. "Bus/autocamper, M"
/**
* All the vehicle types that this parser supports.
* Copied from XL Vask's vehicle types @ 10:32 10/06/2025
* **
* Anlægsmaskine
* Bus/autocamper, L
* Bus/autocamper, M
* Bus/autocamper, S
* Containerbil
* Containerbil med hænger
* Containertræk
* Entreprenør forvogn/trækker
* Entreprenør sættevogn
* Forvogn med hænger
* Forvogn med hænger, autotransport
* Forvogn med krogehejs
* Forvogn, L
* Forvogn, lave sider
* Forvogn, M
* Forvogn, S
* Forvogn, skraldebil
* Kassevogn/Varevogn, L
* Kassevogn/Varevogn, S
* Kun trailer
* Landbrugstraktor
* Minibus
* Personbil
* Pickup
* Sættevognstræk
* Sættevognstræk, maskintrailer
* Tankvogn
* Tankvogn, Slamsuger
* Tankvognstræk
* Tankvognstræk, silobil
* Trækker
* Tømmerbil
*/
$vehicleTypes = [
// Formats for vehicle types
// 'exact match' => ['Vehicle Type 1', 'Vehicle Type 2'], - Matches when the vehicle type is exactly one of these.
// 'contains' => ['Vehicle Type 3', 'Vehicle Type 4'], - Matches when the vehicle type contains any of these substrings.
// 'matchFunction' => function($vehicleType) { return true; } - Matches when the vehicle type passes this function.
// product_id => ['exact match' => ['Vehicle Type 1', 'Vehicle Type 2'], 'contains' => ['Vehicle Type 3', 'Vehicle Type 4'], 'matchFunction' => function]
// Trækker (1)
1 => [
'exact match' => ['Trækker'],
'contains' => ['Trækker'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Trækker$/', $vehicleType);
}
],
// Trailer (2)
2 => [
'exact match' => ['Kun trailer'],
'contains' => ['Trailer'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Kun trailer$/', $vehicleType);
}
],
// Sættevognstræk (3)
3 => [
'exact match' => ['Sættevognstræk'],
'contains' => ['Sættevognstræk'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Sættevognstræk$/', $vehicleType);
}
],
// Dolly (4)
4 => [
'exact match' => ['Dolly'],
'contains' => ['Dolly'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Dolly$/', $vehicleType);
}
],
// Forvogn (5)
5 => [
'exact match' => ['Forvogn, L', 'Forvogn, M', 'Forvogn, S', 'Forvogn, lave sider', 'Forvogn med krogehejs', 'Forvogn, skraldebil'],
'contains' => ['Forvogn'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Forvogn/', $vehicleType);
}
],
// Forvogn med hænger (6)
6 => [
'exact match' => ['Forvogn med hænger', 'Forvogn med hænger, autotransport'],
'contains' => ['Forvogn med hænger'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Forvogn med hænger/', $vehicleType);
}
],
// Tankvogn (7)
7 => [
'exact match' => ['Tankvogn', 'Tankvogn, Slamsuger'],
'contains' => ['Tankvogn'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Tankvogn/', $vehicleType);
}
],
// Tankvogn med hænger (8)
8 => [
'exact match' => ['Tankvognstræk', 'Tankvognstræk, silobil'],
'contains' => ['Tankvognstræk'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Tankvognstræk/', $vehicleType);
}
],
// Tankvognstræk (9)
9 => [
'exact match' => ['Tankvognstræk', 'Tankvognstræk, silobil'],
'contains' => ['Tankvognstræk'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Tankvognstræk/', $vehicleType);
}
],
// Kassevogn/Varevogn (15)
15 => [
'exact match' => ['Kassevogn/Varevogn, L', 'Kassevogn/Varevogn, S'],
'contains' => ['Kassevogn', 'Varevogn'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Kassevogn\/Varevogn/', $vehicleType);
}
],
// Campingvogn/Autocamper (16)
16 => [
'exact match' => ['Bus/autocamper, L', 'Bus/autocamper, M', 'Bus/autocamper, S'],
'contains' => ['Bus', 'Autocamper'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Bus\/autocamper/', $vehicleType);
}
],
// Bus (17)
17 => [
'exact match' => ['Bus/autocamper, L', 'Bus/autocamper, M', 'Bus/autocamper, S'],
'contains' => ['Bus'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Bus\/autocamper/', $vehicleType);
}
],
// Entreprenør forvogn (18)
18 => [
'exact match' => ['Entreprenør forvogn/trækker'],
'contains' => ['Entreprenør forvogn', 'Trækker'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Entreprenør forvogn/', $vehicleType);
}
],
// Entreprenør forvogn med kærre (19)
19 => [
'exact match' => ['Entreprenør sættevogn'],
'contains' => ['Entreprenør sættevogn'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Entreprenør sættevogn$/', $vehicleType);
}
],
// Entreprenør sættevogn (20)
20 => [
'exact match' => ['Entreprenør sættevogn'],
'contains' => ['Entreprenør sættevogn'],
'matchFunction' => function ($vehicleType) {
return preg_match('/^Entreprenør sættevogn$/', $vehicleType);
}
],
];
// Check if the vehicle type matches any of the defined vehicle types
$isValidVehicleType = false;
$productId = null; // Initialize product_id to null
foreach ( $vehicleTypes as $product_id => $conditions ) {
if (in_array($vehicleType, $conditions['exact match'], true)) {
$isValidVehicleType = true;
$productId = $product_id; // Set the product_id if an exact match is found
break;
}
foreach ( $conditions['contains'] as $substring ) {
if (strpos($vehicleType, $substring) !== false) {
$isValidVehicleType = true;
$productId = $product_id; // Set the product_id if a substring match is found
break 2; // Break out of both loops
}
}
if (isset($conditions['matchFunction']) && is_callable($conditions['matchFunction']) && $conditions['matchFunction']($vehicleType)) {
$isValidVehicleType = true;
$productId = $product_id; // Set the product_id if the match function returns true
break;
}
}
if (!$isValidVehicleType) {
//echo 'Step 2: Vehicle type does not match any of the defined vehicle types.' . PHP_EOL;
throw new \Exception('Vehicle type does not match any of the defined vehicle types.');
}
//echo ' Found matching vehicle type: ' . $vehicleType . PHP_EOL;
//echo ' Step 3: Vehicle type is valid, proceeding with product creation.' . PHP_EOL;
// Check if the vehicle
//print_r($xlvask_usage_log);
// Create a new product object
return (new products_o())->select((int)$productId);
}
}
@@ -0,0 +1,36 @@
<?php
namespace helpers;
use Exception;
use objects\product_options_o;
use objects\products_o;
class xlvask_parser_undervognskylning extends xlvask_product_parser
{
use xlvask_product_parser_t;
public function __construct()
{
$this->setup('Undervognskylning'); // This is the name of Spot free product(s)
}
/**
* @inheritDoc
* @throws Exception
*/
protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
// This means that we need to check the vehicle type and return the appropriate product.
$primary_product = $xlvask_usage_log->getPrimaryItem()->getProduct($xlvask_usage_log);
$primary_product_id = $primary_product->id;
// Get the available addons for the primary product
$product_options = new product_options_o();
if ($product_options->isOptionAllowedOnType(21, $primary_product_id)) {
return (new products_o())->select(21); // Undervognskylning product ID
}
// Throw an exception if the vehicle type is not supported
throw new Exception('Unsupported vehicle type for Undervognskylning');
}
}
@@ -0,0 +1,23 @@
<?php
namespace helpers;
use Exception;
abstract class xlvask_product_parser
{
use xlvask_product_parser_t;
/**
* The setup method initializes the Original Product Name.
* This is required for the parser to function correctly.
* @throws Exception If the Original Product Name is empty or null.
*/
public function setup(string $OriginalProductName): void
{
if (empty($OriginalProductName)) {
throw new Exception('The Original Product Name cannot be empty or null. Please provide a valid product name.');
}
$this->OriginalProductName = $OriginalProductName;
}
}
@@ -0,0 +1,44 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
trait xlvask_product_parser_t
{
protected string $OriginalProductName;
/**
* Parse the Original Product Name to a product object.
* @param xlvask_wash_item $wash_item
* @param xlvask_usage_log $xlvask_usage_log
* @return products_o
* @throws Exception If the product cannot be parsed.
*/
public function parseProduct(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o
{
// Make sure the Original Product Name is set
if (empty($this->OriginalProductName)) {
throw new Exception('The Original Product Name is not set. Please provide a valid product name.');
}
// Make sure this is the correct parser
//if ($this->OriginalProductName !== $wash_item->OriginalProductName) {
// throw new Exception('The Original Product Name does not match the parser\'s Original Product Name. Expected: ' . $this->OriginalProductName . ', got: ' . $wash_item->OriginalProductName);
//}
// All checks passed, parse the product
return $this->parse(...func_get_args());
}
/**
* The actual parsing logic.
* This method should be implemented in the class that uses this trait.
* @param xlvask_wash_item $wash_item
* @param xlvask_usage_log $xlvask_usage_log
* @return products_o
*/
abstract protected function parse(xlvask_wash_item $wash_item, xlvask_usage_log $xlvask_usage_log): products_o;
}
@@ -163,4 +163,107 @@ class xlvask_tasks
}
return $with_external_id; // Return the synchronized customers
}
/**
* Run the sync usage task
* This task is used to synchronize the usage of the XL Vask module.
* It fetches the usage data from XL Vask and adds it to the applicable users.
* The time format XL vask uses is "2025-05-01T00:00:00.000" - this is the ISO 8601 format.
* @return array|null
* @throws Exception
*/
public function runSyncUsage(): array|null
{
// Define the XL Vask object
$xlvask = new \classes\xlvask();
// Require the module to be enabled
$xlvask->requireModuleEnabled();
// Check if synchronization is enabled
if (!$xlvask->config->synchronization_enabled->isTrue()) {
throw new Exception('XL Vask synchronization is not enabled.');
}
// Set the date from which to fetch the usage data
// Since this is a cron task, we will fetch the usage data from the last 24 hours
$minutes = 24 * 60; // 24 hours in minutes
$dateFrom = date('Y-m-d\TH:i:s.000', strtotime('-' . $minutes . ' minutes'));
echo $dateFrom;
// If there are no customers, get them. TODO: Remove this in production, this is just for testing
$xlvask->getTasks()->runSyncUsers(true);
// Get the cached customers
$customers = $xlvask->getCache()->getAllCachedCustomers();
// Loop through the customers and check if there's any new usage data
foreach ( $customers as $customer ) {
/** @var xlvask_customer $customer */
// Check if the customer has an externId
if (empty($customer->externId)) {
continue; // Skip customers without an externId
}
echo 'Checking customer: ' . $customer->name . ' (' . $customer->externId . ')' . PHP_EOL;
$tmp_usage_logs = self::formatUsageLogs($xlvask->getUsageLog(
$dateFrom,
null, // regNr
null, // vehicleId
$customer->customerId // customerId
));
foreach ( $tmp_usage_logs as $log ) {
/** @var xlvask_usage_log $log */
// Check if the wash was completed
if (!$log->isCompleted()) {
echo 'A log is not completed: ' . $log->getFormattedDate() . ' - ' . $log->CustomerId . ' - ' . $log->VehicleId . PHP_EOL;
continue; // Skip logs that are not completed
}
echo PHP_EOL;
echo '### ' . $log->getFormattedDate() . ' - ' . $customer->name . ' (' . $customer->externId . ') - ' . $log->getTotalPrice() . PHP_EOL;
echo '# License Plate: ' . $log->RegistrationNumber . PHP_EOL;
echo '# Vehicle ID: ' . $log->VehicleId . PHP_EOL;
echo '# Wash ID: ' . $log->WashId . PHP_EOL;
echo '# Hall: ' . $log->Hall . PHP_EOL;
echo '# Hall ID: ' . $log->HallId . PHP_EOL;
echo '# Department: ' . $log->getDepartment()->id . PHP_EOL;
echo '# Track / Lane: ' . $log->getLane() . PHP_EOL;
echo '# Linked to order: ' . ($log->isLinkedToOrder() ? 'Yes' : 'No') . PHP_EOL;
echo '# Items: ' . PHP_EOL;
$tmp_skipped_items = [];
foreach ( $log->WashItems as $item ) {
/** @var xlvask_wash_item $item */
// Skip items that have the id 64, since these are not relevant to anyone.
$tmp_item_id = $item->getProduct($log)->id;
if ($tmp_item_id === 64) {
$tmp_skipped_items[] = $item;
continue; // Skip items with id 64
}
echo '## ' . $item->Count . ' x ' . $item->OriginalProductName . ' @ ' . $item->getPriceExVat() . ' as ' . $tmp_item_id . PHP_EOL;
}
echo '### Total: ' . $log->getTotalPrice() . PHP_EOL;
echo '### Skipped items: ' . count($tmp_skipped_items) . PHP_EOL;
}
//print_r($tmp_usage_logs);
// Check if the customer has any usage data in the last 24 hours
// This is a placeholder, you might want to implement a method to check this
// if (!$xlvask->hasRecentUsageData($customer->externId, $dateFrom)) {
// continue; // Skip customers without recent usage data
// }
}
// Process the usage data and update the users accordingly
//$xlvask->processUsageData($usage_data);
return [];
}
private static function formatUsageLogs(array $getUsageLog): array
{
//print_r($getUsageLog);
// Format the usage logs to a more readable format
// This is a placeholder, you might want to implement a method to format the logs
return array_map(/**
* @throws Exception
*/ function ($log) {
$xlvask = new \classes\xlvask();
return (new $xlvask->helpers->xlvask_usage_log())->setProperties($log);
}, $getUsageLog);
}
}
@@ -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;
}
}
@@ -0,0 +1,304 @@
<?php
namespace helpers;
use Exception;
use objects\products_o;
class xlvask_wash_item
{
/**
* Example of a wash item object:
* [WashItemId] => 0774be08-6777-490f-add2-0ccff70771ee
* [ExternalProductId] =>
* [ExternalProductName] =>
* [OriginalProductName] => Stor bil
* [Unit] => stk
* [UnitPrice] => 559
* [Count] => 1
* [Discount] => 65
* [PriceExVat] => 559
* [Vat] => 48.91
* [PriceIncVat] => 244.56
*/
/**
* The ID of the wash item
* @var string|int|float|null $WashItemId
*/
public string|int|float|null $WashItemId;
/**
* The external product ID of the wash item
* @var string|int|float|null $ExternalProductId
*/
public string|int|float|null $ExternalProductId;
/**
* The external product name of the wash item
* @var string|int|float|null $ExternalProductName
*/
public string|int|float|null $ExternalProductName;
/**
* The original product name of the wash item
* @var string|int|float|null $OriginalProductName
*/
public string|int|float|null $OriginalProductName;
/**
* The Unit of the wash item
* @var string|int|float|null $Unit
*/
public string|int|float|null $Unit;
/**
* The Unit price of the wash item
* @var string|int|float|null $UnitPrice
*/
public string|int|float|null $UnitPrice;
/**
* The count of the wash item
* @var string|int|float|null $Count
*/
public string|int|float|null $Count;
/**
* The discount applied to the wash item
* @var string|int|float|null $Discount
*/
public string|int|float|null $Discount;
/**
* The price excluding VAT of the wash item
* @var string|int|float|null $PriceExVat
*/
public string|int|float|null $PriceExVat;
/**
* The VAT amount of the wash item
* @var string|int|float|null $Vat
*/
public string|int|float|null $Vat;
/**
* The price including VAT of the wash item
* @var string|int|float|null $PriceIncVat
*/
public string|int|float|null $PriceIncVat;
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->WashItemId = $this->default_string;
$this->ExternalProductId = $this->default_string;
$this->ExternalProductName = $this->default_string;
$this->OriginalProductName = $this->default_string;
$this->Unit = $this->default_string;
$this->UnitPrice = $this->default_int; // Assuming Unit price is a float, but using default_int for initialization
$this->Count = $this->default_int; // Assuming count is an integer
$this->Discount = $this->default_int; // Assuming discount is a float, but using default_int for initialization
$this->PriceExVat = $this->default_int; // Assuming priceExVat is a float, but using default_int for initialization
$this->Vat = $this->default_int; // Assuming vat is a float, but using default_int for initialization
$this->PriceIncVat = $this->default_int; // Assuming priceIncVat is a float, but using default_int for initialization
}
/**
* 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 = [
'WashItemId' => $this->default_string,
'ExternalProductId' => $this->default_string,
'ExternalProductName' => $this->default_string,
'OriginalProductName' => $this->default_string,
'Unit' => $this->default_string,
'UnitPrice' => $this->default_int, // Assuming Unit price is a float, but using default_int for initialization
'Count' => $this->default_int, // Assuming count is an integer
'Discount' => $this->default_int, // Assuming discount is a float, but using default_int for initialization
'PriceExVat' => $this->default_int, // Assuming priceExVat is a float, but using default_int for initialization
'Vat' => $this->default_int, // Assuming vat is a float, but using default_int for initialization
'PriceIncVat' => $this->default_int, // Assuming priceIncVat is a float, but using default_int for initialization
];
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
throw new Exception("Property '$key' does not exist in " . self::class);
}
}
// After setting all properties, nullify nullable properties
$this->unsetNullifiableProperties();
// Return the populated object
return $this;
}
private function unsetNullifiableProperties(): void
{
// Unset properties that are null or empty strings
$properties = [
'WashItemId', 'ExternalProductId', 'ExternalProductName', 'OriginalProductName',
'Unit', 'UnitPrice', 'Count', 'Discount', 'PriceExVat', 'Vat', 'PriceIncVat'
];
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 product associated with this wash item.
* @throws Exception If the OriginalProductName is not set or if the product cannot be found.
*/
public function getProduct(xlvask_usage_log $xlvask_usage_log): products_o
{
// Since the ExternalProductId is rarely set, we must use the OriginalProductName to find the product.
// The OriginalProductName is the name of the service in the xlvask module, this is ususally very ambiguous.
// Example: "Stor bil" represents a large car/vehicle (And doesn't specify the type of vehicle, this could be a truck, bus, etc.)
if (empty($this->OriginalProductName)) {
throw new Exception('The OriginalProductName is not set. Please provide a valid product name.');
}
// Check if a parser exists for the OriginalProductName
$parser_class = 'helpers\xlvask_parser_' . $this->getParserName();
if (!class_exists($parser_class)) {
throw new Exception("No parser found for the OriginalProductName: {$this->OriginalProductName} (Parsed as: {$this->getParserName()}). Please ensure the parser class exists.");
}
$parser = new $parser_class();
if (!$parser instanceof xlvask_product_parser) {
throw new Exception('The xlvask product parser class is not an instance of xlvask_product_parser. Please ensure it implements the correct interface.');
}
return $parser->parseProduct($this, $xlvask_usage_log);
}
/**
* Get the parser name based on the OriginalProductName.
* This method converts the OriginalProductName to a valid parser name.
* @return string
*/
private function getParserName(): string
{
// Convert the OriginalProductName to a valid parser name
// Example: "Stor bil" becomes "stor_bil", "2-b\u00f8rstevask" becomes "2_br_stevask"
// Remove all non-alphanumeric characters and replace spaces with underscores
if (empty($this->OriginalProductName)) {
throw new Exception('The OriginalProductName is not set. Please provide a valid product name.');
}
// Use preg_replace to replace non-alphanumeric characters with underscores
$result = preg_replace('/[^a-z0-9]+/i', '_', $this->OriginalProductName);
// Remove leading and trailing underscores
$result = trim($result, '_');
// Convert to lowercase
if ($result === '') {
throw new Exception('The OriginalProductName is empty after processing. Please provide a valid product name.');
}
// Remove any special characters that might still be present
$result = preg_replace('/[^a-z0-9_]/i', '', $result);
return strtolower($result);
}
public function getPriceExVat(): float
{
// Calculate the price excluding VAT
if (is_numeric($this->PriceIncVat) && is_numeric($this->Count)) {
return (float)$this->PriceIncVat - (float)$this->Vat;
}
// If either PriceIncVat or Count is not numeric, return 0
return 0.0;
}
}
@@ -5,11 +5,31 @@ namespace xlvask;
require_once WD . '/modules/xlvask/helpers/xlvask_tasks.php';
require_once WD . '/modules/xlvask/helpers/xlvask_cache.php';
require_once WD . '/modules/xlvask/helpers/xlvask_customer.php';
require_once WD . '/modules/xlvask/helpers/xlvask_usage_log.php';
require_once WD . '/modules/xlvask/helpers/xlvask_wash_item.php';
require_once WD . '/modules/xlvask/helpers/xlvask_product_parser_t.php';
require_once WD . '/modules/xlvask/helpers/xlvask_product_parser.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_stor_bil.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_halleje.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_2_b_rstevask.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_3_b_rstevask.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ikke_ht_dysebom_bag.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ht_sider.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_skylning_med_ro.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_eu_spejl.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ikke_ht_dysebom_p_front.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_lift.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ht_chassis.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_undervognskylning.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ht_osc_sider_verst.php';
require_once WD . '/modules/xlvask/helpers/xlvask_parser_s_be_dysebom.php';
use helpers\xlvask_cache;
use helpers\xlvask_customer;
use helpers\xlvask_tasks;
use helpers\xlvask_usage_log;
use helpers\xlvask_wash_item;
class xlvask_helpers
{
@@ -28,4 +48,14 @@ class xlvask_helpers
* @var string $xlvask_customer
*/
public string $xlvask_customer = xlvask_customer::class;
/**
* The xlvask usage log helper
* @var string $xlvask_usage_log
*/
public string $xlvask_usage_log = xlvask_usage_log::class;
/**
* The xlvask wash item helper
* @var string $xlvask_wash_item
*/
public string $xlvask_wash_item = xlvask_wash_item::class;
}
+26 -7
View File
@@ -198,13 +198,6 @@ class departments_o extends db
return $db->fetch_all($result);
}
public function selectId(int $department_id): self
{
$this->id = $department_id;
$this->getObjectProperties();
return $this;
}
public function getDepartmentName(int $department): string
{
global $db;
@@ -355,4 +348,30 @@ class departments_o extends db
self::requireSelected();
return (bool)$this->variables->getVariable('bookingsystem_time_based_enabled');
}
/**
* Select a department by its name (case-sensitive, exact match)
* @throws Exception If the department selection fails or the department is not found
* @example "Hvidovre"
*/
public function selectByName(string $departmentName): self
{
$result = self::getFieldsWhere([
'name' => $departmentName,
], [
'id',
]);
if (empty($result)) {
throw new Exception('Department not found: ' . $departmentName);
}
$this->selectId($result[0]['id']);
return $this;
}
public function selectId(int $department_id): self
{
$this->id = $department_id;
$this->getObjectProperties();
return $this;
}
}
+21
View File
@@ -653,4 +653,25 @@ class orders_o extends db
return false;
}
}
/**
* Select an order by its wash ID
* @param int|string|null $WashId The wash ID to select the order by
* @return orders_o|null The selected order object or null if no order is found
* @throws Exception
*/
public function selectByWashId(int|string|null $WashId): orders_o|null
{
$result = self::getFieldsWhere([
'wash_id' => $WashId,
'deleted_at' => null,
], [
'id'
]);
if (empty($result)) {
return null; // No order found with the given wash ID
}
$this->select((int)$result[0]['id']);
return $this;
}
}
@@ -169,5 +169,23 @@ class moduleXLVaskRoute
'modules_xlvask_sync_users' => 'Synchronize users with the xlvask module'
]
);
$this->get('/modules/xlvask/tasks/sync-usage', function () {
global $response;
//self::requirePermission('modules_xlvask_sync_usage');
// Create the xlvask tasks object
$xlvask = new xlvask();
// Run the sync usage task
$result = $xlvask->getTasks()->runSyncUsage();
// Response
$response->success(
$result,
200
);
},
[
'modules_xlvask_sync_usage' => 'Synchronize usage with the xlvask module'
]
);
}
}