feat: Umstellung auf Anthropic Claude SDK, Rechnungsgenerator-Systemtemplate und Task-Status-Sync
- LLM-Integration von LM Studio (Spring AI OpenAI) auf Anthropic Java SDK umgestellt - Rechnungsgenerator: System-Template für Rechnungen an Nutzer, Canvas leeren, PDF-Vorschau - Job manuell beenden sendet job_deleted an verbundene App-Clients - MessageController: nur offene Jobs ausliefern, station_completed per jobId auflösen - App: Task-Erledigungsstatus aus Server-Tasks auf Stations-Snapshots übernehmen, lokale Task-Status bei Offline-Pufferung erhalten - Adressbuch-Wording in allen Sprachdateien, Version 0.9.18 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -258,6 +258,40 @@ class Job {
|
||||
return (a.taskOrder ?? 0).compareTo(b.taskOrder ?? 0);
|
||||
}
|
||||
|
||||
// The tasks embedded in deliveryStations are snapshots from job creation
|
||||
// and never reflect later completions. The top-level tasks list comes from
|
||||
// the server's task collection and carries the current completion state,
|
||||
// so overlay it onto the station tasks by id.
|
||||
final topLevelTasksRaw = json['tasks'] ?? jobJson['tasks'];
|
||||
if (deliveryStations.isNotEmpty && topLevelTasksRaw is List) {
|
||||
final freshTasksById = <String, Task>{};
|
||||
for (final rawTask in topLevelTasksRaw) {
|
||||
if (rawTask is! Map) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
final task = Task.fromJson(Map<String, dynamic>.from(rawTask));
|
||||
if (task.id.isNotEmpty) {
|
||||
freshTasksById[task.id] = task;
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore unparseable entries; the station snapshot stays in place.
|
||||
}
|
||||
}
|
||||
for (final station in deliveryStations) {
|
||||
for (var i = 0; i < station.tasks.length; i++) {
|
||||
final fresh = freshTasksById[station.tasks[i].id];
|
||||
if (fresh != null && fresh.completed && !station.tasks[i].completed) {
|
||||
station.tasks[i] = station.tasks[i].copyWith(
|
||||
completed: true,
|
||||
completedAt: fresh.completedAt,
|
||||
completedBy: fresh.completedBy,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse tasks array
|
||||
List<Task> tasks = [];
|
||||
if (deliveryStations.isNotEmpty) {
|
||||
|
||||
@@ -116,9 +116,16 @@ class DatabaseService {
|
||||
final jobBox = _store!.box<JobEntity>();
|
||||
final taskStatusBox = _store!.box<TaskStatusEntity>();
|
||||
|
||||
// Clear existing jobs and related task statuses before inserting new ones
|
||||
// Clear existing jobs before inserting new ones. Task statuses are NOT
|
||||
// cleared wholesale: locally completed tasks may not be confirmed by the
|
||||
// server yet (offline buffering), so only statuses of tasks that no
|
||||
// longer exist in any job are removed below.
|
||||
jobBox.removeAll();
|
||||
taskStatusBox.removeAll();
|
||||
|
||||
final existingStatuses = {
|
||||
for (final entity in taskStatusBox.getAll()) entity.taskId: entity,
|
||||
};
|
||||
final knownTaskIds = <String>{};
|
||||
|
||||
// Save new jobs
|
||||
for (final job in jobs) {
|
||||
@@ -134,7 +141,8 @@ class DatabaseService {
|
||||
// Also persist task completion states from JSON (adopt status on load)
|
||||
// Only set completed=true entries to avoid overwriting local progress with false
|
||||
for (final task in normalized.tasks) {
|
||||
if (task.completed) {
|
||||
knownTaskIds.add(task.id);
|
||||
if (task.completed && !existingStatuses.containsKey(task.id)) {
|
||||
final taskStatusEntity = TaskStatusEntity(
|
||||
taskId: task.id,
|
||||
completed: true,
|
||||
@@ -147,6 +155,13 @@ class DatabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
// Drop statuses of tasks that no longer belong to any assigned job
|
||||
for (final entry in existingStatuses.entries) {
|
||||
if (!knownTaskIds.contains(entry.key)) {
|
||||
taskStatusBox.remove(entry.value.id);
|
||||
}
|
||||
}
|
||||
|
||||
developer.log(
|
||||
'Jobs and task statuses saved successfully',
|
||||
name: 'DatabaseService',
|
||||
|
||||
Reference in New Issue
Block a user