Add unit tests for various modules: attachments grouping, department weather caching behaviors, economic module order sanitization, enriched order batching, and user cashier name lookups. Update related route logic for enhanced data fetching and caching integrations.

This commit is contained in:
Jeppe Bundgaard
2026-03-24 14:59:16 +01:00
parent 4a5dd3a83f
commit 6fdc8d466e
19 changed files with 1798 additions and 84 deletions
+166
View File
@@ -23,6 +23,7 @@ use objects\departments_o;
use objects\bookings_o;
use objects\logs_o;
use objects\users_o;
use routes\moduleWeatherAPIRoute;
if (!defined('WD')) {
exit;
@@ -105,6 +106,12 @@ $cron_tasks = [
'next_run' => 0,
'function' => 'PreRenderDynamicImagesCron',
],
'PreloadDepartmentWeatherResponsesCron' => [
'interval' => 60, // 1 minute
'last_run' => 0,
'next_run' => 0,
'function' => 'PreloadDepartmentWeatherResponsesCron',
],
'GoalsProgressAlertsCron' => [
'interval' => 60, // check every minute
'last_run' => 0,
@@ -626,6 +633,165 @@ function mergeUniqueIntValues(array $base, array $append): array
return array_values($result);
}
function PreloadDepartmentWeatherResponsesCron(): void
{
$start = microtime(true);
if (!defined('redis')) {
warn('PreloadDepartmentWeatherResponsesCron skipped: Redis is unavailable.');
return;
}
$maxDepartmentsRaw = getenv('DEPARTMENTS_WEATHER_PRELOAD_MAX_DEPARTMENTS');
$maxDepartmentsPerRun = max(
1,
(int)(
$maxDepartmentsRaw !== false && trim((string)$maxDepartmentsRaw) !== ''
? $maxDepartmentsRaw
: 25
)
);
$hotLimitRaw = getenv('DEPARTMENTS_WEATHER_PRELOAD_HOT_LIMIT');
$hotLimit = max(
1,
(int)(
$hotLimitRaw !== false && trim((string)$hotLimitRaw) !== ''
? $hotLimitRaw
: 50
)
);
$hotTtlRaw = getenv('DEPARTMENTS_WEATHER_PRELOAD_HOT_TTL');
$hotTtl = max(
1,
(int)(
$hotTtlRaw !== false && trim((string)$hotTtlRaw) !== ''
? $hotTtlRaw
: 900
)
);
try {
$departmentRows = (new departments_o())->list(true);
} catch (Throwable $e) {
warn('PreloadDepartmentWeatherResponsesCron failed to list departments: ' . $e->getMessage());
return;
}
if (!is_array($departmentRows) || $departmentRows === []) {
echo "[" . date('Y-m-d H:i:s') . "][CRON] PreloadDepartmentWeatherResponsesCron: no departments found.\n";
return;
}
usort($departmentRows, static function (array $left, array $right): int {
$leftPriority = isset($left['order_priority']) ? (int)$left['order_priority'] : PHP_INT_MAX;
$rightPriority = isset($right['order_priority']) ? (int)$right['order_priority'] : PHP_INT_MAX;
if ($leftPriority !== $rightPriority) {
return $leftPriority <=> $rightPriority;
}
return ((int)($left['id'] ?? 0)) <=> ((int)($right['id'] ?? 0));
});
$departmentIds = [];
foreach ($departmentRows as $row) {
$departmentId = (int)($row['id'] ?? 0);
if ($departmentId < 1) {
continue;
}
if (isset($row['visible']) && (int)$row['visible'] === 0) {
continue;
}
$departmentIds[$departmentId] = true;
if (count($departmentIds) >= $maxDepartmentsPerRun) {
break;
}
}
// Fallback: if visibility is unavailable or all are hidden, include all departments up to cap.
if ($departmentIds === []) {
foreach ($departmentRows as $row) {
$departmentId = (int)($row['id'] ?? 0);
if ($departmentId < 1) {
continue;
}
$departmentIds[$departmentId] = true;
if (count($departmentIds) >= $maxDepartmentsPerRun) {
break;
}
}
}
$departmentIds = array_map('intval', array_keys($departmentIds));
sort($departmentIds, SORT_NUMERIC);
if ($departmentIds === []) {
echo "[" . date('Y-m-d H:i:s') . "][CRON] PreloadDepartmentWeatherResponsesCron: no preloadable departments.\n";
return;
}
$warmedSets = 0;
$failedSets = 0;
$totalEntries = 0;
$hotTargetsRequested = 0;
$hotTargetsWarmed = 0;
$hotTargets = moduleWeatherAPIRoute::getDepartmentWeatherHotPreloadTargets($hotLimit, $hotTtl);
$hotTargetsRequested = count($hotTargets);
foreach ($hotTargets as $target) {
try {
$result = moduleWeatherAPIRoute::preloadDepartmentWeatherTimelineCache(
$target['department_ids'] ?? [],
null,
null,
$target['timeline_range'] ?? null
);
if (($result['warmed'] ?? false) === true) {
$warmedSets++;
$hotTargetsWarmed++;
$totalEntries += (int)($result['entries'] ?? 0);
}
} catch (Throwable $e) {
$failedSets++;
warn('PreloadDepartmentWeatherResponsesCron failed for hot target: ' . $e->getMessage());
}
}
foreach ($departmentIds as $departmentId) {
try {
$result = moduleWeatherAPIRoute::preloadDepartmentWeatherTimelineCache([$departmentId]);
if (($result['warmed'] ?? false) === true) {
$warmedSets++;
$totalEntries += (int)($result['entries'] ?? 0);
}
} catch (Throwable $e) {
$failedSets++;
warn('PreloadDepartmentWeatherResponsesCron failed for department #' . $departmentId . ': ' . $e->getMessage());
}
}
if (count($departmentIds) > 1) {
try {
$result = moduleWeatherAPIRoute::preloadDepartmentWeatherTimelineCache($departmentIds);
if (($result['warmed'] ?? false) === true) {
$warmedSets++;
$totalEntries += (int)($result['entries'] ?? 0);
}
} catch (Throwable $e) {
$failedSets++;
warn('PreloadDepartmentWeatherResponsesCron failed for aggregate department set: ' . $e->getMessage());
}
}
$duration = round(microtime(true) - $start, 2);
echo "[" . date('Y-m-d H:i:s') . "][CRON] PreloadDepartmentWeatherResponsesCron completed. "
. "departments=" . count($departmentIds)
. " warmed_sets=$warmedSets failed_sets=$failedSets total_entries=$totalEntries "
. "hot_targets_requested=$hotTargetsRequested hot_targets_warmed=$hotTargetsWarmed "
. "max_departments_per_run=$maxDepartmentsPerRun hot_limit=$hotLimit hot_ttl=$hotTtl "
. "duration={$duration}s\n";
}
/**
* GoalsProgressAlertsCron
*