diff --git a/common.php b/common.php index 75916dd..aa396b8 100644 --- a/common.php +++ b/common.php @@ -1562,9 +1562,23 @@ function qdb_glass_symptoms_with_labels(PDO $pdo, array $config): array { return $rows; } -/** Sync note German text into string_translation rows for the app. */ -function qdb_sync_question_note_strings(PDO $pdo, string $questionKey, array $config): void { +/** Sync note German text into string_translation rows for the app. Removes rows when notes are cleared. */ +function qdb_sync_question_note_strings( + PDO $pdo, + string $questionKey, + array $config, + ?string $previousQuestionKey = null +): void { + $keysToDelete = []; + if ($previousQuestionKey !== null) { + $prev = trim($previousQuestionKey); + if ($prev !== '' && $prev !== $questionKey) { + $keysToDelete[] = qdb_note_before_key($prev); + $keysToDelete[] = qdb_note_after_key($prev); + } + } if ($questionKey === '') { + qdb_delete_string_translation_keys($pdo, $keysToDelete); return; } foreach ([ @@ -1574,8 +1588,29 @@ function qdb_sync_question_note_strings(PDO $pdo, string $questionKey, array $co $text = trim((string)($config[$field] ?? '')); if ($text !== '') { qdb_put_translation($pdo, 'string', $stringKey, QDB_SOURCE_LANGUAGE, $text); + } else { + $keysToDelete[] = $stringKey; } } + qdb_delete_string_translation_keys($pdo, $keysToDelete); +} + +/** @param list $stringKeys */ +function qdb_delete_string_translation_keys(PDO $pdo, array $stringKeys): void { + $keys = []; + foreach ($stringKeys as $key) { + $key = trim((string)$key); + if ($key !== '') { + $keys[$key] = true; + } + } + if ($keys === []) { + return; + } + $list = array_keys($keys); + $ph = implode(',', array_fill(0, count($list), '?')); + $stmt = $pdo->prepare("DELETE FROM string_translation WHERE stringKey IN ($ph)"); + $stmt->execute($list); } function qdb_assert_unique_question_key(PDO $pdo, string $qnID, string $questionKey, ?string $excludeQuestionID = null): void { diff --git a/handlers/questions.php b/handlers/questions.php index 5ada392..30ecca1 100644 --- a/handlers/questions.php +++ b/handlers/questions.php @@ -215,7 +215,7 @@ case 'PUT': $pdo->prepare("UPDATE question SET defaultText = :t, type = :ty, orderIndex = :o, isRequired = :r, configJson = :cj WHERE questionID = :id") ->execute([':t' => $text, ':ty' => $type, ':o' => $order, ':r' => $req, ':cj' => $configJson, ':id' => $id]); qdb_upsert_source_translation($pdo, 'question', $id, $text); - qdb_sync_question_note_strings($pdo, $questionKey, $config); + qdb_sync_question_note_strings($pdo, $questionKey, $config, $oldKey); if ($type === 'glass_scale_question') { qdb_sync_glass_symptom_strings($pdo, $glassSymptoms); } diff --git a/tests/Integration/QuestionnaireEditingTest.php b/tests/Integration/QuestionnaireEditingTest.php index fb6c3bf..46cd057 100644 --- a/tests/Integration/QuestionnaireEditingTest.php +++ b/tests/Integration/QuestionnaireEditingTest.php @@ -107,5 +107,39 @@ final class QuestionnaireEditingTest extends QdbTestCase 'Bitte dem Klienten deutlich machen: Ohne Einwilligung keine Teilnahme.', $de['consent_q_note_before'] ?? null ); + + $clear = $this->api()->withToken($adminToken, 'PUT', 'questions', [ + 'questionID' => $f->questionId, + 'questionKey' => 'consent_q', + 'defaultText' => 'Consent?', + 'type' => 'single_choice_question', + 'isRequired' => 1, + 'configJson' => json_encode(['questionKey' => 'consent_q'], JSON_UNESCAPED_UNICODE), + 'noteBefore' => '', + 'noteAfter' => '', + ]); + $this->assertApiOk($clear); + $this->assertGreaterThan( + $revAfter, + (int)($clear['data']['structureRevision'] ?? 0), + 'clearing notes must bump structureRevision' + ); + + $detailCleared = $this->api()->withMobileToken($coachLogin['token'], 'GET', 'app_questionnaires', null, [ + 'id' => $f->questionnaireId, + ]); + $this->assertApiOk($detailCleared); + $matchedCleared = null; + foreach ($detailCleared['data']['questions'] ?? [] as $q) { + if (($q['question'] ?? '') === 'consent_q' || ($q['id'] ?? '') === 'q1') { + $matchedCleared = $q; + break; + } + } + $this->assertNotNull($matchedCleared); + $this->assertArrayNotHasKey('noteBeforeKey', $matchedCleared); + $this->assertArrayNotHasKey('noteAfterKey', $matchedCleared); + $deCleared = $detailCleared['data']['translations']['de'] ?? []; + $this->assertArrayNotHasKey('consent_q_note_before', $deCleared); } }