Add tests for customer-scoped vehicle conditions and property gate permissions

- Introduced tests for `SelfserveNonOwnedVehicleWashAccess` to validate customer-scoped conditions for non-owned vehicles.
- Added `SelfservePropertyGatePermissionBypassTest` to ensure proper permission handling for lanes and departments.
- Updated `department_selfserve_vehicle_conditions_o` and routes to prevent cross-customer answer persistence.
- Enhanced `selfserve_wash_flow` with customer-scoped persisted answer logic and improved method parameters for vehicle eligibility and session synchronization.
- Adjusted OpenAPI spec and unit tests to reflect new customer-scoping behavior in self-serve operations.
This commit is contained in:
Jeppe Bundgaard
2026-04-28 17:59:33 +02:00
parent 4dd00cd7a2
commit 690e114d38
11 changed files with 325 additions and 37 deletions
@@ -66,9 +66,9 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
$this->conditionEvaluator ??= new selfserve_condition_evaluator();
}
public function previewVehicleEligibility(int $laneId, string $reg, ?int $customerNumber = null, ?int $vehicleTypeIdOverride = null): array
public function previewVehicleEligibility(int $laneId, string $reg, ?int $customerNumber = null, ?int $vehicleTypeIdOverride = null, array $options = []): array
{
$snapshot = $this->buildEligibilitySnapshot($laneId, $reg, $customerNumber, $vehicleTypeIdOverride);
$snapshot = $this->buildEligibilitySnapshot($laneId, $reg, $customerNumber, $vehicleTypeIdOverride, $options);
$session = $this->findLatestOpenSession($laneId, $snapshot['reg'], $snapshot['customer_number']);
return $this->formatSnapshotResponse($snapshot, $session->exists() ? $session->asArray() : null);
@@ -93,9 +93,9 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
return $response;
}
public function synchronizeSession(int $laneId, string $reg, ?int $customerNumber = null, bool $activateMachine = true, ?int $vehicleTypeIdOverride = null, bool $syncRelayState = true): array
public function synchronizeSession(int $laneId, string $reg, ?int $customerNumber = null, bool $activateMachine = true, ?int $vehicleTypeIdOverride = null, bool $syncRelayState = true, array $options = []): array
{
$snapshot = $this->buildEligibilitySnapshot($laneId, $reg, $customerNumber, $vehicleTypeIdOverride);
$snapshot = $this->buildEligibilitySnapshot($laneId, $reg, $customerNumber, $vehicleTypeIdOverride, $options);
$session = $this->findLatestOpenSession($laneId, $snapshot['reg'], $snapshot['customer_number']);
if (!$session->exists()) {
@@ -414,11 +414,12 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
$vehicleData = $vehicle?->asArray();
$vehicleTypeId = $this->resolveVehicleTypeId($vehicle, $vehicleTypeIdOverride);
$resolvedCustomerNumber = $customerNumber ?? ($vehicle !== null ? (int)$vehicle->customer_id->value() : null);
$persistedAnswerCustomerNumber = $this->resolvePersistedAnswerCustomerNumber($resolvedCustomerNumber);
$questions = $this->loadQuestions($departmentId, $laneId, $vehicleTypeId, $publishedConfigPayload);
$conditions = $this->loadConditions($departmentId, $laneId, $vehicleTypeId, $machineTypeId, $publishedConfigPayload);
$rules = $this->loadConditionRules($conditions, $publishedConfigPayload);
$persistedAnswers = (new department_selfserve_vehicle_conditions_o())->getAnswerMapForVehicle($departmentId, $laneId, $normalizedReg);
$persistedAnswers = $this->loadPersistedAnswers($departmentId, $laneId, $normalizedReg, $persistedAnswerCustomerNumber);
$answerOverrides = $this->normalizeAnswerOverrides($options['answer_overrides'] ?? []);
$answers = $this->applyAnswerOverrides($persistedAnswers, $answerOverrides);
$answerSources = $this->buildAnswerSources($persistedAnswers, $answerOverrides);
@@ -543,6 +544,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
'vehicle_type_id' => $vehicleTypeId,
'answers' => $answers,
'persisted_answers' => $persistedAnswers,
'persisted_answer_customer_number' => $persistedAnswerCustomerNumber,
'answer_overrides' => $answerOverrides,
'answer_sources' => $answerSources,
'questions' => $visibleQuestions,
@@ -767,6 +769,32 @@ class selfserve_wash_flow implements selfserve_wash_flow_i
return $sources;
}
protected function resolvePersistedAnswerCustomerNumber(?int $resolvedCustomerNumber): ?int
{
if ($resolvedCustomerNumber === null || $resolvedCustomerNumber <= 0) {
return null;
}
return $resolvedCustomerNumber;
}
/**
* @return array<int,bool>
*/
protected function loadPersistedAnswers(int $departmentId, int $laneId, string $reg, ?int $customerNumber): array
{
if ($customerNumber === null || $customerNumber <= 0) {
return [];
}
return (new department_selfserve_vehicle_conditions_o())->getAnswerMapForVehicle(
$departmentId,
$laneId,
$reg,
$customerNumber
);
}
/**
* @param array<string,mixed> $snapshot
* @param array<int,array<string,mixed>> $questions