/// Domain Entity: Loyalty Point Entry /// /// Represents a single loyalty points transaction. library; /// Entry type enum enum EntryType { /// Points earned earn, /// Points spent/redeemed redeem, /// Points adjusted by admin adjustment, /// Points expired expiry, } /// Entry source enum enum EntrySource { /// Points from order purchase order, /// Points from referral referral, /// Points from gift redemption redemption, /// Points from project submission project, /// Points from points record pointsRecord, /// Manual adjustment by admin manual, /// Birthday bonus birthday, /// Welcome bonus welcome, /// Other source other, } /// Complaint status enum enum ComplaintStatus { /// No complaint none, /// Complaint submitted submitted, /// Complaint under review reviewing, /// Complaint approved approved, /// Complaint rejected rejected, } /// Loyalty Point Entry Entity /// /// Contains information about a single points transaction: /// - Points amount (positive for earn, negative for redeem) /// - Transaction type and source /// - Reference to related entity /// - Complaint handling class LoyaltyPointEntry { /// Unique entry identifier final String entryId; /// User ID final String userId; /// Points amount (positive for earn, negative for redeem) final int points; /// Entry type final EntryType entryType; /// Source of the points final EntrySource source; /// Description of the transaction final String? description; /// Reference ID to related entity (order ID, gift ID, etc.) final String? referenceId; /// Reference type (order, gift, project, etc.) final String? referenceType; /// Complaint details (if any) final Map? complaint; /// Complaint status final ComplaintStatus complaintStatus; /// Balance after this transaction final int balanceAfter; /// Points expiry date final DateTime? expiryDate; /// Transaction timestamp final DateTime timestamp; /// ERPNext entry ID final String? erpnextEntryId; const LoyaltyPointEntry({ required this.entryId, required this.userId, required this.points, required this.entryType, required this.source, this.description, this.referenceId, this.referenceType, this.complaint, required this.complaintStatus, required this.balanceAfter, this.expiryDate, required this.timestamp, this.erpnextEntryId, }); /// Check if points are earned (positive) bool get isEarn => points > 0 && entryType == EntryType.earn; /// Check if points are spent (negative) bool get isRedeem => points < 0 && entryType == EntryType.redeem; /// Check if entry has complaint bool get hasComplaint => complaintStatus != ComplaintStatus.none; /// Check if complaint is pending bool get isComplaintPending => complaintStatus == ComplaintStatus.submitted || complaintStatus == ComplaintStatus.reviewing; /// Check if points are expired bool get isExpired { if (expiryDate == null) return false; return DateTime.now().isAfter(expiryDate!); } /// Check if points are expiring soon (within 30 days) bool get isExpiringSoon { if (expiryDate == null) return false; final daysUntilExpiry = expiryDate!.difference(DateTime.now()).inDays; return daysUntilExpiry > 0 && daysUntilExpiry <= 30; } /// Get absolute points value int get absolutePoints => points.abs(); /// Copy with method for immutability LoyaltyPointEntry copyWith({ String? entryId, String? userId, int? points, EntryType? entryType, EntrySource? source, String? description, String? referenceId, String? referenceType, Map? complaint, ComplaintStatus? complaintStatus, int? balanceAfter, DateTime? expiryDate, DateTime? timestamp, String? erpnextEntryId, }) { return LoyaltyPointEntry( entryId: entryId ?? this.entryId, userId: userId ?? this.userId, points: points ?? this.points, entryType: entryType ?? this.entryType, source: source ?? this.source, description: description ?? this.description, referenceId: referenceId ?? this.referenceId, referenceType: referenceType ?? this.referenceType, complaint: complaint ?? this.complaint, complaintStatus: complaintStatus ?? this.complaintStatus, balanceAfter: balanceAfter ?? this.balanceAfter, expiryDate: expiryDate ?? this.expiryDate, timestamp: timestamp ?? this.timestamp, erpnextEntryId: erpnextEntryId ?? this.erpnextEntryId, ); } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is LoyaltyPointEntry && other.entryId == entryId && other.userId == userId && other.points == points && other.entryType == entryType && other.source == source && other.balanceAfter == balanceAfter; } @override int get hashCode { return Object.hash( entryId, userId, points, entryType, source, balanceAfter, ); } @override String toString() { return 'LoyaltyPointEntry(entryId: $entryId, userId: $userId, points: $points, ' 'entryType: $entryType, source: $source, balanceAfter: $balanceAfter, ' 'timestamp: $timestamp)'; } }