enhanced questionnaire version handling and handling uploads from outdated questionnaires through automatic revisioning and checks
Some checks failed
PHPUnit / test (push) Has been cancelled

This commit is contained in:
2026-06-12 21:38:39 +02:00
parent 5dff24a232
commit b4d4de72f4
17 changed files with 1289 additions and 209 deletions

View File

@ -1,5 +1,7 @@
<?php
require_once __DIR__ . '/../lib/questionnaire_structure.php';
$tokenRec = require_valid_token_web();
$method = $_SERVER['REQUEST_METHOD'];
@ -50,12 +52,14 @@ case 'POST':
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
try {
$chk = $pdo->prepare('SELECT 1 FROM question WHERE questionID = :id');
$chk = $pdo->prepare('SELECT questionnaireID FROM question WHERE questionID = :id');
$chk->execute([':id' => $qID]);
if (!$chk->fetch()) {
$qnID = $chk->fetchColumn();
if (!$qnID) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Question not found', 404);
}
$qnID = (string)$qnID;
if ($order === 0) {
$max = $pdo->prepare('SELECT COALESCE(MAX(orderIndex),0)+1 FROM answer_option WHERE questionID = :id');
$max->execute([':id' => $qID]);
@ -65,8 +69,11 @@ case 'POST':
$pdo->prepare('INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) VALUES (:id, :qid, :t, :p, :o, :nq)')
->execute([':id' => $id, ':qid' => $qID, ':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ]);
qdb_upsert_source_translation($pdo, 'answer_option', $id, $text);
$newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_added');
qdb_save($tmpDb, $lockFp);
json_success(['answerOption' => [
json_success([
'structureRevision' => $newRev,
'answerOption' => [
'answerOptionID' => $id,
'questionID' => $qID,
'optionKey' => $optKey,
@ -115,11 +122,19 @@ case 'PUT':
$order = (int)($body['orderIndex'] ?? $row['orderIndex']);
$nextQ = trim($body['nextQuestionId'] ?? $row['nextQuestionId']);
$keyChanged = $optKey !== qdb_option_key($row);
$pdo->prepare('UPDATE answer_option SET defaultText = :t, points = :p, orderIndex = :o, nextQuestionId = :nq WHERE answerOptionID = :id')
->execute([':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ, ':id' => $id]);
qdb_upsert_source_translation($pdo, 'answer_option', $id, $labelGerman);
$qnID = qdb_questionnaire_id_for_question($pdo, (string)$row['questionID']);
$newRev = null;
if ($keyChanged && $qnID !== null) {
$newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_key_changed');
}
qdb_save($tmpDb, $lockFp);
json_success(['answerOption' => [
json_success([
'structureRevision' => $newRev ?? ($qnID !== null ? qdb_questionnaire_structure_revision($pdo, $qnID) : 1),
'answerOption' => [
'answerOptionID' => $id,
'questionID' => $row['questionID'],
'optionKey' => $optKey,
@ -144,15 +159,35 @@ case 'DELETE':
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
try {
$pdo->exec('PRAGMA foreign_keys = OFF;');
$existing = $pdo->prepare('SELECT questionID FROM answer_option WHERE answerOptionID = :id');
$existing->execute([':id' => $id]);
$qid = $existing->fetchColumn();
if (!$qid) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Answer option not found', 404);
}
$qnID = qdb_questionnaire_id_for_question($pdo, (string)$qid);
if ($qnID === null) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Question not found', 404);
}
$pdo->beginTransaction();
$newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_removed');
if (qdb_option_has_client_data($pdo, $id)) {
$pdo->prepare('UPDATE answer_option SET retiredAt = :ts WHERE answerOptionID = :id')
->execute([':ts' => time(), ':id' => $id]);
$pdo->commit();
qdb_save($tmpDb, $lockFp);
json_success(['deleted' => false, 'retired' => true, 'structureRevision' => $newRev]);
break;
}
$pdo->exec('PRAGMA foreign_keys = OFF;');
$pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $id]);
$pdo->prepare('UPDATE client_answer SET answerOptionID = NULL WHERE answerOptionID = :id')->execute([':id' => $id]);
$pdo->prepare('DELETE FROM answer_option WHERE answerOptionID = :id')->execute([':id' => $id]);
$pdo->commit();
$pdo->exec('PRAGMA foreign_keys = ON;');
$pdo->commit();
qdb_save($tmpDb, $lockFp);
json_success(['deleted' => true]);
json_success(['deleted' => true, 'retired' => false, 'structureRevision' => $newRev]);
} catch (Throwable $e) {
qdb_handler_fail($e, 'Delete answer option', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null);
}