diff --git a/openapi.yaml b/openapi.yaml index 4c57ea96..6970b740 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -11816,9 +11816,11 @@ components: type: string answer: type: boolean + nullable: true answered_at: type: string format: date-time + nullable: true SelfserveWashTaskSnapshot: type: object 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 f05d6054..e3bbbb87 100644 --- a/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php +++ b/services/nginx/app/modules/selfserve/classes/selfserve_wash_flow.php @@ -167,14 +167,8 @@ class selfserve_wash_flow implements selfserve_wash_flow_i } } - $answers = array_map(function (array $row): array { - return [ - 'question_id' => (int)$row['question_id'], - 'question' => (string)$row['question_text'], - 'answer' => (bool)$row['answer_value'], - 'answered_at' => (string)$row['answered_at'], - ]; - }, (new selfserve_wash_session_answers_o())->listBySession($sessionId)); + $answerRows = (new selfserve_wash_session_answers_o())->listBySession($sessionId); + $answers = $this->buildSessionQuestions($session, $answerRows); $tasks = array_map(function (array $row): array { return [ @@ -255,15 +249,17 @@ class selfserve_wash_flow implements selfserve_wash_flow_i $conditions = $this->loadConditions($departmentId, $laneId, $vehicleTypeId, $machineTypeId); $rules = $this->loadConditionRules($conditions); $answers = (new department_selfserve_vehicle_conditions_o())->getAnswerMapForVehicle($departmentId, $laneId, $normalizedReg); - $conditionResults = $this->conditionEvaluator->evaluate($conditions, $rules, $answers); + $visibilityConditionResults = $this->conditionEvaluator->evaluate($conditions, $rules, $answers); $visibleQuestions = []; + $visibleQuestionIds = []; foreach ($questions as $question) { $gateId = $this->nullableInt($question['condition_id'] ?? null); - if ($gateId !== null && (($conditionResults[$gateId] ?? false) !== true)) { + if ($gateId !== null && (($visibilityConditionResults[$gateId] ?? false) !== true)) { continue; } $questionId = (int)$question['id']; + $visibleQuestionIds[] = $questionId; $visibleQuestions[] = [ 'id' => $questionId, 'question' => (string)$question['question'], @@ -275,12 +271,14 @@ class selfserve_wash_flow implements selfserve_wash_flow_i } usort($visibleQuestions, static fn(array $a, array $b): int => $a['order_priority'] <=> $b['order_priority']); + $visibleAnswers = $this->filterAnswersToVisibleQuestions($answers, $visibleQuestionIds); + $serviceConditionResults = $this->conditionEvaluator->evaluate($conditions, $rules, $visibleAnswers); $tasks = $this->loadTasks($departmentId, $laneId, $vehicleTypeId, $machineTypeId); $activeTasks = []; foreach ($tasks as $task) { $gateId = $this->nullableInt($task['condition_id'] ?? null); - if (!$this->conditionEvaluator->taskGateSatisfied($gateId, $conditionResults, $answers)) { + if (!$this->conditionEvaluator->taskGateSatisfied($gateId, $serviceConditionResults, $visibleAnswers)) { continue; } @@ -335,7 +333,7 @@ class selfserve_wash_flow implements selfserve_wash_flow_i 'vehicle_type_id' => $vehicleTypeId, 'answers' => $answers, 'questions' => $visibleQuestions, - 'conditions' => $conditionResults, + 'conditions' => $serviceConditionResults, 'tasks' => $activeTasks, 'allowed_services' => $allowedServices, 'machine_available' => $machineAvailable, @@ -515,10 +513,133 @@ class selfserve_wash_flow implements selfserve_wash_flow_i 'machine_available' => (bool)$snapshot['machine_available'], 'all_visible_questions_answered' => (bool)$snapshot['all_visible_questions_answered'], 'visible_question_ids' => array_map(static fn(array $question): int => (int)$question['id'], $snapshot['questions']), + 'visible_questions' => array_map(static fn(array $question): array => [ + 'id' => (int)$question['id'], + 'question' => (string)$question['question'], + 'order_priority' => (int)($question['order_priority'] ?? 0), + ], $snapshot['questions']), 'task_ids' => array_map(static fn(array $task): int => (int)$task['id'], $snapshot['tasks']), ]; } + /** + * @param array> $answerRows + * @return array> + */ + protected function buildSessionQuestions(selfserve_wash_sessions_o $session, array $answerRows): array + { + $answersByQuestionId = []; + foreach ($answerRows as $row) { + $answersByQuestionId[(int)$row['question_id']] = $row; + } + + $metadata = $session->metadata_json->value(); + $metadata = is_array($metadata) ? $metadata : []; + $visibleQuestions = $this->resolveVisibleQuestionsFromMetadata( + $metadata, + (int)$session->department_id->value(), + (int)$session->lane_id->value(), + $session->vehicle_type_id->value() === null ? null : (int)$session->vehicle_type_id->value(), + ); + + if ($visibleQuestions === []) { + return array_map(static function (array $row): array { + return [ + 'question_id' => (int)$row['question_id'], + 'question' => (string)$row['question_text'], + 'answer' => (bool)$row['answer_value'], + 'answered_at' => (string)$row['answered_at'], + ]; + }, $answerRows); + } + + $questions = []; + foreach ($visibleQuestions as $visibleQuestion) { + $questionId = (int)$visibleQuestion['id']; + if ($questionId <= 0) { + continue; + } + + $answerRow = $answersByQuestionId[$questionId] ?? null; + $questions[] = [ + 'question_id' => $questionId, + 'question' => $answerRow === null ? (string)$visibleQuestion['question'] : (string)$answerRow['question_text'], + 'answer' => $answerRow === null ? null : (bool)$answerRow['answer_value'], + 'answered_at' => $answerRow === null ? null : (string)$answerRow['answered_at'], + ]; + unset($answersByQuestionId[$questionId]); + } + + foreach ($answerRows as $row) { + $questionId = (int)$row['question_id']; + if (!array_key_exists($questionId, $answersByQuestionId)) { + continue; + } + + $questions[] = [ + 'question_id' => $questionId, + 'question' => (string)$row['question_text'], + 'answer' => (bool)$row['answer_value'], + 'answered_at' => (string)$row['answered_at'], + ]; + unset($answersByQuestionId[$questionId]); + } + + return $questions; + } + + /** + * @param array $metadata + * @return array + */ + protected function resolveVisibleQuestionsFromMetadata(array $metadata, int $departmentId, int $laneId, ?int $vehicleTypeId): array + { + $visibleQuestions = []; + if (isset($metadata['visible_questions']) && is_array($metadata['visible_questions'])) { + foreach ($metadata['visible_questions'] as $visibleQuestion) { + if (!is_array($visibleQuestion)) { + continue; + } + $questionId = (int)($visibleQuestion['id'] ?? 0); + if ($questionId <= 0) { + continue; + } + $visibleQuestions[] = [ + 'id' => $questionId, + 'question' => (string)($visibleQuestion['question'] ?? ''), + 'order_priority' => (int)($visibleQuestion['order_priority'] ?? 0), + ]; + } + return $visibleQuestions; + } + + if (!isset($metadata['visible_question_ids']) || !is_array($metadata['visible_question_ids'])) { + return []; + } + + $questionById = []; + foreach ($this->loadQuestions($departmentId, $laneId, $vehicleTypeId) as $question) { + $questionById[(int)$question['id']] = [ + 'question' => (string)($question['question'] ?? ''), + 'order_priority' => (int)($question['order_priority'] ?? 0), + ]; + } + + foreach ($metadata['visible_question_ids'] as $visibleQuestionId) { + $questionId = (int)$visibleQuestionId; + if ($questionId <= 0) { + continue; + } + $visibleQuestions[] = [ + 'id' => $questionId, + 'question' => (string)($questionById[$questionId]['question'] ?? ''), + 'order_priority' => (int)($questionById[$questionId]['order_priority'] ?? 0), + ]; + } + + return $visibleQuestions; + } + protected function findVehicleByRegistration(string $reg): ?customer_vehicles_o { $vehicle = (new customer_vehicles_o())->selectByPlate($reg); @@ -560,6 +681,24 @@ class selfserve_wash_flow implements selfserve_wash_flow_i return (int)$value; } + /** + * @param array $answers + * @param array $visibleQuestionIds + * @return array + */ + protected function filterAnswersToVisibleQuestions(array $answers, array $visibleQuestionIds): array + { + $filtered = []; + foreach ($visibleQuestionIds as $questionId) { + $questionId = (int)$questionId; + if ($questionId <= 0 || !array_key_exists($questionId, $answers)) { + continue; + } + $filtered[$questionId] = $answers[$questionId]; + } + return $filtered; + } + protected function normalizeJsonValue(mixed $value): mixed { if ($value === null || $value === '') {