Merge pull request #238 from copenhagentruckwash/fix-cache-only-lookup-for-invoice-flags

Normalize invoice-period cache keys and restore DB fallbacks for missing Redis entries
This commit is contained in:
Jeppe B
2026-06-01 23:41:26 +02:00
committed by GitHub
3 changed files with 82 additions and 16 deletions
@@ -320,6 +320,16 @@ class invoice_period_flag_service
}
public function warmManualFlagsCache(): void
{
$flags = $this->fetchActiveManualFlagsFromDb();
try {
(new redis())->cache_invoice_period_manual_flags($flags);
} catch (Throwable) {
}
}
private function fetchActiveManualFlagsFromDb(): array
{
global $db;
@@ -337,10 +347,7 @@ class invoice_period_flag_service
}
}
try {
(new redis())->cache_invoice_period_manual_flags($flags);
} catch (Throwable) {
}
return $flags;
}
private function formatStoredFlag(array $row): array
@@ -388,15 +395,11 @@ class invoice_period_flag_service
}
if (!is_array($flags)) {
// Cache miss — warm on demand and re-fetch
$this->warmManualFlagsCache();
// Cache miss — read from the database and refresh Redis without hiding active flags.
$flags = $this->fetchActiveManualFlagsFromDb();
try {
$flags = (new redis())->get_invoice_period_manual_flags();
(new redis())->cache_invoice_period_manual_flags($flags);
} catch (Throwable) {
return [];
}
if (!is_array($flags)) {
return [];
}
}
@@ -529,7 +532,7 @@ class invoice_period_flag_service
}
if (!is_array($flags)) {
$flags = $this->buildAutomaticFlagsForPeriod($dateFrom, $dateTo);
$flags = $this->calculateAutomaticFlagsForPeriod($dateFrom, $dateTo);
$this->cacheAutomaticFlagsForPeriod($dateFrom, $dateTo, $flags);
}
@@ -545,15 +548,17 @@ class invoice_period_flag_service
public function warmAutomaticFlagsForPeriod(string $dateFrom, string $dateTo): void
{
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
$this->cacheAutomaticFlagsForPeriod(
$dateFrom,
$dateTo,
$this->buildAutomaticFlagsForPeriod($dateFrom, $dateTo)
$this->calculateAutomaticFlagsForPeriod($dateFrom, $dateTo)
);
}
private function buildAutomaticFlagsForPeriod(string $dateFrom, string $dateTo): array
private function calculateAutomaticFlagsForPeriod(string $dateFrom, string $dateTo): array
{
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
$rows = $this->getPeriodOrderItemRows($dateFrom, $dateTo, null);
$attributes = $this->getCustomerAttributes(null);
@@ -611,6 +616,7 @@ class invoice_period_flag_service
private function getPeriodOrderItemRows(string $dateFrom, string $dateTo, ?array $onlyCustomerNumbers): array
{
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
try {
$rows = (new redis())->get_invoice_period_order_item_rows($dateFrom, $dateTo);
} catch (Throwable) {
@@ -639,6 +645,7 @@ class invoice_period_flag_service
public function warmOrderItemRowsForPeriod(string $dateFrom, string $dateTo): void
{
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
$rows = $this->fetchOrderItemRowsFromDb($dateFrom, $dateTo);
try {
(new redis())->cache_invoice_period_order_item_rows($dateFrom, $dateTo, $rows);
@@ -646,6 +653,24 @@ class invoice_period_flag_service
}
}
private function normalizePeriodDateRange(string $dateFrom, string $dateTo): array
{
return [
$this->normalizePeriodDate($dateFrom, true),
$this->normalizePeriodDate($dateTo, false),
];
}
private function normalizePeriodDate(string $date, bool $startOfDay): string
{
$timestamp = strtotime($date);
if ($timestamp === false) {
return $date;
}
return date($startOfDay ? 'Y-m-d 00:00:00' : 'Y-m-d 23:59:59', $timestamp);
}
private function fetchOrderItemRowsFromDb(string $dateFrom, string $dateTo): array
{
global $db;
+18 -2
View File
@@ -392,7 +392,19 @@ class redis implements redis_i
private function invoicePeriodCacheKey(string $prefix, string $dateFrom, string $dateTo): string
{
return $prefix . ':' . $dateFrom . ':' . $dateTo;
return $prefix . ':'
. $this->normalizeInvoicePeriodCacheDate($dateFrom, true) . ':'
. $this->normalizeInvoicePeriodCacheDate($dateTo, false);
}
private function normalizeInvoicePeriodCacheDate(string $date, bool $startOfDay): string
{
$timestamp = strtotime($date);
if ($timestamp === false) {
return $date;
}
return date($startOfDay ? 'Y-m-d 00:00:00' : 'Y-m-d 23:59:59', $timestamp);
}
private function workfeedEmployeeNameCacheKey(string $employeeId): string
@@ -512,7 +524,11 @@ class redis implements redis_i
*/
public function enqueue_invoice_period_warming(string $dateFrom, string $dateTo): self
{
$this->get_client()->sadd('invoice_period_warming_queue', [$dateFrom . '|' . $dateTo]);
$this->get_client()->sadd('invoice_period_warming_queue', [
$this->normalizeInvoicePeriodCacheDate($dateFrom, true)
. '|'
. $this->normalizeInvoicePeriodCacheDate($dateTo, false),
]);
return $this;
}
@@ -21,6 +21,31 @@ function invoice_period_flag_service_invoke(string $method, array $args = []): m
return $target->invokeArgs($service, $args);
}
it('normalizes invoice period date ranges to full-day timestamps before cache-backed reads', function (): void {
expect(invoice_period_flag_service_invoke('normalizePeriodDateRange', ['2025-03-01', '2025-03-31']))
->toBe(['2025-03-01 00:00:00', '2025-03-31 23:59:59']);
expect(invoice_period_flag_service_invoke('normalizePeriodDateRange', [
'2025-03-01 00:00:00',
'2025-03-31 23:59:59',
]))->toBe(['2025-03-01 00:00:00', '2025-03-31 23:59:59']);
});
it('canonicalizes invoice period redis keys for bare dates and normalized API timestamps', function (): void {
$reflection = new ReflectionClass(\classes\redis::class);
$redis = $reflection->newInstanceWithoutConstructor();
$key = $reflection->getMethod('invoicePeriodCacheKey');
$key->setAccessible(true);
expect($key->invoke($redis, 'invoice_period_automatic_flags', '2025-03-01', '2025-03-31'))
->toBe($key->invoke(
$redis,
'invoice_period_automatic_flags',
'2025-03-01 00:00:00',
'2025-03-31 23:59:59'
));
});
it('builds deterministic automatic flag fingerprints and interactive price message parts', function (): void {
$row = [
'customer_number' => 424242,