update database
This commit is contained in:
212
lib/features/loyalty/domain/entities/gift_catalog.dart
Normal file
212
lib/features/loyalty/domain/entities/gift_catalog.dart
Normal file
@@ -0,0 +1,212 @@
|
||||
/// Domain Entity: Gift Catalog
|
||||
///
|
||||
/// Represents a redeemable gift in the loyalty program catalog.
|
||||
library;
|
||||
|
||||
/// Gift category enum
|
||||
enum GiftCategory {
|
||||
/// Voucher gift
|
||||
voucher,
|
||||
|
||||
/// Physical product
|
||||
product,
|
||||
|
||||
/// Service
|
||||
service,
|
||||
|
||||
/// Discount coupon
|
||||
discount,
|
||||
|
||||
/// Other type
|
||||
other;
|
||||
|
||||
/// Get display name for category
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case GiftCategory.voucher:
|
||||
return 'Voucher';
|
||||
case GiftCategory.product:
|
||||
return 'Product';
|
||||
case GiftCategory.service:
|
||||
return 'Service';
|
||||
case GiftCategory.discount:
|
||||
return 'Discount';
|
||||
case GiftCategory.other:
|
||||
return 'Other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gift Catalog Entity
|
||||
///
|
||||
/// Contains information about a redeemable gift:
|
||||
/// - Gift details (name, description, image)
|
||||
/// - Pricing in points
|
||||
/// - Availability
|
||||
/// - Terms and conditions
|
||||
class GiftCatalog {
|
||||
/// Unique catalog item identifier
|
||||
final String catalogId;
|
||||
|
||||
/// Gift name
|
||||
final String name;
|
||||
|
||||
/// Gift description
|
||||
final String? description;
|
||||
|
||||
/// Gift image URL
|
||||
final String? imageUrl;
|
||||
|
||||
/// Gift category
|
||||
final GiftCategory category;
|
||||
|
||||
/// Points cost to redeem
|
||||
final int pointsCost;
|
||||
|
||||
/// Cash value equivalent
|
||||
final double? cashValue;
|
||||
|
||||
/// Quantity available for redemption
|
||||
final int quantityAvailable;
|
||||
|
||||
/// Quantity already redeemed
|
||||
final int quantityRedeemed;
|
||||
|
||||
/// Terms and conditions
|
||||
final String? termsConditions;
|
||||
|
||||
/// Gift is active and available
|
||||
final bool isActive;
|
||||
|
||||
/// Valid from date
|
||||
final DateTime? validFrom;
|
||||
|
||||
/// Valid until date
|
||||
final DateTime? validUntil;
|
||||
|
||||
/// Creation timestamp
|
||||
final DateTime createdAt;
|
||||
|
||||
/// Last update timestamp
|
||||
final DateTime updatedAt;
|
||||
|
||||
const GiftCatalog({
|
||||
required this.catalogId,
|
||||
required this.name,
|
||||
this.description,
|
||||
this.imageUrl,
|
||||
required this.category,
|
||||
required this.pointsCost,
|
||||
this.cashValue,
|
||||
required this.quantityAvailable,
|
||||
required this.quantityRedeemed,
|
||||
this.termsConditions,
|
||||
required this.isActive,
|
||||
this.validFrom,
|
||||
this.validUntil,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
/// Check if gift is available for redemption
|
||||
bool get isAvailable => isActive && quantityRemaining > 0 && isCurrentlyValid;
|
||||
|
||||
/// Get remaining quantity
|
||||
int get quantityRemaining => quantityAvailable - quantityRedeemed;
|
||||
|
||||
/// Check if gift is in stock
|
||||
bool get isInStock => quantityRemaining > 0;
|
||||
|
||||
/// Check if gift is currently valid (date range)
|
||||
bool get isCurrentlyValid {
|
||||
final now = DateTime.now();
|
||||
if (validFrom != null && now.isBefore(validFrom!)) return false;
|
||||
if (validUntil != null && now.isAfter(validUntil!)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if gift is coming soon
|
||||
bool get isComingSoon {
|
||||
if (validFrom == null) return false;
|
||||
return DateTime.now().isBefore(validFrom!);
|
||||
}
|
||||
|
||||
/// Check if gift has expired
|
||||
bool get hasExpired {
|
||||
if (validUntil == null) return false;
|
||||
return DateTime.now().isAfter(validUntil!);
|
||||
}
|
||||
|
||||
/// Get redemption percentage
|
||||
double get redemptionPercentage {
|
||||
if (quantityAvailable == 0) return 0;
|
||||
return (quantityRedeemed / quantityAvailable) * 100;
|
||||
}
|
||||
|
||||
/// Copy with method for immutability
|
||||
GiftCatalog copyWith({
|
||||
String? catalogId,
|
||||
String? name,
|
||||
String? description,
|
||||
String? imageUrl,
|
||||
GiftCategory? category,
|
||||
int? pointsCost,
|
||||
double? cashValue,
|
||||
int? quantityAvailable,
|
||||
int? quantityRedeemed,
|
||||
String? termsConditions,
|
||||
bool? isActive,
|
||||
DateTime? validFrom,
|
||||
DateTime? validUntil,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return GiftCatalog(
|
||||
catalogId: catalogId ?? this.catalogId,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
imageUrl: imageUrl ?? this.imageUrl,
|
||||
category: category ?? this.category,
|
||||
pointsCost: pointsCost ?? this.pointsCost,
|
||||
cashValue: cashValue ?? this.cashValue,
|
||||
quantityAvailable: quantityAvailable ?? this.quantityAvailable,
|
||||
quantityRedeemed: quantityRedeemed ?? this.quantityRedeemed,
|
||||
termsConditions: termsConditions ?? this.termsConditions,
|
||||
isActive: isActive ?? this.isActive,
|
||||
validFrom: validFrom ?? this.validFrom,
|
||||
validUntil: validUntil ?? this.validUntil,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is GiftCatalog &&
|
||||
other.catalogId == catalogId &&
|
||||
other.name == name &&
|
||||
other.category == category &&
|
||||
other.pointsCost == pointsCost &&
|
||||
other.isActive == isActive;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
catalogId,
|
||||
name,
|
||||
category,
|
||||
pointsCost,
|
||||
isActive,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GiftCatalog(catalogId: $catalogId, name: $name, category: $category, '
|
||||
'pointsCost: $pointsCost, quantityRemaining: $quantityRemaining, '
|
||||
'isAvailable: $isAvailable)';
|
||||
}
|
||||
}
|
||||
232
lib/features/loyalty/domain/entities/loyalty_point_entry.dart
Normal file
232
lib/features/loyalty/domain/entities/loyalty_point_entry.dart
Normal file
@@ -0,0 +1,232 @@
|
||||
/// 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<String, dynamic>? 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<String, dynamic>? 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)';
|
||||
}
|
||||
}
|
||||
182
lib/features/loyalty/domain/entities/points_record.dart
Normal file
182
lib/features/loyalty/domain/entities/points_record.dart
Normal file
@@ -0,0 +1,182 @@
|
||||
/// Domain Entity: Points Record
|
||||
///
|
||||
/// Represents a user-submitted invoice for points earning.
|
||||
library;
|
||||
|
||||
/// Points record status enum
|
||||
enum PointsStatus {
|
||||
/// Record submitted, pending review
|
||||
pending,
|
||||
|
||||
/// Record approved, points awarded
|
||||
approved,
|
||||
|
||||
/// Record rejected
|
||||
rejected;
|
||||
|
||||
/// Get display name for status
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case PointsStatus.pending:
|
||||
return 'Pending';
|
||||
case PointsStatus.approved:
|
||||
return 'Approved';
|
||||
case PointsStatus.rejected:
|
||||
return 'Rejected';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Points Record Entity
|
||||
///
|
||||
/// Contains information about a user-submitted invoice:
|
||||
/// - Invoice details
|
||||
/// - Submission files/attachments
|
||||
/// - Review status
|
||||
/// - Points calculation
|
||||
class PointsRecord {
|
||||
/// Unique record identifier
|
||||
final String recordId;
|
||||
|
||||
/// User ID who submitted
|
||||
final String userId;
|
||||
|
||||
/// Invoice number
|
||||
final String invoiceNumber;
|
||||
|
||||
/// Store/vendor name
|
||||
final String storeName;
|
||||
|
||||
/// Transaction date
|
||||
final DateTime transactionDate;
|
||||
|
||||
/// Invoice amount
|
||||
final double invoiceAmount;
|
||||
|
||||
/// Additional notes
|
||||
final String? notes;
|
||||
|
||||
/// Attachment URLs (invoice photos, receipts)
|
||||
final List<String> attachments;
|
||||
|
||||
/// Record status
|
||||
final PointsStatus status;
|
||||
|
||||
/// Rejection reason (if rejected)
|
||||
final String? rejectReason;
|
||||
|
||||
/// Points earned (if approved)
|
||||
final int? pointsEarned;
|
||||
|
||||
/// Submission timestamp
|
||||
final DateTime submittedAt;
|
||||
|
||||
/// Processing timestamp
|
||||
final DateTime? processedAt;
|
||||
|
||||
/// ID of admin who processed
|
||||
final String? processedBy;
|
||||
|
||||
const PointsRecord({
|
||||
required this.recordId,
|
||||
required this.userId,
|
||||
required this.invoiceNumber,
|
||||
required this.storeName,
|
||||
required this.transactionDate,
|
||||
required this.invoiceAmount,
|
||||
this.notes,
|
||||
required this.attachments,
|
||||
required this.status,
|
||||
this.rejectReason,
|
||||
this.pointsEarned,
|
||||
required this.submittedAt,
|
||||
this.processedAt,
|
||||
this.processedBy,
|
||||
});
|
||||
|
||||
/// Check if record is pending review
|
||||
bool get isPending => status == PointsStatus.pending;
|
||||
|
||||
/// Check if record is approved
|
||||
bool get isApproved => status == PointsStatus.approved;
|
||||
|
||||
/// Check if record is rejected
|
||||
bool get isRejected => status == PointsStatus.rejected;
|
||||
|
||||
/// Check if record has been processed
|
||||
bool get isProcessed => status != PointsStatus.pending;
|
||||
|
||||
/// Check if record has attachments
|
||||
bool get hasAttachments => attachments.isNotEmpty;
|
||||
|
||||
/// Get processing time duration
|
||||
Duration? get processingDuration {
|
||||
if (processedAt == null) return null;
|
||||
return processedAt!.difference(submittedAt);
|
||||
}
|
||||
|
||||
/// Copy with method for immutability
|
||||
PointsRecord copyWith({
|
||||
String? recordId,
|
||||
String? userId,
|
||||
String? invoiceNumber,
|
||||
String? storeName,
|
||||
DateTime? transactionDate,
|
||||
double? invoiceAmount,
|
||||
String? notes,
|
||||
List<String>? attachments,
|
||||
PointsStatus? status,
|
||||
String? rejectReason,
|
||||
int? pointsEarned,
|
||||
DateTime? submittedAt,
|
||||
DateTime? processedAt,
|
||||
String? processedBy,
|
||||
}) {
|
||||
return PointsRecord(
|
||||
recordId: recordId ?? this.recordId,
|
||||
userId: userId ?? this.userId,
|
||||
invoiceNumber: invoiceNumber ?? this.invoiceNumber,
|
||||
storeName: storeName ?? this.storeName,
|
||||
transactionDate: transactionDate ?? this.transactionDate,
|
||||
invoiceAmount: invoiceAmount ?? this.invoiceAmount,
|
||||
notes: notes ?? this.notes,
|
||||
attachments: attachments ?? this.attachments,
|
||||
status: status ?? this.status,
|
||||
rejectReason: rejectReason ?? this.rejectReason,
|
||||
pointsEarned: pointsEarned ?? this.pointsEarned,
|
||||
submittedAt: submittedAt ?? this.submittedAt,
|
||||
processedAt: processedAt ?? this.processedAt,
|
||||
processedBy: processedBy ?? this.processedBy,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is PointsRecord &&
|
||||
other.recordId == recordId &&
|
||||
other.userId == userId &&
|
||||
other.invoiceNumber == invoiceNumber &&
|
||||
other.invoiceAmount == invoiceAmount &&
|
||||
other.status == status;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
recordId,
|
||||
userId,
|
||||
invoiceNumber,
|
||||
invoiceAmount,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PointsRecord(recordId: $recordId, invoiceNumber: $invoiceNumber, '
|
||||
'storeName: $storeName, invoiceAmount: $invoiceAmount, status: $status, '
|
||||
'pointsEarned: $pointsEarned)';
|
||||
}
|
||||
}
|
||||
207
lib/features/loyalty/domain/entities/redeemed_gift.dart
Normal file
207
lib/features/loyalty/domain/entities/redeemed_gift.dart
Normal file
@@ -0,0 +1,207 @@
|
||||
/// Domain Entity: Redeemed Gift
|
||||
///
|
||||
/// Represents a gift that has been redeemed by a user.
|
||||
library;
|
||||
|
||||
import 'gift_catalog.dart';
|
||||
|
||||
/// Gift status enum
|
||||
enum GiftStatus {
|
||||
/// Gift is active and can be used
|
||||
active,
|
||||
|
||||
/// Gift has been used
|
||||
used,
|
||||
|
||||
/// Gift has expired
|
||||
expired,
|
||||
|
||||
/// Gift has been cancelled
|
||||
cancelled;
|
||||
|
||||
/// Get display name for status
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case GiftStatus.active:
|
||||
return 'Active';
|
||||
case GiftStatus.used:
|
||||
return 'Used';
|
||||
case GiftStatus.expired:
|
||||
return 'Expired';
|
||||
case GiftStatus.cancelled:
|
||||
return 'Cancelled';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Redeemed Gift Entity
|
||||
///
|
||||
/// Contains information about a redeemed gift:
|
||||
/// - Gift details
|
||||
/// - Voucher code and QR code
|
||||
/// - Usage tracking
|
||||
/// - Expiry dates
|
||||
class RedeemedGift {
|
||||
/// Unique gift identifier
|
||||
final String giftId;
|
||||
|
||||
/// User ID who redeemed the gift
|
||||
final String userId;
|
||||
|
||||
/// Catalog ID of the gift
|
||||
final String catalogId;
|
||||
|
||||
/// Gift name (snapshot at redemption time)
|
||||
final String name;
|
||||
|
||||
/// Gift description
|
||||
final String? description;
|
||||
|
||||
/// Voucher code
|
||||
final String? voucherCode;
|
||||
|
||||
/// QR code image URL
|
||||
final String? qrCodeImage;
|
||||
|
||||
/// Gift type/category
|
||||
final GiftCategory giftType;
|
||||
|
||||
/// Points cost (snapshot at redemption time)
|
||||
final int pointsCost;
|
||||
|
||||
/// Cash value (snapshot at redemption time)
|
||||
final double? cashValue;
|
||||
|
||||
/// Expiry date
|
||||
final DateTime? expiryDate;
|
||||
|
||||
/// Gift status
|
||||
final GiftStatus status;
|
||||
|
||||
/// Redemption timestamp
|
||||
final DateTime redeemedAt;
|
||||
|
||||
/// Usage timestamp
|
||||
final DateTime? usedAt;
|
||||
|
||||
/// Location where gift was used
|
||||
final String? usedLocation;
|
||||
|
||||
/// Reference number when used (e.g., order ID)
|
||||
final String? usedReference;
|
||||
|
||||
const RedeemedGift({
|
||||
required this.giftId,
|
||||
required this.userId,
|
||||
required this.catalogId,
|
||||
required this.name,
|
||||
this.description,
|
||||
this.voucherCode,
|
||||
this.qrCodeImage,
|
||||
required this.giftType,
|
||||
required this.pointsCost,
|
||||
this.cashValue,
|
||||
this.expiryDate,
|
||||
required this.status,
|
||||
required this.redeemedAt,
|
||||
this.usedAt,
|
||||
this.usedLocation,
|
||||
this.usedReference,
|
||||
});
|
||||
|
||||
/// Check if gift is active
|
||||
bool get isActive => status == GiftStatus.active;
|
||||
|
||||
/// Check if gift is used
|
||||
bool get isUsed => status == GiftStatus.used;
|
||||
|
||||
/// Check if gift is expired
|
||||
bool get isExpired =>
|
||||
status == GiftStatus.expired ||
|
||||
(expiryDate != null && DateTime.now().isAfter(expiryDate!));
|
||||
|
||||
/// Check if gift can be used
|
||||
bool get canBeUsed => isActive && !isExpired;
|
||||
|
||||
/// Check if gift is expiring soon (within 7 days)
|
||||
bool get isExpiringSoon {
|
||||
if (expiryDate == null || isExpired) return false;
|
||||
final daysUntilExpiry = expiryDate!.difference(DateTime.now()).inDays;
|
||||
return daysUntilExpiry > 0 && daysUntilExpiry <= 7;
|
||||
}
|
||||
|
||||
/// Get days until expiry
|
||||
int? get daysUntilExpiry {
|
||||
if (expiryDate == null) return null;
|
||||
final days = expiryDate!.difference(DateTime.now()).inDays;
|
||||
return days > 0 ? days : 0;
|
||||
}
|
||||
|
||||
/// Copy with method for immutability
|
||||
RedeemedGift copyWith({
|
||||
String? giftId,
|
||||
String? userId,
|
||||
String? catalogId,
|
||||
String? name,
|
||||
String? description,
|
||||
String? voucherCode,
|
||||
String? qrCodeImage,
|
||||
GiftCategory? giftType,
|
||||
int? pointsCost,
|
||||
double? cashValue,
|
||||
DateTime? expiryDate,
|
||||
GiftStatus? status,
|
||||
DateTime? redeemedAt,
|
||||
DateTime? usedAt,
|
||||
String? usedLocation,
|
||||
String? usedReference,
|
||||
}) {
|
||||
return RedeemedGift(
|
||||
giftId: giftId ?? this.giftId,
|
||||
userId: userId ?? this.userId,
|
||||
catalogId: catalogId ?? this.catalogId,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
voucherCode: voucherCode ?? this.voucherCode,
|
||||
qrCodeImage: qrCodeImage ?? this.qrCodeImage,
|
||||
giftType: giftType ?? this.giftType,
|
||||
pointsCost: pointsCost ?? this.pointsCost,
|
||||
cashValue: cashValue ?? this.cashValue,
|
||||
expiryDate: expiryDate ?? this.expiryDate,
|
||||
status: status ?? this.status,
|
||||
redeemedAt: redeemedAt ?? this.redeemedAt,
|
||||
usedAt: usedAt ?? this.usedAt,
|
||||
usedLocation: usedLocation ?? this.usedLocation,
|
||||
usedReference: usedReference ?? this.usedReference,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is RedeemedGift &&
|
||||
other.giftId == giftId &&
|
||||
other.userId == userId &&
|
||||
other.catalogId == catalogId &&
|
||||
other.voucherCode == voucherCode &&
|
||||
other.status == status;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
giftId,
|
||||
userId,
|
||||
catalogId,
|
||||
voucherCode,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RedeemedGift(giftId: $giftId, name: $name, voucherCode: $voucherCode, '
|
||||
'status: $status, redeemedAt: $redeemedAt)';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user