From 9b2d5d5291690714ce74476daf11b910e9a7e821 Mon Sep 17 00:00:00 2001 From: Jeppe B <2jepp9350@gmail.com> Date: Thu, 16 Jul 2026 12:06:42 +0200 Subject: [PATCH] Fix customer restriction CI regressions --- ...er_rule_product_restriction_schema_bootstrap.php | 2 +- .../customer_rule_product_restriction_service.php | 2 +- services/nginx/app/objects/order_items_o.php | 2 +- .../app/tests/Api/CustomerAttributesApiTest.php | 4 +++- .../Invoicing/InvoicingPeriodBackendOpenApiTest.php | 11 ++++++----- ...stomerRuleProductRestrictionArchitectureTest.php | 13 +++++++++++++ 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/services/nginx/app/classes/customer_rule_product_restriction_schema_bootstrap.php b/services/nginx/app/classes/customer_rule_product_restriction_schema_bootstrap.php index 11d3aa07..036df389 100644 --- a/services/nginx/app/classes/customer_rule_product_restriction_schema_bootstrap.php +++ b/services/nginx/app/classes/customer_rule_product_restriction_schema_bootstrap.php @@ -187,7 +187,7 @@ class customer_rule_product_restriction_schema_bootstrap $predicate = self::legacyPredicate($db, $attribute); $activePredicate = self::columnExists($db, 'products', 'deleted_at') - ? "(p.deleted_at IS NULL OR p.deleted_at = '')" + ? 'p.deleted_at IS NULL' : '1 = 1'; $insert = $db->query( "INSERT IGNORE INTO customer_rule_product_collection_products (collection_id, product_id) diff --git a/services/nginx/app/classes/customer_rule_product_restriction_service.php b/services/nginx/app/classes/customer_rule_product_restriction_service.php index eeada1c8..38725468 100644 --- a/services/nginx/app/classes/customer_rule_product_restriction_service.php +++ b/services/nginx/app/classes/customer_rule_product_restriction_service.php @@ -353,7 +353,7 @@ class customer_rule_product_restriction_service { global $db; $activeExpression = $this->columnExists('products', 'deleted_at') - ? "CASE WHEN p.deleted_at IS NULL OR p.deleted_at = '' THEN 1 ELSE 0 END" + ? 'CASE WHEN p.deleted_at IS NULL THEN 1 ELSE 0 END' : '1'; $result = $db->query( "SELECT p.id, p.name, p.category AS category_id, c.name AS category_name, diff --git a/services/nginx/app/objects/order_items_o.php b/services/nginx/app/objects/order_items_o.php index 1838bb47..16b9dca9 100644 --- a/services/nginx/app/objects/order_items_o.php +++ b/services/nginx/app/objects/order_items_o.php @@ -107,7 +107,7 @@ class order_items_o extends db "SELECT order_id FROM order_items WHERE id = {$normalizedRelatedItemId} - AND (deleted_at IS NULL OR deleted_at = '') + AND deleted_at IS NULL LIMIT 1" ); if (!$result || $result->num_rows < 1) { diff --git a/services/nginx/app/tests/Api/CustomerAttributesApiTest.php b/services/nginx/app/tests/Api/CustomerAttributesApiTest.php index c94ecb29..ba97025a 100644 --- a/services/nginx/app/tests/Api/CustomerAttributesApiTest.php +++ b/services/nginx/app/tests/Api/CustomerAttributesApiTest.php @@ -111,7 +111,8 @@ it('returns exact product restrictions for product-impact attributes and null fo expect($byAttribute['restrictSpotFree']['product_restriction']['disabled_product_ids'] ?? []) ->toContain((int)$product['id']) ->and($byAttribute['restrictSpotFree']['product_restriction']['collections'] ?? [])->not->toBeEmpty() - ->and($byAttribute['exemptFromAdministrationFee']['product_restriction'] ?? 'missing')->toBeNull(); + ->and($byAttribute['exemptFromAdministrationFee'])->toHaveKey('product_restriction') + ->and($byAttribute['exemptFromAdministrationFee']['product_restriction'])->toBeNull(); }); it('keeps workflow-only customer attribute activation compatible', function (): void { @@ -141,6 +142,7 @@ it('keeps workflow-only customer attribute activation compatible', function (): api_client()->delete( '/customer/attributes?user_id=' . (int)$customer['id'] . '&attribute=exemptFromAdministrationFee', + null, $session['headers'] ) ->assertStatus(200) diff --git a/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodBackendOpenApiTest.php b/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodBackendOpenApiTest.php index 852fbd80..bcd7898e 100644 --- a/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodBackendOpenApiTest.php +++ b/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodBackendOpenApiTest.php @@ -1,10 +1,12 @@ toBeFile(); @@ -21,4 +23,3 @@ it('documents period invoice states and authenticated attachment content', funct expect($content)->toContain('enum: [draft, booked, none]'); } }); - diff --git a/services/nginx/app/tests/Unit/Orders/CustomerRuleProductRestrictionArchitectureTest.php b/services/nginx/app/tests/Unit/Orders/CustomerRuleProductRestrictionArchitectureTest.php index 265161ee..fe8775c9 100644 --- a/services/nginx/app/tests/Unit/Orders/CustomerRuleProductRestrictionArchitectureTest.php +++ b/services/nginx/app/tests/Unit/Orders/CustomerRuleProductRestrictionArchitectureTest.php @@ -48,3 +48,16 @@ it('fails closed when configured restriction reads cannot be completed', functio ->toContain('foreach (self::PRODUCT_IMPACT_ATTRIBUTES as $attribute)') ->toContain('Unable to validate collection products'); }); + +it('uses strict datetime-safe deleted row predicates', function (): void { + $schema = file_get_contents(app_path('classes/customer_rule_product_restriction_schema_bootstrap.php')); + $service = file_get_contents(app_path('classes/customer_rule_product_restriction_service.php')); + $orderItems = file_get_contents(app_path('objects/order_items_o.php')); + + expect($schema)->toContain('p.deleted_at IS NULL') + ->not->toContain("p.deleted_at = ''") + ->and($service)->toContain('p.deleted_at IS NULL') + ->not->toContain("p.deleted_at = ''") + ->and($orderItems)->toContain('AND deleted_at IS NULL') + ->not->toContain("deleted_at = ''"); +});