Task-Typnummern neu zugeordnet und benutzerdefinierte Typen via effectiveType unterstuetzt
- TourTask: base_type-Feld und effectiveType-Getter ergaenzt; benutzerdefinierte Typen (>= 1000) aus dem Workflow-Editor werden auf ihren Basis-Typ abgebildet, bei Altdaten ohne base_type wird der Basis-Typ aus den Eigenschaften abgeleitet - TaskListPage: alle Typ-Switches auf effectiveType und die neuen Typnummern umgestellt (u.a. Ankunft 1->3, Fotos 3->4, Bestaetigung 6->12, Listen 12-14->13-15) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa44311776
commit
ac4266079a
2 files changed
+88
-54
No files matched your search
@@ -313,19 +313,19 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
|
||||
Widget _buildEditableBody(TaskInfo taskInfo) {
|
||||
final draft = _draftFor(taskInfo);
|
||||
final type = taskInfo.task?.type ?? 0;
|
||||
// Benutzerdefinierte Typen aus dem Workflow-Editor (>= 1000) verhalten
|
||||
// sich wie ihr Basis-Typ, siehe TourTask.effectiveType.
|
||||
final type = taskInfo.task?.effectiveType ?? 0;
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
return _buildConfirmButtonBody(draft, 'confirm_arrival'.tr);
|
||||
case 2:
|
||||
return _buildSignatureBody(taskInfo, draft);
|
||||
case 3:
|
||||
return _buildConfirmButtonBody(draft, 'confirm_arrival'.tr);
|
||||
case 4:
|
||||
return _buildPhotosBody(taskInfo, draft);
|
||||
case 5:
|
||||
return Text('barcode_disabled'.tr, style: const TextStyle(color: Colors.grey));
|
||||
case 6:
|
||||
return _buildConfirmBody(draft, taskInfo.task?.topic ?? 'confirm'.tr);
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
@@ -333,17 +333,19 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
case 11:
|
||||
return _buildStatusBody(taskInfo, draft);
|
||||
case 12:
|
||||
return _buildFieldsBody(taskInfo, draft, numeric: false);
|
||||
return _buildConfirmBody(draft, taskInfo.task?.topic ?? 'confirm'.tr);
|
||||
case 13:
|
||||
return _buildFieldsBody(taskInfo, draft, numeric: true);
|
||||
return _buildFieldsBody(taskInfo, draft, numeric: false);
|
||||
case 14:
|
||||
return _buildFieldsBody(taskInfo, draft, numeric: true);
|
||||
case 15:
|
||||
return _buildCheckboxListBody(taskInfo, draft);
|
||||
default:
|
||||
return Text('task_type_unsupported'.tr, style: const TextStyle(color: Colors.grey));
|
||||
}
|
||||
}
|
||||
|
||||
/// Bestätigung per Button (Ankunft melden = 1). Antippen bestätigt bzw.
|
||||
/// Bestätigung per Button (Ankunft melden = 3). Antippen bestätigt bzw.
|
||||
/// nimmt die Bestätigung wieder zurück; der Zustand wird über ein
|
||||
/// Häkchen-Symbol im Button angezeigt.
|
||||
Widget _buildConfirmButtonBody(_TaskDraft draft, String label) {
|
||||
@@ -375,7 +377,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Bestätigungs-Aufgabe als Checkbox (Typ 6).
|
||||
/// Bestätigungs-Aufgabe als Checkbox (Typ 12).
|
||||
Widget _buildConfirmBody(_TaskDraft draft, String label) {
|
||||
return CheckboxListTile(
|
||||
value: draft.confirmed,
|
||||
@@ -440,7 +442,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Text-/Zahlen-Liste (Typ 12/13): je Feld ein beschriftetes Eingabefeld.
|
||||
/// Text-/Zahlen-Liste (Typ 13/14): je Feld ein beschriftetes Eingabefeld.
|
||||
Widget _buildFieldsBody(TaskInfo taskInfo, _TaskDraft draft, {required bool numeric}) {
|
||||
final fields = taskInfo.task?.fields ?? [];
|
||||
if (fields.isEmpty) {
|
||||
@@ -480,7 +482,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Checkbox-Liste (Typ 14): Mehrfachauswahl aus den entries.
|
||||
/// Checkbox-Liste (Typ 15): Mehrfachauswahl aus den entries.
|
||||
Widget _buildCheckboxListBody(TaskInfo taskInfo, _TaskDraft draft) {
|
||||
final entries = taskInfo.task?.entries ?? [];
|
||||
if (entries.isEmpty) {
|
||||
@@ -554,7 +556,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Fotos aufnehmen (Typ 3) – Inline-Aufnahme und Vorschau.
|
||||
/// Fotos aufnehmen (Typ 4) – Inline-Aufnahme und Vorschau.
|
||||
Widget _buildPhotosBody(TaskInfo taskInfo, _TaskDraft draft) {
|
||||
final max = taskInfo.task?.max ?? 999;
|
||||
final min = taskInfo.task?.min ?? 0;
|
||||
@@ -641,14 +643,14 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
final task = taskInfo.task;
|
||||
if (task == null) return true;
|
||||
if (task.opt ?? false) return true;
|
||||
switch (task.type) {
|
||||
case 12:
|
||||
switch (task.effectiveType) {
|
||||
case 13:
|
||||
case 14:
|
||||
final fields = task.fields ?? [];
|
||||
if (fields.isEmpty) return true;
|
||||
if (fields.every((f) => f.required == null)) return false;
|
||||
return fields.every((f) => f.required != true);
|
||||
case 14:
|
||||
case 15:
|
||||
final entries = task.entries ?? [];
|
||||
if (entries.isEmpty) return true;
|
||||
if (entries.every((e) => e.required == null)) return false;
|
||||
@@ -660,15 +662,15 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
|
||||
bool _isValid(TaskInfo taskInfo) {
|
||||
final draft = _draftFor(taskInfo);
|
||||
final type = taskInfo.task?.type ?? 0;
|
||||
final type = taskInfo.task?.effectiveType ?? 0;
|
||||
switch (type) {
|
||||
case 1:
|
||||
case 6:
|
||||
return draft.confirmed;
|
||||
case 2:
|
||||
final sig = draft.signatureController;
|
||||
return sig != null && sig.points.length > 50;
|
||||
case 3:
|
||||
case 12:
|
||||
return draft.confirmed;
|
||||
case 4:
|
||||
final min = (taskInfo.task?.min ?? 0);
|
||||
final max = (taskInfo.task?.max ?? 999);
|
||||
final required = min > 0 ? min : 1;
|
||||
@@ -687,13 +689,13 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
return (draft.remark?.text.trim().isNotEmpty ?? false);
|
||||
}
|
||||
return true;
|
||||
case 12:
|
||||
_ensureFieldControllers(draft, taskInfo.task?.fields?.length ?? 0);
|
||||
return _fieldsValid(taskInfo, draft, numeric: false);
|
||||
case 13:
|
||||
_ensureFieldControllers(draft, taskInfo.task?.fields?.length ?? 0);
|
||||
return _fieldsValid(taskInfo, draft, numeric: true);
|
||||
return _fieldsValid(taskInfo, draft, numeric: false);
|
||||
case 14:
|
||||
_ensureFieldControllers(draft, taskInfo.task?.fields?.length ?? 0);
|
||||
return _fieldsValid(taskInfo, draft, numeric: true);
|
||||
case 15:
|
||||
final entries = taskInfo.task?.entries ?? [];
|
||||
// Ohne definierte Einträge gibt es keine Pflichtfelder -> freigeben.
|
||||
if (entries.isEmpty) return true;
|
||||
@@ -709,7 +711,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prüft die Eingabefelder einer Text-/Zahlen-Liste (Typ 12/13) gegen ihre
|
||||
/// Prüft die Eingabefelder einer Text-/Zahlen-Liste (Typ 13/14) gegen ihre
|
||||
/// Pflicht-Kennzeichen. Altdaten ohne Kennzeichen behalten das bisherige
|
||||
/// Verhalten (alle Felder müssen gefüllt bzw. numerisch sein). Sonst müssen
|
||||
/// nur Pflichtfelder gefüllt (bei Zahlenlisten numerisch) sein; optionale
|
||||
@@ -736,15 +738,15 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
/// entscheidet, ob eine Antwort geschrieben oder die Aufgabe übersprungen wird).
|
||||
bool _hasData(TaskInfo taskInfo) {
|
||||
final draft = _draftFor(taskInfo);
|
||||
final type = taskInfo.task?.type ?? 0;
|
||||
final type = taskInfo.task?.effectiveType ?? 0;
|
||||
switch (type) {
|
||||
case 1:
|
||||
case 6:
|
||||
return draft.confirmed;
|
||||
case 2:
|
||||
final sig = draft.signatureController;
|
||||
return sig != null && sig.points.length > 50;
|
||||
case 3:
|
||||
case 12:
|
||||
return draft.confirmed;
|
||||
case 4:
|
||||
return draft.photos.isNotEmpty;
|
||||
case 7:
|
||||
case 8:
|
||||
@@ -752,10 +754,10 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
return (draft.text?.text.trim().isNotEmpty ?? false);
|
||||
case 11:
|
||||
return draft.selectedCode != null;
|
||||
case 12:
|
||||
case 13:
|
||||
return draft.fields.any((c) => c.text.trim().isNotEmpty);
|
||||
case 14:
|
||||
return draft.fields.any((c) => c.text.trim().isNotEmpty);
|
||||
case 15:
|
||||
return draft.checked.isNotEmpty;
|
||||
default:
|
||||
return false;
|
||||
@@ -800,7 +802,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
|
||||
Future<void> _commitCurrent(TaskInfo taskInfo) async {
|
||||
final draft = _draftFor(taskInfo);
|
||||
final type = taskInfo.task?.type ?? 0;
|
||||
final type = taskInfo.task?.effectiveType ?? 0;
|
||||
|
||||
// Optionale Aufgabe ohne Eingabe: als erledigt markieren, keine Antwort.
|
||||
if (!_hasData(taskInfo)) {
|
||||
@@ -812,15 +814,6 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 1: // Ankunft melden
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
taskInfo.answer = jsonEncode(answer.toJson());
|
||||
taskInfo.additionalText = DateFormat("dd.MM.yyyy HH:mm").format(DateTime.now());
|
||||
taskInfo.completed = true;
|
||||
_persist(taskInfo);
|
||||
_sendArrivedMessage(taskInfo);
|
||||
break;
|
||||
|
||||
case 2: // Unterschrift
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
answer.sign = _convertSignaturePoints(draft.signatureController!.points);
|
||||
@@ -830,7 +823,16 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
_persist(taskInfo);
|
||||
break;
|
||||
|
||||
case 3: // Fotos
|
||||
case 3: // Ankunft melden
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
taskInfo.answer = jsonEncode(answer.toJson());
|
||||
taskInfo.additionalText = DateFormat("dd.MM.yyyy HH:mm").format(DateTime.now());
|
||||
taskInfo.completed = true;
|
||||
_persist(taskInfo);
|
||||
_sendArrivedMessage(taskInfo);
|
||||
break;
|
||||
|
||||
case 4: // Fotos
|
||||
_storePhotos(draft.photos);
|
||||
taskInfo.answer = null; // Bilder werden separat als pic-Nachrichten gesendet.
|
||||
taskInfo.additionalText = 'photos_taken'.tr;
|
||||
@@ -838,7 +840,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
_persist(taskInfo);
|
||||
break;
|
||||
|
||||
case 6: // Checkbox / Bestätigung
|
||||
case 12: // Checkbox / Bestätigung
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
answer.val = "1";
|
||||
taskInfo.answer = jsonEncode(answer.toJson());
|
||||
@@ -871,8 +873,8 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
_persist(taskInfo);
|
||||
break;
|
||||
|
||||
case 12: // Text-Liste
|
||||
case 13: // Zahlen-Liste
|
||||
case 13: // Text-Liste
|
||||
case 14: // Zahlen-Liste
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
final fields = taskInfo.task?.fields ?? [];
|
||||
final answers = <FinFieldAnswer>[];
|
||||
@@ -891,7 +893,7 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
_persist(taskInfo);
|
||||
break;
|
||||
|
||||
case 14: // Checkbox-Liste
|
||||
case 15: // Checkbox-Liste
|
||||
final answer = await _createAnswerHeader(taskInfo);
|
||||
final checked = draft.checked.toList()..sort();
|
||||
answer.checked = checked;
|
||||
@@ -943,9 +945,9 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
/// erneut bearbeitbaren Aufgaben aus der gespeicherten Antwort vor.
|
||||
_TaskDraft _buildDraft(TaskInfo taskInfo) {
|
||||
final draft = _TaskDraft();
|
||||
final type = taskInfo.task?.type ?? 0;
|
||||
final type = taskInfo.task?.effectiveType ?? 0;
|
||||
|
||||
if (type == 12 || type == 13) {
|
||||
if (type == 13 || type == 14) {
|
||||
_ensureFieldControllers(draft, taskInfo.task?.fields?.length ?? 0);
|
||||
}
|
||||
|
||||
@@ -957,8 +959,8 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
try {
|
||||
final answer = FinTourTask.fromJson(jsonDecode(taskInfo.answer!) as Map<String, dynamic>);
|
||||
switch (type) {
|
||||
case 1:
|
||||
case 6:
|
||||
case 3:
|
||||
case 12:
|
||||
draft.confirmed = true;
|
||||
break;
|
||||
case 7:
|
||||
@@ -970,14 +972,14 @@ class _TaskListPageState extends State<TaskListPage> with ProducerMixin {
|
||||
draft.selectedCode = answer.code;
|
||||
draft.remark = _listenedController(answer.val ?? '');
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
final values = answer.fields ?? [];
|
||||
for (int i = 0; i < draft.fields.length && i < values.length; i++) {
|
||||
draft.fields[i].text = values[i].val ?? '';
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
draft.checked = (answer.checked ?? []).toSet();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
class TourTask {
|
||||
/// Untergrenze der im Workflow-Editor vergebenen Typnummern für
|
||||
/// benutzerdefinierte Typen.
|
||||
static const int customTypeStart = 1000;
|
||||
|
||||
late int sId;
|
||||
late int type;
|
||||
|
||||
/// Typnummer des Basis-Typs bei benutzerdefinierten Typen
|
||||
/// (>= [customTypeStart]) aus dem Workflow-Editor. `null` bei
|
||||
/// Standard-Typen und bei Altdaten ohne Angabe.
|
||||
int? baseType;
|
||||
|
||||
late String topic;
|
||||
bool? opt;
|
||||
bool? reenter;
|
||||
@@ -17,15 +27,36 @@ class TourTask {
|
||||
/// Auswahloptionen eines Status-Tasks (Typ 11).
|
||||
List<TourChoice>? choices;
|
||||
|
||||
/// Eingabefelder einer Text-/Zahlen-Liste (Typ 12/13).
|
||||
/// Eingabefelder einer Text-/Zahlen-Liste (Typ 13/14).
|
||||
List<TourField>? fields;
|
||||
|
||||
TourTask();
|
||||
|
||||
/// Der für Darstellung und Validierung maßgebliche Typ: Standard-Typen
|
||||
/// unverändert; benutzerdefinierte Typen (>= [customTypeStart]) werden auf
|
||||
/// ihren Basis-Typ abgebildet. Fehlt {@code base_type} (Altdaten), wird der
|
||||
/// Basis-Typ aus den typprägenden Eigenschaften abgeleitet.
|
||||
int get effectiveType {
|
||||
if (type < customTypeStart) {
|
||||
return type;
|
||||
}
|
||||
if (baseType != null) {
|
||||
return baseType!;
|
||||
}
|
||||
if (choices != null && choices!.isNotEmpty) return 11; // Statusauswahl
|
||||
if (entries != null && entries!.isNotEmpty) return 15; // Checkbox-Liste
|
||||
if (fields != null && fields!.isNotEmpty) return 13; // Text-Liste
|
||||
if (min != null || max != null) return 4; // Fotos
|
||||
if (maxLen != null) return 8; // Texteingabe (Bemerkung)
|
||||
if (track != null) return 3; // Ankunft melden
|
||||
return 12; // Bestätigung per Checkbox
|
||||
}
|
||||
|
||||
factory TourTask.fromJson(Map<String, dynamic> json) {
|
||||
final task = TourTask();
|
||||
task.sId = json['s_id'] as int;
|
||||
task.type = json['type'] as int;
|
||||
task.baseType = json['base_type'] as int?;
|
||||
task.topic = json['topic'] as String;
|
||||
task.opt = json['opt'] as bool? ?? false;
|
||||
task.reenter = json['reenter'] as bool? ?? false;
|
||||
@@ -59,6 +90,7 @@ class TourTask {
|
||||
return {
|
||||
's_id': sId,
|
||||
'type': type,
|
||||
if (baseType != null) 'base_type': baseType,
|
||||
'topic': topic,
|
||||
'opt': opt,
|
||||
'reenter': reenter,
|
||||
@@ -82,7 +114,7 @@ class TourEntry {
|
||||
late String text;
|
||||
late bool queryRemark;
|
||||
|
||||
/// Pflicht-Kennzeichen aus dem Workflow-Editor (Checkbox-Liste, Typ 14).
|
||||
/// Pflicht-Kennzeichen aus dem Workflow-Editor (Checkbox-Liste, Typ 15).
|
||||
/// `null`, wenn das JSON keine Angabe enthält (Altdaten).
|
||||
bool? required;
|
||||
|
||||
@@ -145,7 +177,7 @@ class TourChoice {
|
||||
bool get requiresRemark => (quest != null && quest!.trim().isNotEmpty);
|
||||
}
|
||||
|
||||
/// Ein Eingabefeld einer Text-/Zahlen-Liste (Typ 12/13).
|
||||
/// Ein Eingabefeld einer Text-/Zahlen-Liste (Typ 13/14).
|
||||
class TourField {
|
||||
String? title;
|
||||
String? placeholder;
|
||||
|
||||
Reference in new issue
Block a user