/// Domain Entity: Payment Reminder /// /// Represents a payment reminder for an unpaid invoice. library; /// Reminder type enum enum ReminderType { /// Initial reminder before due date initial, /// Reminder on due date dueDate, /// First reminder after due date firstOverdue, /// Second reminder after due date secondOverdue, /// Final warning finalWarning; /// Get display name for reminder type String get displayName { switch (this) { case ReminderType.initial: return 'Initial Reminder'; case ReminderType.dueDate: return 'Due Date Reminder'; case ReminderType.firstOverdue: return 'First Overdue'; case ReminderType.secondOverdue: return 'Second Overdue'; case ReminderType.finalWarning: return 'Final Warning'; } } } /// Payment Reminder Entity /// /// Contains information about a payment reminder: /// - Invoice reference /// - Reminder content /// - Delivery status /// - Scheduling class PaymentReminder { /// Unique reminder identifier final String reminderId; /// Invoice ID this reminder is for final String invoiceId; /// User ID receiving the reminder final String userId; /// Reminder type final ReminderType reminderType; /// Reminder subject final String subject; /// Reminder message final String message; /// Reminder has been read final bool isRead; /// Reminder has been sent final bool isSent; /// Scheduled send timestamp final DateTime? scheduledAt; /// Actual send timestamp final DateTime? sentAt; /// Read timestamp final DateTime? readAt; const PaymentReminder({ required this.reminderId, required this.invoiceId, required this.userId, required this.reminderType, required this.subject, required this.message, required this.isRead, required this.isSent, this.scheduledAt, this.sentAt, this.readAt, }); /// Check if reminder is pending (scheduled but not sent) bool get isPending => !isSent && scheduledAt != null; /// Check if reminder is overdue to be sent bool get isOverdueToSend { if (isSent || scheduledAt == null) return false; return DateTime.now().isAfter(scheduledAt!); } /// Check if reminder is unread bool get isUnread => !isRead; /// Get time until scheduled send Duration? get timeUntilSend { if (scheduledAt == null || isSent) return null; final duration = scheduledAt!.difference(DateTime.now()); return duration.isNegative ? null : duration; } /// Get time since sent Duration? get timeSinceSent { if (sentAt == null) return null; return DateTime.now().difference(sentAt!); } /// Copy with method for immutability PaymentReminder copyWith({ String? reminderId, String? invoiceId, String? userId, ReminderType? reminderType, String? subject, String? message, bool? isRead, bool? isSent, DateTime? scheduledAt, DateTime? sentAt, DateTime? readAt, }) { return PaymentReminder( reminderId: reminderId ?? this.reminderId, invoiceId: invoiceId ?? this.invoiceId, userId: userId ?? this.userId, reminderType: reminderType ?? this.reminderType, subject: subject ?? this.subject, message: message ?? this.message, isRead: isRead ?? this.isRead, isSent: isSent ?? this.isSent, scheduledAt: scheduledAt ?? this.scheduledAt, sentAt: sentAt ?? this.sentAt, readAt: readAt ?? this.readAt, ); } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is PaymentReminder && other.reminderId == reminderId && other.invoiceId == invoiceId && other.userId == userId && other.reminderType == reminderType && other.isRead == isRead && other.isSent == isSent; } @override int get hashCode { return Object.hash( reminderId, invoiceId, userId, reminderType, isRead, isSent, ); } @override String toString() { return 'PaymentReminder(reminderId: $reminderId, invoiceId: $invoiceId, ' 'reminderType: $reminderType, isSent: $isSent, isRead: $isRead)'; } }