Files
api/services/nginx/app/modules/xlvask/helpers/xlvask_tasks.php
T

394 lines
16 KiB
PHP

<?php
namespace helpers;
require_once WD . '/classes/xlvask_autopilot_service.php';
use classes\xlvask_autopilot_service;
use Exception;
use objects\orders_o;
use objects\plate_scanners_o;
use objects\users_o;
use objects\xlvask_customers_o;
use objects\xlvask_potential_order_matches_o;
use objects\xlvask_vehicles_o;
class xlvask_tasks
{
public function __construct()
{
// Define the error messages if they are not already defined
/**
* if (!defined("ERROR_MESSAGE_ECONOMIC_INVOICE_NOT_FOUND")) {
* define("ERROR_MESSAGE_ECONOMIC_INVOICE_NOT_FOUND", 'ECONOMIC_INVOICE_NOT_FOUND');
* }
* if (!defined("ERROR_MESSAGE_ECONOMIC_INVOICE_DRAFT_NOT_FOUND")) {
* define("ERROR_MESSAGE_ECONOMIC_INVOICE_DRAFT_NOT_FOUND", 'ECONOMIC_INVOICE_DRAFT_NOT_FOUND');
* }
*/
}
/**
* @throws Exception
*/
public function runCronTasks(): void
{
// Define the XL Vask object
$xlvask = new \classes\xlvask();
// Require the module to be enabled
$xlvask->requireModuleEnabled();
// Run the synchronization tasks
if ($xlvask->config->synchronization_enabled->isTrue()) {
// Synchronize users, this will keep the users (XL Vask customer objects) in cache up to date
$this->runImportTasks();
$this->runSyncUsers(true);
$this->runSyncUsage();
$this->runSyncVehicles();
$this->runCleanupTasks();
// Automation is an optional final phase. A pending migration or an
// off/advisory policy must never interrupt the ordinary XL Vask sync.
$this->runScheduledAutomationIfReady();
};
}
/**
* Run the import tasks
* This task is used to import the XL Vask vehicles, users, usage logs, and other necessary data into the system.
* It will import the necessary classes and objects to be used in the system.
* @return void
* @throws Exception If the task fails
*/
public function runImportTasks(): void
{
// Define the XL Vask object
$xlvask = new \classes\xlvask();
// Require the module to be enabled
$xlvask->requireModuleEnabled();
// Run the synchronization tasks
if ($xlvask->config->synchronization_enabled->isTrue()) {
// Synchronize users, this will keep the database tables updated:
// - xlvask_customers
// - xlvask_vehicles
// - xlvask_usage_logs
(new xlvask_customers_o())->importCustomers();
(new xlvask_vehicles_o())->importVehicles();
};
}
/** Enqueue automatic work only after explicit migration and policy activation. */
public function runScheduledAutomationIfReady(): array
{
try {
$migrationStatus = \classes\xlvask_usage_logs_schema_bootstrap::migrationStatus();
if (!(bool)($migrationStatus['ready'] ?? false)) {
return [];
}
$hallIds = $this->configuredHallIds();
if ($hallIds === []) {
return [];
}
$autopilot = new xlvask_autopilot_service();
$capabilities = (new \classes\xlvask_automation_policy_service())->capabilitiesReadOnly(null, null, $hallIds);
if (!xlvask_autopilot_service::scheduledExecutionAllowed($migrationStatus, $capabilities)) {
return [];
}
$autopilot->createRun(['mode' => 'execute'], null, $hallIds);
return $autopilot->processQueuedRuns(3);
} catch (\Throwable) {
// Fail closed for automation while preserving the completed ordinary sync.
return [];
}
}
/** Drain the durable autopilot queue without running the hourly upstream synchronization. */
public function processAutopilotQueue(int $limit = 3): array
{
$xlvask = new \classes\xlvask();
$xlvask->requireModuleEnabled();
if (!$xlvask->config->synchronization_enabled->isTrue()) {
return [];
}
if (!(bool)(\classes\xlvask_usage_logs_schema_bootstrap::migrationStatus()['ready'] ?? false)) {
return [];
}
// Off/advisory cannot contain execute runs because createRun is server-gated.
// Explicit dry-run/replay evidence may still drain in advisory mode.
return (new xlvask_autopilot_service())->processQueuedRuns($limit);
}
/** Hall GUIDs are configuration, not derived from already-imported usage rows. */
private function configuredHallIds(): array
{
global $db;
(new plate_scanners_o())->structure();
plate_scanners_o::ensureHallIdSchemaForMutation();
$result = $db->query(
"SELECT DISTINCT HallId FROM plate_scanners
WHERE HallId IS NOT NULL AND TRIM(HallId) <> '' AND deleted_at IS NULL"
);
if ($result === false) {
return [];
}
return array_values(array_unique(array_filter(array_map(
static fn(array $row): string => trim((string)($row['HallId'] ?? '')),
$db->fetch_all($result)
), static fn(string $id): bool => $id !== '' && strlen($id) <= 191)));
}
/**
* Run the synchronize users task
* This task fetches the users from XL Vask, and synchronizes them with this system.
* This includes:
* - Fetching the users from XL Vask
* - Checking if the users exist in this system
* - Add objects to the cache, when a matching user is found.
* @return null|array['customers'] The customers fetched from XL Vask, null if synchronization is not enabled
* @throws Exception If the task fails
*/
public function runSyncUsers(bool $isSilent = false): 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()) {
if (!$isSilent) {
throw new Exception('XL Vask synchronization is not enabled.');
}
return null; // Synchronization is not enabled, do nothing
}
// Get the users from XL Vask
$customer_objects = $this->fetchCustomers();
//echo 'Customers fetched from XL Vask: ' . count($customer_objects) . PHP_EOL;
// Return the customers with an external ID
$customers = [
'with_external_id' => [...self::filterCustomersWithExternalId($customer_objects)],
'without_external_id' => [...self::filterCustomersWithoutExternalId($customer_objects)],
'all' => $customer_objects,
];
return self::synchronize_customers($customers['with_external_id']);
}
/**
* Get the customers registered in XL Vask
* This method fetches the customers from XL Vask and returns them as an array of customer objects.
* @return array[xlvask_customer] An array of customer objects fetched from XL Vask
* @throws Exception If the task fails
*/
public function fetchCustomers(): array
{
// Define the XL Vask object
$xlvask = new \classes\xlvask();
// Require the module to be enabled
$xlvask->requireModuleEnabled();
// Get the users from XL Vask
$customer_objects_raw = $xlvask->getCustomers();
// Parse the users into customer objects
return array_map(function ($customer) use ($xlvask) {
/** @var xlvask_customer $tmp */
$tmp = new $xlvask->helpers->xlvask_customer();
foreach ( $customer as $key => $value ) {
// Assign the properties shared by the xlvask_customer class.
if (property_exists($tmp, $key)) {
$tmp->{$key} = $value;
}
}
return $tmp; // Return the xlvask_customer object
}, $customer_objects_raw);
}
/**
* Get the customers with an external ID
* @param array $customers Customers to filter
* @return array The customers with an external ID
*/
public static function filterCustomersWithExternalId(array $customers): array
{
return array_filter($customers, function ($customer) {
/** @var xlvask_customer $customer */
return !empty($customer->externId);
});
}
/**
* Get the customers without an external ID
* @param array $customers Customers to filter
* @return array The customers without an external ID
*/
public static function filterCustomersWithoutExternalId(array $customers): array
{
return array_filter($customers, function ($customer) {
/** @var xlvask_customer $customer */
return empty($customer->externId);
});
}
/**
* Synchronize customers with external IDs
* This method synchronizes the customers with external IDs from XL Vask.
* It checks if the customers exist in this system, creates them if they do not exist,
* and adds/removes the associated external ID to the users in this system.
* @param array $with_external_id Customers with external IDs to synchronize
* @return array|null The synchronized customers, null if synchronization is not enabled
* @throws Exception
*/
private static function synchronize_customers(array $with_external_id): 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()) {
return null; // Synchronization is not enabled, do nothing
}
// Get a list of customer numbers that we need to synchronize
$customer_numbers = array_map(function ($customer) {
/** @var xlvask_customer $customer */
return $customer->externId; // The customer number is stored in the externId property in XL Vask
}, $with_external_id);
// Get all the user ids associated with the customer numbers
$users = (new users_o())->getUsersByCustomerNumbers($customer_numbers, true);
$cache = $xlvask->getCache();
/** @var xlvask_customer $customer */
foreach ( $with_external_id as $customer ) {
// Check if the customer exists in this system
if (isset($users[$customer->externId])) {
// The user exists, update the user with the external ID
/** @var users_o $user */
$user = $users[$customer->externId];
// Cache the customer
$cache->setCustomerCache((int)$customer->externId, $customer);
}
}
return $with_external_id; // Return the synchronized customers
}
/**
* Usage synchronization is intentionally disabled until the XL Vask integration is completed.
*/
public function runSyncUsage(?string $dateFrom = null, ?string $dateTo = null): void
{
unset($dateFrom, $dateTo);
}
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);
}
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.
*/
public function createOrderFromWash(xlvask_usage_log $log, xlvask_customer|\stdClass $customer): ?orders_o
{
// Make sure the XL Vask module is enabled
$xlvask = new \classes\xlvask();
$xlvask->requireModuleEnabled();
// Check if the log is completed
if (!$log->isCompleted()) {
throw new Exception('The log ( ID: ' . $log->WashId . ' ) is not completed, cannot create an order from it.');
}
// Check if the customer has an externId
if (empty($customer->externId)) {
throw new Exception('The customer ( ID: ' . $customer->customerId . ' ) does not have an externId, cannot create an order from it.');
}
// Check if the customer exists in this system
if (!$tmp_user = $customer->getUser()) {
throw new Exception('The customer ( ID: ' . $customer->customerId . ' ) does not exist in this system, cannot create an order from it.');
}
// Create a new order object
$order = new orders_o();
$order->addXLVaskOrder(
$tmp_user,
$log,
);
// Return the order object
return $order;
}
/**
* Synchronize vehicles with XL Vask
* This method is used to synchronize the vehicles with XL Vask.
* It fetches the vehicles from XL Vask and adds them to the cache.
* This includes:
* - Fetching the vehicles from XL Vask
* - Caching the vehicles in this system
* @param bool $isSilent If true, will not throw an exception if synchronization is not enabled
* @return null|array
* @throws Exception If the task fails or if synchronization is not enabled
* @see xlvask_vehicle
* @see xlvask_cache::getVehicleCache()
* @see xlvask_cache::setVehicleCache()
* @see xlvask_cache::isVehicleCached()
*/
public function runSyncVehicles(bool $isSilent = false): ?array
{
// 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()) {
if (!$isSilent) {
throw new Exception('XL Vask synchronization is not enabled.');
}
return null; // Synchronization is not enabled, do nothing
}
// Get the vehicles from XL Vask
/** @var xlvask_vehicles $vehicles */
$vehicles = new $xlvask->helpers->xlvask_vehicles();
$vehicles = $vehicles->getVehicles();
// Cache the vehicles
foreach ( $vehicles as $vehicle ) {
/** @var xlvask_vehicle $vehicle */
$xlvask->getCache()->setVehicleCache($vehicle->registrationNumber, $vehicle);
}
return $vehicles; // Return the vehicles
}
/**
* Run the cleanup tasks
* This task is used to clean up the XL Vask module.
* @return void
* @throws Exception If the task fails
*/
public function runCleanupTasks(): void
{
// Define the XL Vask object
$xlvask = new \classes\xlvask();
// Require the module to be enabled
$xlvask->requireModuleEnabled();
if (!(bool)(\classes\xlvask_usage_logs_schema_bootstrap::migrationStatus()['ready'] ?? false)) {
return;
}
(new xlvask_autopilot_service())->pruneExpiredData();
}
}