Enhance xlvask_tasks: introduce date range options and additional debug outputs for runSyncUsage. Add new parsers xlvask_parser_ingen_b_rster_foran_no_brush_wash_front and xlvask_parser_ht_dysebom and integrate into xlvask_helpers. Improve rate limiting and reduce FastCGI timeout in nginx configuration. Adjust execution time and memory limits in cli.php.
This commit is contained in:
@@ -40,7 +40,7 @@ class response implements response_i
|
||||
}
|
||||
}
|
||||
// If the debug mode is enabled, add the debug data to the response
|
||||
if ($DEBUG) {
|
||||
if ($DEBUG && false) {
|
||||
$this->add_include('debug', [
|
||||
'memory' => memory_get_usage(),
|
||||
'time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
|
||||
|
||||
@@ -25,9 +25,9 @@ if (php_sapi_name() === 'cli') {
|
||||
// Set the max execution time to 0
|
||||
// This is used to prevent the script from timing out
|
||||
// when running for a long time
|
||||
set_time_limit(0);
|
||||
// Set the memory limit to unlimited
|
||||
ini_set('memory_limit', '-1');
|
||||
set_time_limit(10 * 60); // 10 minutes
|
||||
// Set the memory limit to 16 GB
|
||||
ini_set('memory_limit', '16G');
|
||||
|
||||
|
||||
// If the first argument is 'run', switch to the second argument
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace helpers;
|
||||
|
||||
use Exception;
|
||||
use objects\products_o;
|
||||
|
||||
class xlvask_parser_ht_dysebom extends xlvask_product_parser
|
||||
{
|
||||
use xlvask_product_parser_t;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setup('ht_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
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace helpers;
|
||||
|
||||
use Exception;
|
||||
use objects\products_o;
|
||||
|
||||
class xlvask_parser_ingen_b_rster_foran_no_brush_wash_front extends xlvask_product_parser
|
||||
{
|
||||
use xlvask_product_parser_t;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setup('ingen_b_rster_foran_no_brush_wash_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
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ class xlvask_tasks
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the synchronize users task
|
||||
* This task fetches the users from XL Vask, and synchronizes them with this system.
|
||||
@@ -187,8 +186,10 @@ class xlvask_tasks
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function runSyncUsage(): array|null
|
||||
public function runSyncUsage(string $dateFrom = null, string $dateTo = null): array|null
|
||||
{
|
||||
// TODO: Remove this, this is just for testing purposes
|
||||
return null;
|
||||
// Define the XL Vask object
|
||||
$xlvask = new \classes\xlvask();
|
||||
// Require the module to be enabled
|
||||
@@ -197,10 +198,29 @@ class xlvask_tasks
|
||||
if (!$xlvask->config->synchronization_enabled->isTrue()) {
|
||||
throw new Exception('XL Vask synchronization is not enabled.');
|
||||
}
|
||||
// TODO: Remove this, this is just for testing purposes:
|
||||
$dateFrom = "2025-06-01 00:00:00";
|
||||
$dateTo = "2025-07-01 00:00:00";
|
||||
// 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'));
|
||||
// TODO: Remove this, this is just for testing purposes:
|
||||
$minutes = $minutes * 20;
|
||||
// If dateFrom is not set, set it to 24 hours ago
|
||||
if (empty($dateFrom)) {
|
||||
$dateFrom = date('Y-m-d\TH:i:s.000', strtotime('-' . $minutes . ' minutes'));
|
||||
} else {
|
||||
// If dateFrom is set, make sure it is in the correct format
|
||||
$dateFrom = date('Y-m-d\TH:i:s.000', strtotime($dateFrom));
|
||||
}
|
||||
// If dateTo is not set, set it to now
|
||||
if (empty($dateTo)) {
|
||||
$dateTo = date('Y-m-d\TH:i:s.000'); // Current time in ISO 8601 format
|
||||
} else {
|
||||
// If dateTo is set, make sure it is in the correct format
|
||||
$dateTo = date('Y-m-d\TH:i:s.000', strtotime($dateTo));
|
||||
}
|
||||
echo 'Fetching usage data from: ' . $dateFrom . ' to: ' . $dateTo . PHP_EOL;
|
||||
//echo $dateFrom; (E.g. 2025-06-11T12:13:16.000)
|
||||
|
||||
|
||||
@@ -210,6 +230,139 @@ class xlvask_tasks
|
||||
// Get the cached customers
|
||||
$customers = $xlvask->getCache()->getAllCachedCustomers();
|
||||
|
||||
// Get all the usage logs from XL Vask
|
||||
$debug_usage_logs = $xlvask->getUsageLog(
|
||||
$dateFrom,
|
||||
null, // regNr
|
||||
null, // vehicleId
|
||||
null // customerId
|
||||
);
|
||||
|
||||
$debug_usage_logs = self::formatUsageLogs($debug_usage_logs);
|
||||
|
||||
// Filter out the logs that are after the dateTo
|
||||
$debug_usage_logs = array_filter($debug_usage_logs, function ($log) use ($dateTo) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return strtotime($log->getFormattedDate()) <= strtotime($dateTo);
|
||||
});
|
||||
echo 'Found ' . count($debug_usage_logs) . ' usage logs in the date range from ' . $dateFrom . ' to ' . $dateTo . PHP_EOL;
|
||||
echo "Prepaid: " . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->isPrepaid();
|
||||
})) . PHP_EOL;
|
||||
echo "Finished: " . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->isCompleted();
|
||||
})) . PHP_EOL;
|
||||
echo 'Not finished (skipped): ' . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return !$log->isCompleted();
|
||||
})) . PHP_EOL;
|
||||
echo 'Billed to default customer (skipped): ' . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->hasDefaultCustomer();
|
||||
})) . PHP_EOL;
|
||||
echo 'Unique customers: ' . count(array_unique(array_map(function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->CustomerId;
|
||||
}, $debug_usage_logs))) . PHP_EOL;
|
||||
$linked_orders = (new orders_o())->getFieldsWhere([
|
||||
'wash_id' => array_map(function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->WashId;
|
||||
}, $debug_usage_logs),
|
||||
'deleted_at' => null,
|
||||
], [
|
||||
'id',
|
||||
'wash_id',
|
||||
]);
|
||||
echo "Linked to orders: " . count($linked_orders) . PHP_EOL;
|
||||
$eligible_for_automatic_continuance = array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->isEligibleForAutomaticContinuance(true);
|
||||
});
|
||||
$eligible_for_automatic_continuance_without_prepaid = array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->isEligibleForAutomaticContinuance(false);
|
||||
});
|
||||
echo "Eligible for automatic continuance: " . count($eligible_for_automatic_continuance) . " (" . count($eligible_for_automatic_continuance_without_prepaid) . " without prepaid)" . PHP_EOL;
|
||||
// Print a list of customers that do not have an external ID
|
||||
$customers_without_external_id = array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return !$log->hasExternalId() && !$log->hasDefaultCustomer();
|
||||
});
|
||||
echo "Without billable customer: " . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return !$log->hasBillableCustomer();
|
||||
})) . " (" . count(array_filter($debug_usage_logs, function ($log) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return !$log->hasBillableCustomer() && !$log->hasDefaultCustomer() && !$log->hasExternalId();
|
||||
})) . " without external ID)" . PHP_EOL;
|
||||
|
||||
foreach ( self::sortLogsByDate($customers_without_external_id) as $customer ) {
|
||||
/** @var xlvask_usage_log $customer */
|
||||
echo ' - ' . $customer->getFormattedDate() . ' - ' . $customer->Customer . ' - ' . $customer->getDepartment()->name->value() . ', ' . $customer->getLane() . ' - ' . $customer->getTotalPrice() . ' DKK' . ' ( ' . ($customer->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($customer->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ' )' . PHP_EOL;
|
||||
}
|
||||
|
||||
// Get all unique external IDs from the customers
|
||||
$unique_external_customer_ids = array_unique(array_map(function ($customer) {
|
||||
/** @var xlvask_usage_log $customer */
|
||||
return $customer->CustomerId;
|
||||
}, $debug_usage_logs));
|
||||
// Remove the external IDs that are not numeric or empty
|
||||
$unique_external_customer_ids = array_filter($unique_external_customer_ids, function ($id) {
|
||||
return !empty($id) && is_numeric($id);
|
||||
});
|
||||
echo "Unique external customer IDs: " . count($unique_external_customer_ids) . PHP_EOL;
|
||||
foreach ( $unique_external_customer_ids as $customer_id ) {
|
||||
$doesCustomerExistInArray = array_filter($customers, function ($customer) use ($customer_id) {
|
||||
/** @var xlvask_customer $customer */
|
||||
return $customer->externId === $customer_id;
|
||||
});
|
||||
$tmp_does_customer_exist = count($doesCustomerExistInArray) > 0;
|
||||
if (!$tmp_does_customer_exist) {
|
||||
echo ' - ' . $customer_id . ' does not exist in the system (skipped)' . PHP_EOL;
|
||||
continue; // Skip customers that do not exist in the system
|
||||
}
|
||||
$tmp_customer_object = reset($doesCustomerExistInArray); // Get the first customer object that matches the external ID
|
||||
/** @var xlvask_customer $tmp_customer_object */
|
||||
echo ' - ' . $tmp_customer_object->name . ' (' . $tmp_customer_object->externId . ') - ' . $tmp_customer_object->customerId . PHP_EOL;
|
||||
echo " - # Washes: " . count(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->CustomerId === $tmp_customer_object->externId;
|
||||
})) . PHP_EOL;
|
||||
// Echo the eligible for automatic continuance logs
|
||||
foreach ( self::sortLogsByDate(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->CustomerId === $tmp_customer_object->externId && $log->isEligibleForAutomaticContinuance(false);
|
||||
})) as $log ) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
$tmp_linked_to_order = array_filter($linked_orders, function ($order) use ($log) {
|
||||
/** @var array $order */
|
||||
return $order['wash_id'] === $log->WashId;
|
||||
});
|
||||
echo ' - - ' . $log->getFormattedDate() . ' - ' . $log->getDepartment()->name->value() . ', ' . $log->getLane() . ' - ' . $log->getTotalPrice() . ' DKK' . ' ( ' . ($log->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($log->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ', ' . ($tmp_linked_to_order ? 'Linked to order' : 'Not linked to order') . ' )' . PHP_EOL;
|
||||
}
|
||||
// Echo the ineligible for automatic continuance logs
|
||||
echo " - # Not eligible for automatic continuance: " . count(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->CustomerId === $tmp_customer_object->externId && !$log->isEligibleForAutomaticContinuance(false);
|
||||
})) . PHP_EOL;
|
||||
foreach ( self::sortLogsByDate(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
return $log->CustomerId === $tmp_customer_object->externId && !$log->isEligibleForAutomaticContinuance(false);
|
||||
})) as $log ) {
|
||||
/** @var xlvask_usage_log $log */
|
||||
echo ' - - ' . $log->getFormattedDate() . ' - ' . $log->getDepartment()->name->value() . ', ' . $log->getLane() . ' - ' . $log->getTotalPrice() . ' DKK' . ' ( ' . ($log->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($log->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ' )' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
echo 'Customers:';
|
||||
print_r($customers);
|
||||
echo 'Eligible for automatic continuance:';
|
||||
print_r($eligible_for_automatic_continuance);
|
||||
exit;
|
||||
|
||||
|
||||
// Loop through the customers and check if there's any new usage data
|
||||
foreach ( $customers as $customer ) {
|
||||
/** @var xlvask_customer $customer */
|
||||
@@ -330,6 +483,17 @@ class xlvask_tasks
|
||||
}, $getUsageLog);
|
||||
}
|
||||
|
||||
private static function sortLogsByDate(array $logs): array
|
||||
{
|
||||
// Sort the logs by date
|
||||
usort($logs, function ($a, $b) {
|
||||
/** @var xlvask_usage_log $a */
|
||||
/** @var xlvask_usage_log $b */
|
||||
return strtotime($a->getFormattedDate()) - strtotime($b->getFormattedDate());
|
||||
});
|
||||
return $logs; // Return the sorted logs
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception If the log is not completed, or if the customer does not have an externId, or if the customer does not exist in this system.
|
||||
*/
|
||||
|
||||
@@ -393,41 +393,6 @@ class xlvask_usage_log extends xlvask_helper
|
||||
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.
|
||||
@@ -559,4 +524,84 @@ class xlvask_usage_log extends xlvask_helper
|
||||
// Return the start time as a timestamp
|
||||
return date('Y-m-d H:i:s', strtotime($this->StartTime));
|
||||
}
|
||||
|
||||
public function isEligibleForAutomaticContinuance(bool $allowPrepaid = false): bool
|
||||
{
|
||||
// Check if the wash is eligible for automatic continuance
|
||||
// This can be based on various criteria, such as the finish status or other business rules
|
||||
return
|
||||
$this->isCompleted() && // Ensure the wash is completed
|
||||
$this->hasBillableCustomer() && // Ensure the wash has a billable customer
|
||||
(
|
||||
$this->getTotalPrice() > 0 || $allowPrepaid
|
||||
// Allow washes with a total price greater than 0 or allow prepaid washes if $allowPrepaid is true
|
||||
) &&
|
||||
(
|
||||
!$this->isPrepaid() || $allowPrepaid
|
||||
); // Allow prepaid washes if $allowPrepaid is true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
public function hasBillableCustomer(): bool
|
||||
{
|
||||
// Check if the wash has a non-default customer with a valid external ID
|
||||
return !$this->hasDefaultCustomer() && $this->hasExternalId();
|
||||
}
|
||||
|
||||
public function hasDefaultCustomer(): bool
|
||||
{
|
||||
// Check if the customer is a default customer (e.g., "Unknown" or similar)
|
||||
$default_customers = [
|
||||
'CTW',
|
||||
'Personalebiler Truckwash ', // The space at the end is intentional
|
||||
'XL Gønge Transport ApS', // This is a test customer
|
||||
];
|
||||
return in_array($this->Customer, $default_customers, true);
|
||||
}
|
||||
|
||||
public function hasExternalId(): bool
|
||||
{
|
||||
// Check if the wash has an external ID (e.g., CustomerId)
|
||||
return !empty($this->CustomerId) && $this->CustomerId !== '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public function isPrepaid(): bool
|
||||
{
|
||||
// Check if the wash is prepaid
|
||||
return !empty($this->Prepaid) && $this->Prepaid === 1; // Assuming 1 indicates a prepaid wash
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,8 @@ require_once WD . '/modules/xlvask/helpers/xlvask_parser_h_nger.php';
|
||||
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ikke_ht_dysebom_mellom_bil_trailer.php';
|
||||
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ikke_b_rster_mellom_bil_og_trailer.php';
|
||||
require_once WD . '/modules/xlvask/helpers/xlvask_parser_s_be_dysebom_tak.php';
|
||||
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ingen_b_rster_foran_no_brush_wash_front.php';
|
||||
require_once WD . '/modules/xlvask/helpers/xlvask_parser_ht_dysebom.php';
|
||||
|
||||
use helpers\xlvask_cache;
|
||||
use helpers\xlvask_create_customer;
|
||||
|
||||
@@ -7,6 +7,9 @@ events {
|
||||
}
|
||||
|
||||
http {
|
||||
# Add rate limiting to prevent abuse
|
||||
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
|
||||
limit_req_status 429;
|
||||
# Include additional configuration files
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
@@ -71,7 +74,7 @@ http {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass php:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_read_timeout 600;
|
||||
fastcgi_read_timeout 60;
|
||||
fastcgi_param SCRIPT_FILENAME index.php;
|
||||
}
|
||||
|
||||
@@ -119,4 +122,6 @@ http {
|
||||
|
||||
# Additional server blocks can be added here
|
||||
|
||||
# Restrict access to the server, if the
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user