feat: Adressbuch mit Kundennummer, Update-Flow und interne Einträge
- Menüpunkt "Kunden" in "Adressbuch" umbenannt und App-Label "Verfügbare Jobs" zu "Auftragsliste" geändert (alle 10 Sprachen) - Fortlaufende Kundennummer (usrId) ab 10000 über neuen SequenceGeneratorService und Counter-Dokument in misc-Collection - Abholung/Lieferstation-Dialog: Änderungen an verknüpften Stammdaten aktualisieren den bestehenden Adressbuch-Eintrag statt einen neuen zu erzeugen; Checkbox-Label wechselt zu "Adresse im Adressbuch aktualisieren" - Geänderte Adressen ohne Checkbox werden als interner Customer (internal=true) gesichert und im Adressbuch ausgeblendet - E-Mail in AddCustomer und in Stations-Dialogen kein Pflichtfeld mehr; "(Login)" aus profile.email entfernt - Manuelles Beenden eines Auftrags öffnet neue Seite JobManualCompleteView statt eines Dialogs
This commit is contained in:
@@ -55,7 +55,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get jobs => 'Jobs';
|
||||
|
||||
@override
|
||||
String get availableJobs => 'Verfügbare Jobs';
|
||||
String get availableJobs => 'Auftragsliste';
|
||||
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
|
||||
@@ -55,7 +55,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get jobs => 'Jobs';
|
||||
|
||||
@override
|
||||
String get availableJobs => 'Available Jobs';
|
||||
String get availableJobs => 'Order List';
|
||||
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Trabajos';
|
||||
@override
|
||||
String get availableJobs => 'Trabajos Disponibles';
|
||||
String get availableJobs => 'Lista de pedidos';
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsEt extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Tööd';
|
||||
@override
|
||||
String get availableJobs => 'Saadaolevad tööd';
|
||||
String get availableJobs => 'Tellimuste loend';
|
||||
@override
|
||||
String get chats => 'Vestlused';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Emplois';
|
||||
@override
|
||||
String get availableJobs => 'Emplois Disponibles';
|
||||
String get availableJobs => 'Liste des commandes';
|
||||
@override
|
||||
String get chats => 'Discussions';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsLt extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Darbai';
|
||||
@override
|
||||
String get availableJobs => 'Galimi darbai';
|
||||
String get availableJobs => 'Užsakymų sąrašas';
|
||||
@override
|
||||
String get chats => 'Pokalbiai';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsLv extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Darbi';
|
||||
@override
|
||||
String get availableJobs => 'Pieejamie darbi';
|
||||
String get availableJobs => 'Pasūtījumu saraksts';
|
||||
@override
|
||||
String get chats => 'Tērzēšanas';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Zadania';
|
||||
@override
|
||||
String get availableJobs => 'Dostępne Zadania';
|
||||
String get availableJobs => 'Lista zleceń';
|
||||
@override
|
||||
String get chats => 'Czaty';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'Задания';
|
||||
@override
|
||||
String get availableJobs => 'Доступные задания';
|
||||
String get availableJobs => 'Список заказов';
|
||||
@override
|
||||
String get chats => 'Чаты';
|
||||
@override
|
||||
|
||||
@@ -42,7 +42,7 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
@override
|
||||
String get jobs => 'İşler';
|
||||
@override
|
||||
String get availableJobs => 'Mevcut İşler';
|
||||
String get availableJobs => 'Sipariş Listesi';
|
||||
@override
|
||||
String get chats => 'Sohbetler';
|
||||
@override
|
||||
|
||||
@@ -2,6 +2,8 @@ import '../task.dart';
|
||||
|
||||
// Signature Task
|
||||
class SignatureTask extends Task {
|
||||
final String? note;
|
||||
|
||||
SignatureTask({
|
||||
required super.id,
|
||||
required super.jobId,
|
||||
@@ -14,11 +16,19 @@ class SignatureTask extends Task {
|
||||
super.title,
|
||||
super.description,
|
||||
super.displayName,
|
||||
this.note,
|
||||
});
|
||||
|
||||
factory SignatureTask.fromJson(Map<String, dynamic> json) {
|
||||
final commonProps = Task.parseCommonProperties(json);
|
||||
|
||||
String? note;
|
||||
final taskSpecificData = json['taskSpecificData'];
|
||||
if (taskSpecificData is Map<String, dynamic>) {
|
||||
final n = taskSpecificData['note'];
|
||||
if (n is String) note = n;
|
||||
}
|
||||
|
||||
return SignatureTask(
|
||||
id: commonProps['id'],
|
||||
jobId: commonProps['jobId'],
|
||||
@@ -31,6 +41,7 @@ class SignatureTask extends Task {
|
||||
title: commonProps['title'],
|
||||
description: commonProps['description'],
|
||||
displayName: commonProps['displayName'],
|
||||
note: note,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +58,11 @@ class SignatureTask extends Task {
|
||||
'taskOrder': taskOrder,
|
||||
'description': description,
|
||||
'displayName': displayName,
|
||||
'taskSpecificData': {'taskType': 'SIGNATURE', 'title': title},
|
||||
'taskSpecificData': {
|
||||
'taskType': 'SIGNATURE',
|
||||
'title': title,
|
||||
'note': note,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,6 +79,7 @@ class SignatureTask extends Task {
|
||||
String? title,
|
||||
String? description,
|
||||
String? displayName,
|
||||
String? note,
|
||||
}) {
|
||||
return SignatureTask(
|
||||
id: id ?? this.id,
|
||||
@@ -77,6 +93,7 @@ class SignatureTask extends Task {
|
||||
title: title ?? this.title,
|
||||
description: description ?? this.description,
|
||||
displayName: displayName ?? this.displayName,
|
||||
note: note ?? this.note,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,6 +831,42 @@ class DatabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Save signature note (Bemerkung) for a task into user_data table
|
||||
Future<void> saveTaskSignatureNote(String taskId, String note) async {
|
||||
try {
|
||||
if (_store == null) {
|
||||
developer.log('Database not initialized', name: 'DatabaseService');
|
||||
return;
|
||||
}
|
||||
final key = 'task_signature_note:$taskId';
|
||||
await saveKeyValue(key, note);
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'Error saving task signature note: $e',
|
||||
name: 'DatabaseService',
|
||||
);
|
||||
developer.log('Stack trace: $st', name: 'DatabaseService');
|
||||
}
|
||||
}
|
||||
|
||||
/// Load signature note (Bemerkung) for a task from user_data table
|
||||
Future<String?> loadTaskSignatureNote(String taskId) async {
|
||||
try {
|
||||
if (_store == null) {
|
||||
developer.log('Database not initialized', name: 'DatabaseService');
|
||||
return null;
|
||||
}
|
||||
return await loadKeyValue('task_signature_note:$taskId');
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'Error loading task signature note: $e',
|
||||
name: 'DatabaseService',
|
||||
);
|
||||
developer.log('Stack trace: $st', name: 'DatabaseService');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Load signature SVG for a task from user_data table
|
||||
Future<String?> loadTaskSignature(String taskId) async {
|
||||
try {
|
||||
|
||||
@@ -622,10 +622,11 @@ class _TaskViewState extends State<TaskView> {
|
||||
builder:
|
||||
(context) => SignatureCaptureScreen(
|
||||
task: task,
|
||||
onSignatureCompleted: (String svg) async {
|
||||
onSignatureCompleted: (String svg, String note) async {
|
||||
try {
|
||||
// Persist SVG only (no PNG)
|
||||
await _databaseService.saveTaskSignature(task.id, svg);
|
||||
await _databaseService.saveTaskSignatureNote(task.id, note);
|
||||
} catch (e, stackTrace) {
|
||||
developer.log(
|
||||
'Error saving task signature: $e',
|
||||
@@ -649,6 +650,7 @@ class _TaskViewState extends State<TaskView> {
|
||||
'signatureSvg': svg,
|
||||
'svgLength': svg.length,
|
||||
'hasSignature': true,
|
||||
'signatureNote': note,
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -896,6 +898,10 @@ class _TaskViewState extends State<TaskView> {
|
||||
task.description != null
|
||||
? localizeKnownText(context, task.description!)
|
||||
: null;
|
||||
final String? signatureNote =
|
||||
(task is SignatureTask && task.note != null && task.note!.trim().isNotEmpty)
|
||||
? task.note!.trim()
|
||||
: null;
|
||||
|
||||
if (displayName?.isNotEmpty == true) {
|
||||
return Column(
|
||||
@@ -906,14 +912,39 @@ class _TaskViewState extends State<TaskView> {
|
||||
const SizedBox(height: 2),
|
||||
Text(description!, style: subtitleStyle),
|
||||
],
|
||||
if (signatureNote != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(signatureNote, style: subtitleStyle),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
if (description?.isNotEmpty == true) {
|
||||
if (signatureNote != null) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(description!, style: titleStyle),
|
||||
const SizedBox(height: 2),
|
||||
Text(signatureNote, style: subtitleStyle),
|
||||
],
|
||||
);
|
||||
}
|
||||
return Text(description!, style: titleStyle);
|
||||
}
|
||||
|
||||
if (signatureNote != null) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(_getStandardTaskDisplayText(task), style: titleStyle),
|
||||
const SizedBox(height: 2),
|
||||
Text(signatureNote, style: subtitleStyle),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Fall back to standard text based on task type
|
||||
return Text(_getStandardTaskDisplayText(task), style: titleStyle);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import '../widgets/offline_banner.dart';
|
||||
|
||||
class SignatureCaptureScreen extends StatefulWidget {
|
||||
final SignatureTask task;
|
||||
final void Function(String svg) onSignatureCompleted;
|
||||
final void Function(String svg, String note) onSignatureCompleted;
|
||||
|
||||
const SignatureCaptureScreen({
|
||||
super.key,
|
||||
@@ -23,6 +23,7 @@ class SignatureCaptureScreen extends StatefulWidget {
|
||||
|
||||
class _SignatureCaptureScreenState extends State<SignatureCaptureScreen> {
|
||||
late final SignatureController _controller;
|
||||
final TextEditingController _noteController = TextEditingController();
|
||||
bool _hasSignature = false;
|
||||
bool _isMobilePlatform = false;
|
||||
|
||||
@@ -84,6 +85,7 @@ class _SignatureCaptureScreenState extends State<SignatureCaptureScreen> {
|
||||
void dispose() {
|
||||
_controller.removeListener(_onSignatureChanged);
|
||||
_controller.dispose();
|
||||
_noteController.dispose();
|
||||
_restoreOrientation();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -155,14 +157,15 @@ class _SignatureCaptureScreenState extends State<SignatureCaptureScreen> {
|
||||
|
||||
// Build SVG from the captured signature points
|
||||
final String svg = _buildSvgFromPoints(_controller.points);
|
||||
final String note = _noteController.text.trim();
|
||||
|
||||
// Close this screen first to show the updated TaskView quickly
|
||||
if (!mounted) return;
|
||||
_restoreOrientation();
|
||||
Navigator.of(context).pop();
|
||||
|
||||
// Then notify the caller (SVG only)
|
||||
widget.onSignatureCompleted(svg);
|
||||
// Then notify the caller (SVG + Bemerkung)
|
||||
widget.onSignatureCompleted(svg, note);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -233,6 +236,16 @@ class _SignatureCaptureScreenState extends State<SignatureCaptureScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextField(
|
||||
controller: _noteController,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).completeTaskNote,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 2,
|
||||
minLines: 1,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
|
||||
Reference in New Issue
Block a user