update database

This commit is contained in:
Phuoc Nguyen
2025-10-24 11:31:48 +07:00
parent f95fa9d0a6
commit c4272f9a21
126 changed files with 23528 additions and 2234 deletions

View File

@@ -0,0 +1,151 @@
/// Domain Entity: Audit Log
///
/// Represents an audit trail entry for system activities.
library;
/// Audit Log Entity
///
/// Contains information about a system action:
/// - User and action details
/// - Entity affected
/// - Change tracking
/// - Session information
class AuditLog {
/// Unique log identifier
final String logId;
/// User ID who performed the action
final String? userId;
/// Action performed (create, update, delete, login, etc.)
final String action;
/// Entity type affected (user, order, product, etc.)
final String? entityType;
/// Entity ID affected
final String? entityId;
/// Old value (before change)
final Map<String, dynamic>? oldValue;
/// New value (after change)
final Map<String, dynamic>? newValue;
/// IP address of the user
final String? ipAddress;
/// User agent string
final String? userAgent;
/// Timestamp of the action
final DateTime timestamp;
const AuditLog({
required this.logId,
this.userId,
required this.action,
this.entityType,
this.entityId,
this.oldValue,
this.newValue,
this.ipAddress,
this.userAgent,
required this.timestamp,
});
/// Check if log has old value
bool get hasOldValue => oldValue != null && oldValue!.isNotEmpty;
/// Check if log has new value
bool get hasNewValue => newValue != null && newValue!.isNotEmpty;
/// Check if action is create
bool get isCreate => action.toLowerCase() == 'create';
/// Check if action is update
bool get isUpdate => action.toLowerCase() == 'update';
/// Check if action is delete
bool get isDelete => action.toLowerCase() == 'delete';
/// Check if action is login
bool get isLogin => action.toLowerCase() == 'login';
/// Check if action is logout
bool get isLogout => action.toLowerCase() == 'logout';
/// Get changed fields
List<String> get changedFields {
if (!hasOldValue || !hasNewValue) return [];
final changed = <String>[];
for (final key in newValue!.keys) {
if (oldValue!.containsKey(key) && oldValue![key] != newValue![key]) {
changed.add(key);
}
}
return changed;
}
/// Get time since action
Duration get timeSinceAction {
return DateTime.now().difference(timestamp);
}
/// Copy with method for immutability
AuditLog copyWith({
String? logId,
String? userId,
String? action,
String? entityType,
String? entityId,
Map<String, dynamic>? oldValue,
Map<String, dynamic>? newValue,
String? ipAddress,
String? userAgent,
DateTime? timestamp,
}) {
return AuditLog(
logId: logId ?? this.logId,
userId: userId ?? this.userId,
action: action ?? this.action,
entityType: entityType ?? this.entityType,
entityId: entityId ?? this.entityId,
oldValue: oldValue ?? this.oldValue,
newValue: newValue ?? this.newValue,
ipAddress: ipAddress ?? this.ipAddress,
userAgent: userAgent ?? this.userAgent,
timestamp: timestamp ?? this.timestamp,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is AuditLog &&
other.logId == logId &&
other.userId == userId &&
other.action == action &&
other.entityType == entityType &&
other.entityId == entityId;
}
@override
int get hashCode {
return Object.hash(
logId,
userId,
action,
entityType,
entityId,
);
}
@override
String toString() {
return 'AuditLog(logId: $logId, userId: $userId, action: $action, '
'entityType: $entityType, entityId: $entityId, timestamp: $timestamp)';
}
}

View File

@@ -0,0 +1,179 @@
/// 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)';
}
}