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:
@@ -320,6 +320,16 @@ class invoice_period_flag_service
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function warmManualFlagsCache(): void
|
public function warmManualFlagsCache(): void
|
||||||
|
{
|
||||||
|
$flags = $this->fetchActiveManualFlagsFromDb();
|
||||||
|
|
||||||
|
try {
|
||||||
|
(new redis())->cache_invoice_period_manual_flags($flags);
|
||||||
|
} catch (Throwable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchActiveManualFlagsFromDb(): array
|
||||||
{
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|
||||||
@@ -337,10 +347,7 @@ class invoice_period_flag_service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return $flags;
|
||||||
(new redis())->cache_invoice_period_manual_flags($flags);
|
|
||||||
} catch (Throwable) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatStoredFlag(array $row): array
|
private function formatStoredFlag(array $row): array
|
||||||
@@ -388,15 +395,11 @@ class invoice_period_flag_service
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($flags)) {
|
if (!is_array($flags)) {
|
||||||
// Cache miss — warm on demand and re-fetch
|
// Cache miss — read from the database and refresh Redis without hiding active flags.
|
||||||
$this->warmManualFlagsCache();
|
$flags = $this->fetchActiveManualFlagsFromDb();
|
||||||
try {
|
try {
|
||||||
$flags = (new redis())->get_invoice_period_manual_flags();
|
(new redis())->cache_invoice_period_manual_flags($flags);
|
||||||
} catch (Throwable) {
|
} catch (Throwable) {
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (!is_array($flags)) {
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +532,7 @@ class invoice_period_flag_service
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($flags)) {
|
if (!is_array($flags)) {
|
||||||
$flags = $this->buildAutomaticFlagsForPeriod($dateFrom, $dateTo);
|
$flags = $this->calculateAutomaticFlagsForPeriod($dateFrom, $dateTo);
|
||||||
$this->cacheAutomaticFlagsForPeriod($dateFrom, $dateTo, $flags);
|
$this->cacheAutomaticFlagsForPeriod($dateFrom, $dateTo, $flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,15 +548,17 @@ class invoice_period_flag_service
|
|||||||
|
|
||||||
public function warmAutomaticFlagsForPeriod(string $dateFrom, string $dateTo): void
|
public function warmAutomaticFlagsForPeriod(string $dateFrom, string $dateTo): void
|
||||||
{
|
{
|
||||||
|
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
|
||||||
$this->cacheAutomaticFlagsForPeriod(
|
$this->cacheAutomaticFlagsForPeriod(
|
||||||
$dateFrom,
|
$dateFrom,
|
||||||
$dateTo,
|
$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);
|
$rows = $this->getPeriodOrderItemRows($dateFrom, $dateTo, null);
|
||||||
$attributes = $this->getCustomerAttributes(null);
|
$attributes = $this->getCustomerAttributes(null);
|
||||||
|
|
||||||
@@ -611,6 +616,7 @@ class invoice_period_flag_service
|
|||||||
|
|
||||||
private function getPeriodOrderItemRows(string $dateFrom, string $dateTo, ?array $onlyCustomerNumbers): array
|
private function getPeriodOrderItemRows(string $dateFrom, string $dateTo, ?array $onlyCustomerNumbers): array
|
||||||
{
|
{
|
||||||
|
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
|
||||||
try {
|
try {
|
||||||
$rows = (new redis())->get_invoice_period_order_item_rows($dateFrom, $dateTo);
|
$rows = (new redis())->get_invoice_period_order_item_rows($dateFrom, $dateTo);
|
||||||
} catch (Throwable) {
|
} catch (Throwable) {
|
||||||
@@ -639,6 +645,7 @@ class invoice_period_flag_service
|
|||||||
|
|
||||||
public function warmOrderItemRowsForPeriod(string $dateFrom, string $dateTo): void
|
public function warmOrderItemRowsForPeriod(string $dateFrom, string $dateTo): void
|
||||||
{
|
{
|
||||||
|
[$dateFrom, $dateTo] = $this->normalizePeriodDateRange($dateFrom, $dateTo);
|
||||||
$rows = $this->fetchOrderItemRowsFromDb($dateFrom, $dateTo);
|
$rows = $this->fetchOrderItemRowsFromDb($dateFrom, $dateTo);
|
||||||
try {
|
try {
|
||||||
(new redis())->cache_invoice_period_order_item_rows($dateFrom, $dateTo, $rows);
|
(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
|
private function fetchOrderItemRowsFromDb(string $dateFrom, string $dateTo): array
|
||||||
{
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|||||||
@@ -392,7 +392,19 @@ class redis implements redis_i
|
|||||||
|
|
||||||
private function invoicePeriodCacheKey(string $prefix, string $dateFrom, string $dateTo): string
|
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
|
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
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,31 @@ function invoice_period_flag_service_invoke(string $method, array $args = []): m
|
|||||||
return $target->invokeArgs($service, $args);
|
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 {
|
it('builds deterministic automatic flag fingerprints and interactive price message parts', function (): void {
|
||||||
$row = [
|
$row = [
|
||||||
'customer_number' => 424242,
|
'customer_number' => 424242,
|
||||||
|
|||||||
Reference in New Issue
Block a user