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:
2026-04-20 12:42:56 +02:00
parent 6e8bedd9b4
commit 704d1e7378
42 changed files with 720 additions and 196 deletions

View File

@@ -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);
}