diff --git a/services/nginx/app/routes/moduleSelfServeRoute.php b/services/nginx/app/routes/moduleSelfServeRoute.php index ff92d84e..174395a9 100644 --- a/services/nginx/app/routes/moduleSelfServeRoute.php +++ b/services/nginx/app/routes/moduleSelfServeRoute.php @@ -569,6 +569,30 @@ class moduleSelfServeRoute 'subuser_id' => $subuser === false ? null : (int)$subuser->id, ]); $lane->execute($command, $args); + if ($command === selfserve_lane_command::START) { + try { + $license_plate = selfserve::standardize_registration( + (string)($args->license_plate ?: $lane->getLicensePlate()) + ); + if ($license_plate !== '') { + (new selfserve_wash_flow())->synchronizeSession( + $lane_id, + $license_plate, + $customer_number > 0 ? $customer_number : null, + false, + null, + false + ); + } + } catch (\Throwable $e) { + error_log( + 'Failed to persist self-serve START session for lane ' + . $lane_id + . ': ' + . $e->getMessage() + ); + } + } $response->success([ 'id' => $lane->id, 'status' => $lane->getLaneStatus()->name, diff --git a/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php b/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php index 37730a61..5445d019 100644 --- a/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php +++ b/services/nginx/app/tests/Api/SelfserveCustomerStartApiTest.php @@ -360,6 +360,74 @@ it('does not create an active wash preview session from read-only eligibility ch expect($openSession)->toBeNull(); }); +it('creates a durable wash session from customer start after read-only eligibility', function (): void { + $group = api_fixtures()->createGroup([], [ + 'list_own_department_selfserve_vehicle_conditions', + ]); + $scenario = api_fixtures()->createSelfServeScenario([ + 'customer' => ['group_id' => $group['id']], + 'department_selfserve_enabled' => true, + 'lane_selfserve_enabled' => true, + ]); + $headers = api_fixtures()->bearerHeaders( + api_fixtures()->createAuthToken((int)$scenario['customer']['id']) + ); + $laneId = (int)$scenario['lane']['id']; + $reg = (string)$scenario['vehicle']['reg']; + $productId = (int)$scenario['product']['id']; + $sessionId = (int)$scenario['session']['id']; + + api_test_runtime()->db()->query( + 'DELETE FROM selfserve_wash_session_events WHERE session_id = ' . $sessionId + ); + api_test_runtime()->db()->query( + 'DELETE FROM selfserve_wash_session_tasks WHERE session_id = ' . $sessionId + ); + api_test_runtime()->db()->query( + 'DELETE FROM selfserve_wash_session_answers WHERE session_id = ' . $sessionId + ); + api_test_runtime()->db()->query( + 'DELETE FROM selfserve_wash_sessions WHERE id = ' . $sessionId + ); + + selfserve_customer_start_make_available($laneId); + + api_client() + ->get( + '/department/selfserve/vehicle/allowed?lane_id=' . $laneId + . '®=' . urlencode($reg) + . '&vehicle_type=' . $productId, + $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); + + $openSession = api_test_runtime()->queryOne( + 'SELECT id, status, customer_number FROM selfserve_wash_sessions' + . ' WHERE lane_id = ' . $laneId + . ' AND reg = "' . api_test_runtime()->db()->real_escape_string($reg) . '"' + . ' AND completed_at IS NULL' + . ' AND deleted_at IS NULL' + . ' ORDER BY id DESC LIMIT 1' + ); + + expect($openSession)->not->toBeNull() + ->and((int)$openSession['customer_number'])->toBe((int)$scenario['customer']['customer_number']) + ->and($openSession['status'])->toBe('READY_FOR_MACHINE_START'); + + selfserve_customer_start_make_available($laneId); +}); + it('does not create a replacement session when refreshing a completed summary with vehicle type', 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 bbb2f6fc..87ba8a63 100644 --- a/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php +++ b/services/nginx/app/tests/Unit/Selfserve/SelfserveRouteWiringTest.php @@ -509,6 +509,10 @@ it('classifies self-serve lane command route authorization by customer product b expect($productDocs)->toContain('Customer `START` requires an enabled self-serve lane. Customer `STOP` and property gate commands require the customer\'s active wash in the lane department.'); expect($washFlow)->toContain('$payload[\'command\'] = $relayRole === \'PROPERTY_ENTRANCE\' ? \'OPEN_PROPERTY_ACCESS_GATE\' : \'OPEN_PROPERTY_EXIT_GATE\''); expect($washFlow)->toContain('$signalType = \'studio_action_gate_open\''); + expect($moduleSelfServeRoute) + ->toContain('if ($command === selfserve_lane_command::START)') + ->toContain('(new selfserve_wash_flow())->synchronizeSession(') + ->toContain('Failed to persist self-serve START session'); $commandCases = selfserve_lane_command_route_cases($moduleSelfServeRoute);