Lieferpausen - Technische Dokumentation für Mobile App¶
Übersicht¶
Lieferpausen sind kundenspezifische Zeiträume, in denen KEINE Lieferungen an einen Kunden erfolgen. Sie werden verwendet, um Urlaube, Betriebsferien oder andere Pausen in der regelmäßigen Lieferung zu verwalten. Lieferpausen werden bei der Berechnung des nächsten Liefertermins und bei Bestellerinnerungen berücksichtigt.
Datenmodell¶
DeliveryBreakItem¶
class DeliveryBreakItem {
final String? id; // Firestore Document ID
final DateTime beginDate; // Startdatum der Lieferpause (inklusiv)
final DateTime endDate; // Enddatum der Lieferpause (inklusiv)
final String? reason; // Optionaler Grund (z.B. "Urlaub", "Betriebsferien")
final DateTime createdAt; // Erstellungszeitpunkt
final String createdBy; // User ID des Erstellers
}
Eigenschaften¶
- id: Wird automatisch von Firestore vergeben,
nullbei neuen Items - beginDate: Datum im Format
DateTime, Uhrzeit wird ignoriert (nur Tag zählt) - endDate: Datum im Format
DateTime, muss >= beginDate sein - reason: Optional, Standard ist "Lieferpause" wenn nicht angegeben
- createdAt: Timestamp der Erstellung
- createdBy: Firebase Auth UID des Benutzers
Serialisierung¶
toMap():
{
'id': id,
'beginDate': beginDate.toIso8601String(),
'endDate': endDate.toIso8601String(),
'reason': reason,
'createdAt': createdAt.toIso8601String(),
'createdBy': createdBy,
}
fromMap():
DeliveryBreakItem.fromMap(Map<String, dynamic> map) {
return DeliveryBreakItem(
id: map['id'],
beginDate: DateTime.parse(map['beginDate']),
endDate: DateTime.parse(map['endDate']),
reason: map['reason'],
createdAt: DateTime.parse(map['createdAt']),
createdBy: map['createdBy'],
);
}
Firebase-Struktur¶
Firestore Collection Path¶
/customers
/{customerId}
/deliveryBreaks
/{deliveryBreakId}
- beginDate: "2024-01-01T00:00:00.000Z"
- endDate: "2024-01-07T00:00:00.000Z"
- reason: "Urlaub"
- createdAt: "2024-01-01T10:30:00.000Z"
- createdBy: "userId123"
Collection Name Konstante¶
Subcollection Struktur¶
Lieferpausen sind eine Subcollection unter dem jeweiligen Kunden.
Pfad: customers/{customerId}/deliveryBreaks
Backend Service¶
DeliveryBreakFirebaseService¶
Der Service bietet folgende Methoden:
1. Lieferpausen laden (einmalig)¶
- Lädt alle Lieferpausen für einen Kunden einmalig
- Returns: Liste von
DeliveryBreakItem - Bei Fehler: leere Liste
[]
2. Lieferpausen streamen (reactive)¶
- Liefert einen Stream, der bei Änderungen automatisch aktualisiert wird
- Ideal für Echtzeit-Updates in der UI
- Returns:
Stream<List<DeliveryBreakItem>>
3. Lieferpause erstellen¶
- Erstellt eine neue Lieferpause
createdAtundcreatedBywerden automatisch gesetzt- Item wird mit
add()zu Firestore hinzugefügt (ID wird automatisch generiert)
4. Lieferpause aktualisieren¶
- Aktualisiert eine bestehende Lieferpause
item.idmuss gesetzt sein- Verwendet Firestore
update()
5. Lieferpause löschen¶
- Löscht eine Lieferpause
- Firestore Document wird permanent gelöscht
State Management (BLoC Pattern)¶
DeliveryBreakBloc¶
Der BLoC verwaltet den State der Lieferpausen für einen Kunden.
Events¶
// Lieferpausen für einen Kunden laden
LoadDeliveryBreaks(String customerId)
// Neue Lieferpause hinzufügen
AddDeliveryBreak(String customerId, DeliveryBreakItem item)
// Lieferpause aktualisieren
UpdateDeliveryBreak(String customerId, DeliveryBreakItem item)
// Lieferpause löschen
DeleteDeliveryBreak(String customerId, String itemId)
// Internes Event für Stream-Updates
DeliveryBreaksUpdated(List<DeliveryBreakItem> items)
States¶
BlocInitial() // Initial State
BlocLoading() // Lädt gerade Daten
LoadedDeliveryBreaks(List<DeliveryBreakItem>) // Erfolgreich geladen (extends BlocLoaded)
BlocLoadingFailed() // Fehler beim Laden
AddingDeliveryBreakFailed(String message) // Fehler beim Erstellen
UpdatingDeliveryBreakFailed(String message) // Fehler beim Aktualisieren
DeletingDeliveryBreakFailed(String message) // Fehler beim Löschen
Wichtig: LoadedDeliveryBreaks muss von BlocLoaded erben, damit der EsBlocBuilder den State korrekt erkennt und die UI rendert.
Verwendung im Widget¶
// Im Widget
BlocConsumer<DeliveryBreakBloc, BaseBlocState>(
listener: (context, state) {
if (state is BlocLoadingFailed) {
// Fehler anzeigen
}
},
builder: (context, state) {
if (state is LoadedDeliveryBreaks) {
final breaks = state.deliveryBreaksList;
// UI mit Lieferpausen rendern
}
return Container();
},
)
// Event dispatchen
context.read<DeliveryBreakBloc>().add(LoadDeliveryBreaks(customerId));
Logik & Business Rules¶
1. Datumsvalidierung¶
beginDatemuss vor oder gleichendDatesein- Beide Daten müssen in der Zukunft oder Gegenwart liegen
- In der Desktop-App: maximal 730 Tage (2 Jahre) in die Zukunft
2. Überlappende Lieferpausen¶
- Mehrere Lieferpausen können existieren
- Überlappungen sind technisch erlaubt, sollten aber vermieden werden
- Die UI sollte Überlappungen visuell kennzeichnen
3. Prüfung ob Datum in Lieferpause liegt¶
bool isInDeliveryBreak(DateTime date, List<DeliveryBreakItem> breaks) {
return breaks.any((deliveryBreak) {
final dateOnly = DateTime(date.year, date.month, date.day);
final startOnly = DateTime(
deliveryBreak.beginDate.year,
deliveryBreak.beginDate.month,
deliveryBreak.beginDate.day
);
final endOnly = DateTime(
deliveryBreak.endDate.year,
deliveryBreak.endDate.month,
deliveryBreak.endDate.day
);
return (dateOnly.isAfter(startOnly) || dateOnly.isAtSameMomentAs(startOnly)) &&
(dateOnly.isBefore(endOnly) || dateOnly.isAtSameMomentAs(endOnly));
});
}
4. Integration mit Liefertag-Berechnung¶
Lieferpausen werden bei der Berechnung des nächsten Liefertages berücksichtigt:
// Backend Node.js Funktion (job_orderReminderNotifications.js)
function checkCustomerDeliveryBreaks(date, deliveryBreaks) {
if (!deliveryBreaks || deliveryBreaks.length === 0) {
return { isAllowed: true, reason: '' };
}
const deliveryBreak = deliveryBreaks.find(period => {
const start = period.beginDate;
const end = period.endDate;
return date >= start && date <= end;
});
if (deliveryBreak) {
const reason = deliveryBreak.reason || 'Lieferpause';
return { isAllowed: false, reason };
}
return { isAllowed: true, reason: '' };
}
Der Job job_orderReminderNotifications lädt Lieferpausen für jeden Kunden und berücksichtigt sie bei der Berechnung:
// Lade Lieferpausen für diesen Kunden
const deliveryBreaksSnapshot = await db
.collection('customers')
.doc(customerId)
.collection('deliveryBreaks')
.get();
const customerDeliveryBreaks = deliveryBreaksSnapshot.docs.map(doc => {
const data = doc.data();
return {
beginDate: data.beginDate.toDate ? data.beginDate.toDate() : new Date(data.beginDate),
endDate: data.endDate.toDate ? data.endDate.toDate() : new Date(data.endDate),
reason: data.reason || 'Lieferpause',
};
});
// Berechne nächsten Liefertag UNTER BERÜCKSICHTIGUNG der Lieferpausen
const deliveryInfo = calculateNextDeliveryDay(
deliverySchedule,
deliveryDaysSettings,
customerDeliveryBreaks, // ← Lieferpausen werden übergeben!
nowInCustomerTZ
);
UI-Komponenten (Desktop-App als Referenz)¶
1. DeliveryBreakEditorDialog¶
Dialog zum Erstellen/Bearbeiten von Lieferpausen.
Features: - Kalenderansicht zur Auswahl von Start- und Enddatum - Bereichsauswahl (von-bis) - Optionales Textfeld für Grund - Validation: Start muss <= Ende sein - Action Buttons: Abbrechen / Übernehmen
Verwendung:
final result = await DeliveryBreakEditorDialog.show(
context,
deliveryBreak: existingBreak, // null für neuen Eintrag
);
if (result != null) {
// result ist DeliveryBreakItem
// Service aufrufen zum Speichern
}
2. DeliveryDaysCalendarDialog¶
Kalenderansicht die Liefertage UND Lieferpausen visualisiert.
Features:
- Monatsansicht mit Liefertagen (grün)
- Lieferpausen werden visuell hervorgehoben (lila/purple)
- Liste der anstehenden Lieferpausen
- Direktes Bearbeiten/Löschen von Lieferpausen
- Icons: CupertinoIcons.pause_circle_fill für Lieferpausen
- Tap auf Lieferpause scrollt zum Datum
- Long-Press öffnet Editor
Visuelle Kennzeichnung:
if (isDeliveryBreak) {
Icon(
CupertinoIcons.pause_circle_fill,
size: 14,
color: isDelivery ? Colors.white : Colors.purple.shade700,
)
}
Lokalisierung (i18n)¶
Deutsche Texte (lib/l10n/intl_de.arb)¶
{
"customerDetailPage_deliveryBreaks": "Lieferpausen",
"customerDeliveryBreak_edit": "Lieferpause bearbeiten",
"customerDeliveryBreak_add": "Lieferpause hinzufügen",
"customerDeliveryBreak_start": "Beginn",
"customerDeliveryBreak_end": "Ende",
"customerDeliveryBreak_reason": "Grund"
}
Englische Texte (lib/l10n/intl_en.arb)¶
{
"customerDetailPage_deliveryBreaks": "Delivery Breaks",
"customerDeliveryBreak_edit": "Edit delivery break",
"customerDeliveryBreak_add": "Add delivery break",
"customerDeliveryBreak_start": "Start",
"customerDeliveryBreak_end": "End",
"customerDeliveryBreak_reason": "Reason"
}
Verwendung¶
Mobile App - Implementierungshinweise¶
Empfohlene Funktionen für Mobile App¶
1. Anzeige der Lieferpausen¶
Listenansicht: - Liste aller aktiven/zukünftigen Lieferpausen - Sortiert nach Startdatum - Card-Design mit: - Datumsbereich (z.B. "01.01.2024 - 07.01.2024") - Grund (falls vorhanden) - Icon für Lieferpause - Edit/Delete Aktionen (wenn Berechtigungen vorhanden)
Kalenderansicht: - Monatlicher Kalender mit Markierungen - Liefertage in einer Farbe (grün) - Lieferpausen in anderer Farbe (lila/purple) - Tap auf Tag zeigt Details
2. Erstellen einer Lieferpause¶
Bottom Sheet oder Dialog: - Date Range Picker für Start- und Enddatum - Optional: Textfeld für Grund - Save Button
Validation:
if (startDate == null || endDate == null) {
// Fehler: Beide Daten müssen ausgewählt sein
}
if (endDate.isBefore(startDate)) {
// Fehler: Endedatum muss nach Startdatum liegen
}
3. Bearbeiten einer Lieferpause¶
- Gleicher Dialog/Sheet wie beim Erstellen
- Felder werden mit vorhandenen Werten gefüllt
- Update statt Create beim Speichern
4. Löschen einer Lieferpause¶
- Swipe-to-delete in der Liste
- Oder: Icon-Button mit Bestätigungsdialog
- Nach Löschen: Liste aktualisieren
5. Berechtigungen¶
Prüfen Sie: - Darf der aktuelle User Lieferpausen sehen? - Darf der aktuelle User Lieferpausen erstellen/bearbeiten/löschen?
Dies hängt von den Rollen und Berechtigungen ab (siehe ROLES_AND_PERMISSIONS.md).
Code-Beispiel für Mobile App¶
// 1. Service initialisieren
final deliveryBreakService = DeliveryBreakFirebaseService();
// 2. BLoC initialisieren (in BlocProvider)
BlocProvider(
create: (context) => DeliveryBreakBloc(deliveryBreakService),
child: DeliveryBreaksScreen(),
)
// 3. Lieferpausen laden
context.read<DeliveryBreakBloc>().add(LoadDeliveryBreaks(customerId));
// 4. Stream listen
StreamBuilder<List<DeliveryBreakItem>>(
stream: deliveryBreakService.streamDeliveryBreaks(customerId),
builder: (context, snapshot) {
if (snapshot.hasData) {
final breaks = snapshot.data!;
return ListView.builder(
itemCount: breaks.length,
itemBuilder: (context, index) {
final deliveryBreak = breaks[index];
return DeliveryBreakCard(deliveryBreak: deliveryBreak);
},
);
}
return CircularProgressIndicator();
},
)
// 5. Neue Lieferpause erstellen
Future<void> createDeliveryBreak(BuildContext context) async {
// ... User Input holen ...
final newBreak = DeliveryBreakItem(
beginDate: startDate,
endDate: endDate,
reason: reasonController.text.isNotEmpty ? reasonController.text : null,
createdAt: DateTime.now(),
createdBy: '', // Wird vom Service gesetzt
);
context.read<DeliveryBreakBloc>().add(
AddDeliveryBreak(customerId, newBreak)
);
}
// 6. Lieferpause aktualisieren
context.read<DeliveryBreakBloc>().add(
UpdateDeliveryBreak(customerId, updatedBreak)
);
// 7. Lieferpause löschen
context.read<DeliveryBreakBloc>().add(
DeleteDeliveryBreak(customerId, breakId)
);
Testing¶
Unit Tests¶
Die Desktop-App enthält Unit Tests für den BLoC:
test/unit/blocs/delivery_break_bloc/delivery_break_bloc_test.dart
Was wird getestet: - States haben korrekte Properties - Events dispatchen korrekt - BLoC lädt Lieferpausen - CRUD Operationen funktionieren
Beispiel:
test('LoadDeliveryBreaks should hold customerId', () {
final event = LoadDeliveryBreaks('customer123');
expect(event.customerId, 'customer123');
});
Integration Tests¶
Empfohlene Tests für Mobile App: - Lieferpausen laden und in Liste anzeigen - Neue Lieferpause erstellen und in Firestore speichern - Lieferpause bearbeiten - Lieferpause löschen - Validierung (Start < Ende) - Offline-Verhalten (Firestore Persistence)
Performance-Optimierung¶
1. Caching¶
Die Desktop-App verwendet keinen expliziten Cache, aber der BLoC hält deliveryBreaksList im Memory.
2. Pagination¶
Aktuell gibt es keine Pagination. Bei sehr vielen Lieferpausen sollte erwogen werden: - Nur aktive/zukünftige Lieferpausen laden - Limit auf die letzten N Monate
3. Indizierung¶
Empfohlene Firestore Indexes:
- beginDate ASC
- endDate DESC
Für Queries wie:
4. Offline Support¶
Firestore bietet automatisches Caching. Stellen Sie sicher, dass Persistence aktiviert ist:
Fehlerbehandlung¶
1. Fehlende Berechtigungen¶
try {
await deliveryBreakService.createDeliveryBreakItem(customerId, item);
} catch (e) {
if (e is FirebaseException && e.code == 'permission-denied') {
// Keine Berechtigung
showDialog('Sie haben keine Berechtigung...');
}
}
2. Netzwerkfehler¶
if (state is BlocLoadingFailed) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler beim Laden: ${state.message}')),
);
}
3. Ungültige Daten¶
Sicherheit & Berechtigungen¶
Firestore Security Rules¶
Stelle sicher, dass die Security Rules Lieferpausen schützen:
match /customers/{customerId}/deliveryBreaks/{breakId} {
// Nur authentifizierte User mit Zugriff auf den Kunden
allow read: if isAuthenticated() && hasAccessToCustomer(customerId);
allow write: if isAuthenticated() && hasCustomerEditPermission(customerId);
}
Siehe deployment/PERMISSIONS_SYSTEM.md für Details zur Berechtigungsverwaltung.
Zusammenfassung¶
Key Points für Mobile App¶
- Datenstruktur: Einfaches Model mit Start, Ende, Grund
- Firestore: Subcollection unter customer
- CRUD: Vollständiger Service vorhanden
- State Management: BLoC Pattern mit Events und States
- UI: Kalender- und Listenansicht empfohlen
- Backend-Integration: Automatische Berücksichtigung bei Liefertermin-Berechnung
- Validation: Start <= Ende, beide Daten müssen gesetzt sein
- i18n: DE/EN Texte vorhanden
- Testing: Unit Tests als Referenz
Nächste Schritte¶
- UI-Design für Mobile App erstellen
- Screen-Flow definieren (Liste → Detail → Editor)
- Berechtigungen integrieren
- Offline-Support testen
- Push-Benachrichtigung bei neuen Lieferpausen erwägen
Version: 1.0
Datum: 17. Februar 2026
Autor: System Documentation