From 1d43221b4d0883f4da7842b76f3ed19ab997b79c Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Wed, 3 Jun 2026 20:32:35 +0200 Subject: [PATCH] Sync self-serve machine relay session state --- .../selfserve_lane_relay_controller_t.php | 34 +++++++++- .../Api/SelfserveCustomerStartApiTest.php | 67 +++++++++++++++++++ .../Selfserve/SelfserveRouteWiringTest.php | 5 ++ 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/services/nginx/app/modules/selfserve/traits/selfserve_lane_relay_controller_t.php b/services/nginx/app/modules/selfserve/traits/selfserve_lane_relay_controller_t.php index 7c9f3f47..792b30ab 100644 --- a/services/nginx/app/modules/selfserve/traits/selfserve_lane_relay_controller_t.php +++ b/services/nginx/app/modules/selfserve/traits/selfserve_lane_relay_controller_t.php @@ -12,6 +12,7 @@ use modules\selfserve\helpers\selfserve_lane_relay; use modules\selfserve\helpers\selfserve_lane_services; use modules\selfserve\helpers\selfserve_lane_status; use modules\shelly\helpers\shelly_request_body_get_states; +use objects\selfserve_wash_sessions_o; trait selfserve_lane_relay_controller_t { @@ -917,7 +918,38 @@ trait selfserve_lane_relay_controller_t } } - return $this->sendRelaySwitchCommand($relay, true, $duration); + $result = $this->sendRelaySwitchCommand($relay, true, $duration); + if ($result && $relay === selfserve_lane_relay::MACHINE) { + $this->markLatestSelfServeSessionRelayEnabledForLane(); + } + + return $result; + } + + protected function markLatestSelfServeSessionRelayEnabledForLane(): void + { + try { + $customerNumber = (int)$this->getCustomerNumber(); + $session = (new selfserve_wash_sessions_o())->selectLatestOpenByLane( + (int)$this->id, + $customerNumber > 0 ? $customerNumber : null + ); + if (!$session->exists() && $customerNumber > 0) { + $session = (new selfserve_wash_sessions_o())->selectLatestOpenByLane((int)$this->id); + } + if (!$session->exists() || (bool)$session->machine_relay_enabled->value() === true) { + return; + } + + $session->markRelayEnabled(); + } catch (\Throwable $e) { + error_log( + 'Failed to synchronize self-serve machine relay session state for lane ' + . (int)$this->id + . ': ' + . $e->getMessage() + ); + } } /** diff --git a/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php b/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php index 0badb666..1c39b68e 100644 --- a/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php +++ b/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php @@ -86,6 +86,73 @@ it('allows the customer self-serve start sequence without department access', fu ->toBe((int)$scenario['customer']['customer_number']); }); +it('marks the active customer session relay-enabled after machine relay enable', function (): void { + $group = api_fixtures()->createGroup([], [ + 'list_own_department_selfserve_vehicle_conditions', + 'modules_selfserve_lane_relay_enable_machine', + ]); + $scenario = api_fixtures()->createSelfServeScenario([ + 'customer' => ['group_id' => $group['id']], + 'department_selfserve_enabled' => true, + 'lane_selfserve_enabled' => true, + 'session' => [ + 'status' => 'READY_FOR_MACHINE_START', + 'machine_relay_enabled' => 0, + 'machine_relay_enabled_at' => null, + 'machine_start_triggered' => 0, + 'machine_start_triggered_at' => null, + 'wash_started_at' => null, + ], + ]); + $headers = api_fixtures()->bearerHeaders( + api_fixtures()->createAuthToken((int)$scenario['customer']['id']) + ); + $laneId = (int)$scenario['lane']['id']; + $reg = (string)$scenario['vehicle']['reg']; + $machineTaskId = (int)$scenario['tasks'][1]['id']; + + selfserve_customer_start_make_available($laneId); + + api_client() + ->get('/department/selfserve/vehicle/allowed?lane_id=' . $laneId . '®=' . urlencode($reg), $headers) + ->assertStatus(200) + ->assertSuccess(true); + + api_client() + ->post('/modules/self-serve/lane/services/allowed', [ + 'lane_id' => $laneId, + 'task_ids' => [$machineTaskId], + ], $headers) + ->assertStatus(200) + ->assertSuccess(true); + + api_client() + ->post('/modules/self-serve/lane/command', [ + 'lane_id' => $laneId, + 'command' => 'START', + 'license_plate' => $reg, + 'defer_relay_side_effects' => true, + ], $headers) + ->assertStatus(200) + ->assertSuccess(true); + + api_client() + ->post('/modules/self-serve/lane/relay/machine/enable', [ + 'lane_id' => $laneId, + ], $headers) + ->assertStatus(200) + ->assertSuccess(true); + + $activeResponse = api_client() + ->get('/modules/self-serve/lane/wash/my-active-wash', $headers) + ->assertStatus(200) + ->assertSuccess(true); + + expect($activeResponse->data()['session']['status'] ?? null)->toBe('MACHINE_RELAY_ENABLED') + ->and($activeResponse->data()['session']['machine_relay_enabled'] ?? null)->toBeTrue() + ->and($activeResponse->data()['session']['machine_start_triggered'] ?? null)->toBeFalse(); +}); + it('derives allowed services from v2 session task snapshots when task rows are not legacy records', function (): void { $group = api_fixtures()->createGroup([], [ 'list_own_department_selfserve_vehicle_conditions', diff --git a/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php b/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php index b947b6db..5b8fbc83 100644 --- a/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php +++ b/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php @@ -181,8 +181,10 @@ it('keeps legacy self-serve CRUD routes syncing canonical drafts', function (): it('wires machine relay status get and set endpoints', function (): void { $moduleSelfServeRoute = file_get_contents(app_path('routes/moduleSelfServeRoute.php')); + $relayController = file_get_contents(app_path('modules/selfserve/traits/selfserve_lane_relay_controller_t.php')); expect($moduleSelfServeRoute)->not->toBeFalse(); + expect($relayController)->not->toBeFalse(); expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/status'); expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine/set'); expect($moduleSelfServeRoute)->toContain('/modules/self-serve/lane/relay/machine_program_picker/status'); @@ -196,6 +198,9 @@ it('wires machine relay status get and set endpoints', function (): void { expect($moduleSelfServeRoute)->toContain('getMachineCleanerRelayStatus'); expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatus'); expect($moduleSelfServeRoute)->toContain('setMachineCleanerRelayStatusHard(true)'); + expect($relayController)->toContain('markLatestSelfServeSessionRelayEnabledForLane'); + expect($relayController)->toContain('selectLatestOpenByLane('); + expect($relayController)->toContain('$session->markRelayEnabled();'); expect($moduleSelfServeRoute)->toContain('applyShellyTransportOverride($lane)'); expect($moduleSelfServeRoute)->toContain("'transport' => \$this->requestedShellyTransportOverride()"); expect($moduleSelfServeRoute)->toContain('buildRelayStatusResponse');