From 3cdf1571c5dca64482e345e55c3208b4dfd6c1f5 Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Fri, 12 Jun 2026 11:48:51 +0200 Subject: [PATCH] Avoid duplicate self-serve stop relay cleanup --- .../selfserve/classes/selfserve_wash_flow.php | 6 ++++-- .../interfaces/selfserve_wash_flow_i.php | 2 +- .../nginx/app/modules/selfserve/selfserve.md | 2 +- .../traits/selfserve_lane_command_t.php | 3 ++- ...SelfserveWashCompletionRelayWiringTest.php | 21 +++++++++++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php b/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php index 99807a3a..1228babf 100644 --- a/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php +++ b/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php @@ -425,7 +425,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i return $this->getSessionSummary((int)$session->id); } - public function completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null): ?array + public function completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null, bool $disableRelays = true): ?array { $session = $reg !== null ? $this->findLatestOpenSession($laneId, selfserve::standardize_registration($reg), $customerNumber) @@ -438,7 +438,9 @@ class selfserve_wash_flow implements selfserve_wash_flow_i if (!$session->markCompletedIfOpen($orderId)) { return $this->getSessionSummary((int)$session->id); } - $this->disableMachineRelayForCompletedWash($laneId); + if ($disableRelays) { + $this->disableMachineRelayForCompletedWash($laneId); + } $this->logSessionEvent((int)$session->id, selfserve_wash_event_type::SESSION_COMPLETED, [ 'lane_id' => $laneId, 'reg' => $reg === null ? (string)$session->reg->value() : selfserve::standardize_registration($reg), diff --git a/services/nginx/app/modules/selfserve/interfaces/selfserve_wash_flow_i.php b/services/nginx/app/modules/selfserve/interfaces/selfserve_wash_flow_i.php index 587384dc..d4021f22 100644 --- a/services/nginx/app/modules/selfserve/interfaces/selfserve_wash_flow_i.php +++ b/services/nginx/app/modules/selfserve/interfaces/selfserve_wash_flow_i.php @@ -14,7 +14,7 @@ interface selfserve_wash_flow_i public function getLatestSessionSummary(int $laneId, string $reg): array; - public function completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null): ?array; + public function completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null, bool $disableRelays = true): ?array; public function forceStopLane(int $laneId, ?int $sessionId = null, bool $bill = false, ?string $reason = null, ?int $userId = null): array; } diff --git a/services/nginx/app/modules/selfserve/selfserve.md b/services/nginx/app/modules/selfserve/selfserve.md index 15140678..442a3822 100644 --- a/services/nginx/app/modules/selfserve/selfserve.md +++ b/services/nginx/app/modules/selfserve/selfserve.md @@ -439,7 +439,7 @@ Public methods: | `recordMachineStartWebhook(int $laneId, ?string $reg = null, array $payload = [])` | The machine button or hardware event fired. | Full session summary after the machine-start event. | | `getSessionSummary(int $sessionId)` | You have a session id already. | Full session summary. | | `getLatestSessionSummary(int $laneId, string $reg)` | You want the latest session for a lane and vehicle. | Full session summary. | -| `completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null)` | STOP has finished and you want to close the latest open session. | Full summary, or `null` if no open session exists. | +| `completeLatestSessionForLane(int $laneId, ?string $reg = null, ?int $customerNumber = null, ?int $orderId = null, bool $disableRelays = true)` | STOP has finished and you want to close the latest open session. Normal STOP passes `false` because it already disabled relays before opening the exit port. | Full summary, or `null` if no open session exists. | Key implementation details: diff --git a/services/nginx/app/modules/selfserve/traits/selfserve_lane_command_t.php b/services/nginx/app/modules/selfserve/traits/selfserve_lane_command_t.php index 304891bf..eca32e5b 100644 --- a/services/nginx/app/modules/selfserve/traits/selfserve_lane_command_t.php +++ b/services/nginx/app/modules/selfserve/traits/selfserve_lane_command_t.php @@ -538,7 +538,8 @@ trait selfserve_lane_command_t $this->id, $this->getLicensePlate() ?: null, $this->getCustomerNumber() ?: null, - method_exists($this, 'getLastInvoiceOrderId') ? $this->getLastInvoiceOrderId() : null + method_exists($this, 'getLastInvoiceOrderId') ? $this->getLastInvoiceOrderId() : null, + false ); } catch (\Throwable) { // Session completion must not block STOP flow. diff --git a/services/nginx/app/tests/Unit/Selfserve/SelfserveWashCompletionRelayWiringTest.php b/services/nginx/app/tests/Unit/Selfserve/SelfserveWashCompletionRelayWiringTest.php index ebd0f9f3..8839effa 100644 --- a/services/nginx/app/tests/Unit/Selfserve/SelfserveWashCompletionRelayWiringTest.php +++ b/services/nginx/app/tests/Unit/Selfserve/SelfserveWashCompletionRelayWiringTest.php @@ -79,6 +79,8 @@ it('forces machine and cleaner relays off when a self-serve wash session is comp expect($washFlow)->not->toBeFalse(); expect($washFlow)->toContain('$this->disableMachineRelayForCompletedWash($laneId);'); + expect($washFlow)->toContain('bool $disableRelays = true'); + expect($washFlow)->toContain('if ($disableRelays) {'); $methodOffset = strpos($washFlow, 'protected function disableMachineRelayForCompletedWash'); expect($methodOffset)->not->toBeFalse(); @@ -108,3 +110,22 @@ it('always dispatches completion relay off for configured machine relays without [selfserve_lane_relay::MACHINE_CLEANER, false], ]); }); + +it('normal STOP completion skips duplicate completion relay cleanup after STOP already disabled relays', function (): void { + $commandTrait = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_command_t.php')); + + expect($commandTrait)->not->toBeFalse(); + $methodOffset = strpos($commandTrait, 'protected function completeLatestSessionForStop(): void'); + expect($methodOffset)->not->toBeFalse(); + + $methodBody = substr($commandTrait, (int)$methodOffset, 1500); + expect($methodBody)->toContain(<<<'PHP' +(new \modules\selfserve\classes\selfserve_wash_flow())->completeLatestSessionForLane( + $this->id, + $this->getLicensePlate() ?: null, + $this->getCustomerNumber() ?: null, + method_exists($this, 'getLastInvoiceOrderId') ? $this->getLastInvoiceOrderId() : null, + false + ); +PHP); +});