Files
api/services/nginx/app/routes/xlvaskUsageLogsRoute.php
T

371 lines
18 KiB
PHP

<?php
namespace routes;
require_once WD . '/classes/xlvask_automation_service.php';
use classes\authentication;
use classes\redis;
use classes\response;
use classes\stripe;
use classes\xlvask;
use classes\xlvask_automation_service;
use objects\collected_order_invoices_o;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_orders_o;
use objects\stripe_payment_intents_o;
use objects\xlvask_usage_logs_o;
use traits\route_t;
class xlvaskUsageLogsRoute
{
use route_t;
public function run(): void
{
$this->get('/modules/xlvask/services/usage/orders', function () {
// Define the permissions:
$permission_list_own = 'list_xlvask_usage_orders_own'; // Permission to list own orders (Without department filter)
$permission_list_all = 'list_xlvask_usage_orders_all'; // Permission to list all orders (With department filter)
$response_includes_items = false; // Whether to include items in the response (This would increase the memory usage significantly)
$response_includes_items_link = true; // Whether to include a cached direct link to fetch the order with items (This is used to avoid memory issues when the items are not needed)
// Require the user to be logged in
global $response;
self::requirePermission($permission_list_own);
// Check if the user has permission to list all entries
if (self::hasPermission($permission_list_all)) {
$this->requirePermission($permission_list_all);
} else {
$this->requirePermission($permission_list_own);
}
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('xlvask_usage_orders', 'global', 1, $user->id, 'LIST_XLVASK_USAGE_ORDERS', 'User accessed the list of xlvask usage orders');
$xlvask_usage_logs = new xlvask_usage_logs_o();
$xlvask = new xlvask();
$automation_service = new xlvask_automation_service();
$linked_order_ids_by_wash_id = [];
$xlvask->new($xlvask->helpers->xlvask_usage_log)->getDepartment();
$orders_o = new orders_o();
$xlvask_usage_log = $xlvask->new($xlvask->helpers->xlvask_usage_log);
// Increase the memory limit to 512MB (Provided it's currently less than that)
if (ini_get('memory_limit') < '5120M') {
ini_set('memory_limit', '5120M');
}
// Set debug php
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Return the list of usage logs
$result = $xlvask_usage_logs
// Make sure the Customer is not in the default customers list
->setAdditionalWhereClause("`Customer` NOT IN ('" . implode("', '", $xlvask_usage_log::$default_customers) . "')")
->listObjectsWithPaginationIfSet(
function ($log) use ($response_includes_items_link, $response_includes_items, $xlvask_usage_logs, $user, $xlvask, $automation_service, &$linked_order_ids_by_wash_id) {
$automation = $automation_service->evaluateUsageLogRow($log, (int)$user->id, false);
// Remove the 'id' field from the log
$id = (int)$log['id'];
unset($log['id']);
// Convert the 'WashItems' field from JSON to an array
$log['WashItems'] = json_decode($log['WashItems'], true);
// Create a new xlvask usage log object
$tmp = $xlvask->new($xlvask->helpers->xlvask_usage_log);
// Set the properties of the temporary object
$tmp->setProperties($log);
$wash_id = (string)$tmp->WashId;
if ($wash_id !== '' && !array_key_exists($wash_id, $linked_order_ids_by_wash_id)) {
$linked_order = (new orders_o())->selectByWashId($wash_id);
$linked_order_ids_by_wash_id[$wash_id] = $linked_order !== null ? (int)$linked_order->id : null;
}
$linked_order_id = $wash_id !== '' ? $linked_order_ids_by_wash_id[$wash_id] : null;
// Define the result structure
$isEligibleForAutomaticContinuance = $tmp->isEligibleForAutomaticContinuance(true);
// Generate a fast link key for the order, used to retrieve the order with items later.
if ($response_includes_items_link && $isEligibleForAutomaticContinuance) {
// Add a link to the order with items
$fast_link_key = redis->generateTemporaryCacheKey();
redis->set(
$fast_link_key,
json_encode($tmp->toArray(), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
);
redis->expire($fast_link_key, 3600); // Set the link to expire in 1 hour
}
// Return the result
$tmp_res = ($isEligibleForAutomaticContinuance ? (new orders_o())->simulateOrderFromXLVask($tmp, $response_includes_items) : []);
$tmp_res['order']['customer_name'] = $tmp->Customer; // Add the customer name to the order
// Clear memory
unset($tmp);
unset($log);
// Return the result
return [
'id' => $id, // Return the ID of the log
'fast_link_key' => $fast_link_key ?? null, // Return the fast link key if it was generated
'automation' => $automation,
...$tmp_res['order'], // Return the simulated order from XLVask (with or without items)
'usage_log_id' => $id,
'linked_order_id' => $linked_order_id,
];
},
$xlvask_usage_logs->forceRestrictFilters(
[
// This makes sure that the user can only see department logs that belong to their departments
'HallId' => $user->getGroup()->getDepartmentsScannersHallIds(),
'FinishStatus' => ['1'], // Only show finished logs
]
)
);
// Return the response
$response->success($result);
} else {
// Log the incident
(new logs_o())->add('xlvask_usage_orders', 'global', 1, 0, 'LIST_XLVASK_USAGE_ORDERS', 'No user found, or invalid session');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_xlvask_usage_orders_own' => 'List own xlvask usage orders (Without department filter)',
'list_xlvask_usage_orders_all' => 'List all xlvask usage orders (With department filter)',
]
);
$this->patch('/modules/xlvask/services/usage/orders/{id}/ignore', function () {
global $db, $response;
$this->requirePermission('ignore_xlvask_usage_order');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
return;
}
$id = (int)($this->fromRoute('id') ?? 0);
if ($id < 1) {
$response->error('Invalid XL Vask usage log id', 400);
return;
}
$reason = $this->isParametersSet(['reason']) ? trim((string)$this->getParameter('reason')) : null;
$reasonSql = $reason === null || $reason === ''
? 'NULL'
: "'" . $db->escape_string($reason) . "'";
(new xlvask_usage_logs_o())->structure();
$db->query(
"UPDATE xlvask_usage_logs
SET ignored_at = NOW(),
ignored_by = " . (int)$user->id . ",
ignored_reason = {$reasonSql}
WHERE id = {$id}"
);
$response->success([
'id' => $id,
'ignored' => true,
]);
},
[
'ignore_xlvask_usage_order' => 'Ignore an XL Vask usage log for invoice period flagging',
]
);
$this->post('/modules/xlvask/services/usage/orders/automation/run', function () {
global $response;
$this->requirePermission('manage_xlvask_usage_automation');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
return;
}
$ids = $this->isParametersSet(['ids']) ? $this->getParameter('ids') : [];
if (!is_array($ids)) {
$ids = [];
}
$dateFrom = $this->isParametersSet(['dateFrom']) ? (string)$this->getParameter('dateFrom') : null;
$dateTo = $this->isParametersSet(['dateTo']) ? (string)$this->getParameter('dateTo') : null;
$limit = $this->isParametersSet(['limit']) ? (int)$this->getParameter('limit') : 100;
$response->success(
(new xlvask_automation_service())->runPending($dateFrom, $dateTo, $ids, $limit, (int)$user->id)
);
},
[
'manage_xlvask_usage_automation' => 'Evaluate and execute XL Vask usage-log automation',
]
);
$this->post('/modules/xlvask/services/usage/orders/{id}/automation/evaluate', function () {
global $response;
$this->requirePermission('manage_xlvask_usage_automation');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
return;
}
$id = (int)($this->fromRoute('id') ?? 0);
if ($id < 1) {
$response->error('Invalid XL Vask usage log id', 400);
return;
}
$response->success(
(new xlvask_automation_service())->evaluateUsageLogById($id, (int)$user->id, true)
);
},
[
'manage_xlvask_usage_automation' => 'Evaluate XL Vask usage-log automation',
]
);
$this->post('/modules/xlvask/services/usage/orders/{id}/automation/accept', function () {
global $response;
$this->requirePermission('manage_xlvask_usage_automation');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
return;
}
$id = (int)($this->fromRoute('id') ?? 0);
if ($id < 1) {
$response->error('Invalid XL Vask usage log id', 400);
return;
}
$suggestionId = $this->isParametersSet(['suggestion_id']) ? (int)$this->getParameter('suggestion_id') : null;
$reason = $this->isParametersSet(['reason']) ? trim((string)$this->getParameter('reason')) : null;
$response->success(
(new xlvask_automation_service())->acceptUsageLogById($id, (int)$user->id, $suggestionId, $reason)
);
},
[
'manage_xlvask_usage_automation' => 'Accept an XL Vask usage-log automation suggestion',
]
);
$this->post('/modules/xlvask/services/usage/orders/{id}/automation/deny', function () {
global $response;
$this->requirePermission('manage_xlvask_usage_automation');
$user = (new authentication())->get_user();
if (!$user) {
$response->error('Invalid session', 400);
return;
}
$id = (int)($this->fromRoute('id') ?? 0);
if ($id < 1) {
$response->error('Invalid XL Vask usage log id', 400);
return;
}
$suggestionId = $this->isParametersSet(['suggestion_id']) ? (int)$this->getParameter('suggestion_id') : null;
$reason = $this->isParametersSet(['reason']) ? trim((string)$this->getParameter('reason')) : null;
$response->success(
(new xlvask_automation_service())->denyUsageLogById($id, (int)$user->id, $suggestionId, $reason)
);
},
[
'manage_xlvask_usage_automation' => 'Deny an XL Vask usage-log automation suggestion',
]
);
$this->get('/modules/xlvask/services/usage/orders/fast-link', function () {
global $response;
self::requireParameters([
'fast_link_key', // Example: 'temporary_cache_6878cf0603d77'
]);
// Get the fast link key from the request
$fast_link_key = (string)self::getParameter('fast_link_key');
self::requireType($fast_link_key, self::type_string());
self::requireMinLength('fast_link_key', 20); // Minimum length of the fast link key
self::requireMaxLength('fast_link_key', 50); // Maximum length of the fast link key
// Check if the fast link key is valid
// First, check if the key has the correct format
if (preg_match('/^temporary_cache_[a-z0-9]{12,32}$/', $fast_link_key)) {
// Get the cached data from Redis
$cached_data = redis->get($fast_link_key);
// Check if the cached data is valid
if ($cached_data) {
// Decode the cached data
$data = json_decode($cached_data, true);
// Check if the data is valid
if (is_array($data)) {
// Delete the cached data from Redis
redis->delete($fast_link_key);
// Return the data
$xlvask = new xlvask();
$order_arr = (new orders_o())->simulateOrderFromXLVask($xlvask->new($xlvask->helpers->xlvask_usage_log)->setProperties($data), true); // ['order' => $order_arr, 'order_items' => $items_arr]
$tmp_order_obj = (object)[];
$tmp_order_arr = $order_arr['order'] ?? [];
/**
* "id": -1,
* "customer_id": 39159000,
* "cashier_id": 2285,
* "reference": "Simulated Order from XL Vask",
* "notes": "This is a simulated order generated from an XL Vask usage log",
* "department_id": 1,
* "reg_1": "DE55248",
* "reg_2": "",
* "reg_3": "",
* "completed_at": null,
* "created_at": "2025-07-16 13:04:54",
* "deleted_at": null,
* "total_net_amount": 683,
* "invoice_collection_id": 0,
* "booking_id": 0,
* "wash_id": "b24728ea-e22b-4dce-8cd3-a0998f7fdc5e",
* "lane": 1,
* "closed_at": null
*/
$tmp_order_obj->customer_id = $tmp_order_arr['customer_id'] ?? 0;
$tmp_order_obj->department_id = $tmp_order_arr['department_id'] ?? 0;
$tmp_order_obj->reg_1 = $tmp_order_arr['reg_1'] ?? '';
$tmp_order_obj->reg_2 = $tmp_order_arr['reg_2'] ?? '';
$tmp_order_obj->reg_3 = $tmp_order_arr['reg_3'] ?? '';
$tmp_order_obj->created_at = $tmp_order_arr['created_at'] ?? '';
$tmp_order_obj->wash_id = $tmp_order_arr['wash_id'] ?? '';
$tmp_order_obj->lane = $tmp_order_arr['lane'] ?? 0;
$response->success([
...$order_arr,
'potential_duplicates' => (new orders_o())->getOrderPotentialDuplicatesIfFound(
$tmp_order_obj->reg_1,
$tmp_order_obj->reg_2,
$tmp_order_obj->reg_3,
$tmp_order_obj->department_id,
$tmp_order_obj->created_at,
),
]);
} else {
// Return an error if the data is not valid
$response->error('Invalid cached data', 400);
}
} else {
// Return an error if the fast link key does not exist in Redis
$response->error('Fast link key not found', 404);
}
} else {
// Return an error if the fast link key is invalid
$response->error('Invalid fast link key format', 400);
}
},
[
'fast_link_key' => 'string', // Example: 'temporary_cache_6878cf0603d77'
]
);
}
}