Commit graph

6 commits

Author SHA1 Message Date
Jens Reinemann
5e9c072b51 refactor: kcalPerKg -> kcalPerUnit (kcal pro Einheit)
- ItemEntity, ItemDto: kcalPerKg -> kcalPerUnit (kcal_per_unit)
- Room DB: version 4 -> 5, MIGRATION_4_5 hinzugefuegt
- CalculateSupplyRangeUseCase: Berechnung vereinfacht zu
  quantity * kcalPerUnit (keine Einheitenumrechnung mehr noetig,
  alle Einheiten unterstuetzt)
- ItemFormScreen: Label 'kcal / kg' -> 'kcal / Einheit'
- CsvExporter: Header 'kcal/kg' -> 'kcal/Einheit'
- OpenAiVisionService: Prompt-JSON-Feld angepasst
- Server Tables + InventoryRepository: kcal_per_unit
- Alle Tests aktualisiert und gruen
2026-05-17 11:29:39 +02:00
Jens Reinemann
eb9ab6aa54 feat: Multi-Inventar auf Client-Seite (#79)
Server:
- Inventories-Tabelle um name-Spalte erweitert
- Neue User-facing REST-Routes: GET/POST /api/inventories,
  POST /api/inventories/{id}/switch
- LoginResponse enthält inventoryId + inventoryName
- InventoryRepository: createInventory(name), getUserInventories(),
  getInventoryName()
- AuthRoutes: inventoryRepository injiziert für Login-Response

Shared:
- InventoryInfoDto (id, name, isActive)
- CreateInventoryRequest (name)

Client:
- SettingsKeys: ACTIVE_INVENTORY_ID, ACTIVE_INVENTORY_NAME
- SyncService: listInventories(), createInventory(), switchInventory()
- SyncServiceImpl: Implementierung der drei Endpunkte + Login
  speichert inventoryId/Name
- InventoryPickerViewModel: Laden, Erstellen, Wechseln von Inventaren
  mit automatischem Re-Sync
- InventoryPickerSheet: ModalBottomSheet mit Inventarliste, Auswahl
  und Erstellen-Dialog
- MainScreen: TopAppBar zeigt aktiven Inventar-Namen mit Wechsel-Button

Tests:
- 8 neue Server-Tests (InventoryManagementTest)
- 8 neue Client-Tests (InventoryPickerViewModelTest)
- Bestehende FakeSyncService in 2 Test-Dateien aktualisiert

Closes #79
2026-05-17 03:55:08 +02:00
Jens Reinemann
0f25c180ed feat(sync): Full-Inventory-Sync durch Delta-Sync ersetzen
Server + Client: Timestamp-basierter Delta-Sync als Alternative zum
Full-Sync. GET /api/inventory akzeptiert jetzt optionalen ?since=<ts>
Query-Parameter und liefert nur Items mit lastUpdated > since.

Shared: InventoryDto um deletedItemIds-Feld erweitert (Default: leer,
backward-compatible mit bestehenden Clients).

Server:
- DeletedItems-Tabelle trackt geloeschte Item-IDs pro Inventory
- InventoryRepository.loadInventorySince(): Delta-Query mit Items +
  deletedItemIds seit Timestamp
- saveInventory()/deleteItem(): Loeschungen werden in DeletedItems
  protokolliert
- DatabaseFactory: DeletedItems-Tabelle registriert

Client (App):
- SyncService.downloadInventory(since: Long?): optionaler since-Param
- SyncServiceImpl: haengt ?since= an GET-Request
- ItemDao.deleteByIds(): Batch-Loeschung fuer Delta-Sync
- ImportExportRepositoryImpl: verarbeitet deletedItemIds aus DTO
- SettingsViewModel.pullSync(fullSync): Delta-Sync mit letztem
  Sync-Timestamp; fullSyncRequired-Event loest weiterhin Full-Sync aus

Entscheidungen:
- Timestamp-basiert (nutzt bestehendes lastUpdated-Feld)
- Full-Sync bleibt Fallback (fullSyncRequired, erster Sync)
- Categories/Locations/Settings immer vollstaendig (klein)

6 neue DeltaSyncTests + 3 Repository-Tests + 2 SyncService-Tests

Closes #74
2026-05-17 03:15:49 +02:00
Jens Reinemann
56ac9b1425 feat: Messaging-System mit Offline-First und WebSocket-Push (#58)
## Server
- Messages-Tabelle (id, sender_id, receiver_id, body, sent_at, delivered_at)
- MessageRepository: save/getUndelivered/getConversation/markDelivered (JOIN statt N+1)
- POST /api/messages, GET /api/messages/{userId}: Nachrichten senden/abrufen
- GET /api/users: User-Liste fuer authentifizierte User (ohne eigenen Account)
- WebSocketManager: notifyNewMessage() + isOnline()
- WebSocketRoutes: unzugestellte Nachrichten bei Reconnect pushen
- LoginResponse: userId + username ergaenzt
- Server-Dependency: kotlinx.serialization fuer shared

## App
- MessageEntity + MessageDao (Room, Migration 3->4)
- KrisenvorratDatabase v4, Migrations.MIGRATION_3_4
- MessageRepositoryImpl: Offline-First (isPending), drain bei WebSocket-Connect
- WebSocketEvent.NewMessage -> MessageDto aus shared
- WebSocketClientImpl: new_message-Event parsen
- AUTH_USER_ID in SettingsKeys, SyncServiceImpl speichert userId bei Login
- UserListScreen + UserListViewModel: User-Liste anzeigen
- ChatScreen + ChatViewModel: WhatsApp-Style Chat (links/rechts, Zeitstempel)
- Navigation: Screen.UserList, Screen.Chat, MESSAGES in Bottom-Nav
- RepositoryModule: MessageRepository gebunden

## Tests
- 234 Tests, 0 Fehler
2026-05-16 23:35:25 +02:00
Jens Reinemann
8280a9daf9 refactor: kcal/100g -> kcal/kg umbenennen und Mindestbestand entfernen
- ItemEntity, ItemDto: kcalPer100g -> kcalPerKg (kcal_per_kg),
  minStock-Spalte komplett entfernt
- CalculateSupplyRangeUseCase: Formel angepasst (/ 1000.0 * kcalPerKg)
- GetMinStockWarningsUseCase + MinStockWarning: gelöscht
- UI (ItemFormScreen, WarningsScreen, DashboardScreen): Mindestbestand-
  Felder und Warnungsabschnitte entfernt
- ViewModels, UiState, Repository: alle Referenzen bereinigt
- Server (Tables, InventoryRepository): Schema angepasst
- Room: fallbackToDestructiveMigration() hinzugefügt (keine Produktivdaten)
- Alle 434 Tests gruen
2026-05-16 14:19:10 +02:00
Jens Reinemann
c0c4978ccf feat(shared): add shared module with common DTO models
New Gradle module :shared (pure Kotlin/JVM) containing @Serializable
DTO classes for use by both the Android app and future Ktor server.

shared/src/main/kotlin/de/krisenvorrat/shared/model/:
- InventoryDto: root DTO replacing ExportData (version, categories,
  locations, items, settings)
- CategoryDto, LocationDto, ItemDto, SettingDto: extracted from
  the former *Export data classes in :app

Migration in :app:
- ExportData.kt deleted (classes moved to :shared)
- ImportExportRepositoryImpl now imports from de.krisenvorrat.shared.model
- app/build.gradle.kts adds implementation(project(:shared))

Build config:
- libs.versions.toml: added kotlin-jvm plugin entry
- build.gradle.kts (root): registered kotlin-jvm plugin
- settings.gradle.kts: include(:shared)

JSON wire format is unchanged; all 165 existing tests pass.

Closes #39
2026-05-14 19:50:23 +02:00