From 8aefbd8fb3df1427a54bb865bd1cc1ef402df8ae Mon Sep 17 00:00:00 2001 From: Jeppe B <2jepp9350@gmail.com> Date: Thu, 9 Jul 2026 10:24:02 +0200 Subject: [PATCH] Guard wash subscription distribution SQL Guard the wash subscription distribution query after invoice-inclusion filtering removes all candidate orders, preventing an empty IN () clause on the invoicing distribution endpoint. Verified with focused syntax, Pest, PHPStan, and invoicing unit-suite checks. --- .../nginx/app/objects/customer_vehicles_o.php | 13 +++++++------ .../WashSubscriptionDistributionSqlGuardTest.php | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 services/nginx/app/tests/Unit/Invoicing/WashSubscriptionDistributionSqlGuardTest.php diff --git a/services/nginx/app/objects/customer_vehicles_o.php b/services/nginx/app/objects/customer_vehicles_o.php index e63a5746..baafaabb 100644 --- a/services/nginx/app/objects/customer_vehicles_o.php +++ b/services/nginx/app/objects/customer_vehicles_o.php @@ -490,18 +490,19 @@ class customer_vehicles_o extends db return []; } //print_r($transaction_ids); - // Convert the array of transaction ids to a comma separated string - $orders = ''; + $orders = []; foreach ($transaction_ids as $transaction_id) { // Check if the transaction is included in the invoicing. $tmp = (new orders_o())->select((int)$transaction_id); if (!$tmp->isIncludedInInvoicing()) { continue; // The transaction is not included in the invoicing, skip it } - $orders .= (int)$transaction_id . ','; + $orders[] = (int)$transaction_id; } - // Remove the last comma - $orders = rtrim($orders, ','); + if (empty($orders)) { + return []; + } + $order_ids = implode(',', $orders); // Get the first two transactions that are not deleted and contains at least one order item with the 'product_id' of the vehicle type for the vehicle $query = " SELECT o.id @@ -509,7 +510,7 @@ class customer_vehicles_o extends db JOIN order_items oi ON oi.order_id = o.id WHERE o.deleted_at IS NULL AND oi.deleted_at IS NULL - AND o.id IN ($orders) + AND o.id IN ($order_ids) AND oi.product_id = " . (int)$this->type->value() . " GROUP BY o.id ORDER BY o.created_at ASC diff --git a/services/nginx/app/tests/Unit/Invoicing/WashSubscriptionDistributionSqlGuardTest.php b/services/nginx/app/tests/Unit/Invoicing/WashSubscriptionDistributionSqlGuardTest.php new file mode 100644 index 00000000..8e58ff6f --- /dev/null +++ b/services/nginx/app/tests/Unit/Invoicing/WashSubscriptionDistributionSqlGuardTest.php @@ -0,0 +1,15 @@ +not->toBeFalse(); + $content = (string)$content; + + expect($content) + ->toContain('$orders = [];') + ->toMatch('/\$orders\[\] = \(int\)\$transaction_id;\s*}\s*if \(empty\(\$orders\)\) {\s*return \[\];\s*}\s*\$order_ids = implode/s') + ->toContain('AND o.id IN ($order_ids)') + ->not->toContain('rtrim($orders, \',\')') + ->not->toContain('AND o.id IN ($orders)'); +});