update database
This commit is contained in:
70
lib/features/loyalty/data/models/gift_catalog_model.dart
Normal file
70
lib/features/loyalty/data/models/gift_catalog_model.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/core/constants/storage_constants.dart';
|
||||
import 'package:worker/core/database/models/enums.dart';
|
||||
|
||||
part 'gift_catalog_model.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.giftCatalogModel)
|
||||
class GiftCatalogModel extends HiveObject {
|
||||
GiftCatalogModel({required this.catalogId, required this.name, required this.description, this.imageUrl, required this.category, required this.pointsCost, required this.cashValue, required this.quantityAvailable, required this.quantityRedeemed, this.termsConditions, required this.isActive, this.validFrom, this.validUntil, required this.createdAt, this.updatedAt});
|
||||
|
||||
@HiveField(0) final String catalogId;
|
||||
@HiveField(1) final String name;
|
||||
@HiveField(2) final String description;
|
||||
@HiveField(3) final String? imageUrl;
|
||||
@HiveField(4) final GiftCategory category;
|
||||
@HiveField(5) final int pointsCost;
|
||||
@HiveField(6) final double cashValue;
|
||||
@HiveField(7) final int quantityAvailable;
|
||||
@HiveField(8) final int quantityRedeemed;
|
||||
@HiveField(9) final String? termsConditions;
|
||||
@HiveField(10) final bool isActive;
|
||||
@HiveField(11) final DateTime? validFrom;
|
||||
@HiveField(12) final DateTime? validUntil;
|
||||
@HiveField(13) final DateTime createdAt;
|
||||
@HiveField(14) final DateTime? updatedAt;
|
||||
|
||||
factory GiftCatalogModel.fromJson(Map<String, dynamic> json) => GiftCatalogModel(
|
||||
catalogId: json['catalog_id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String,
|
||||
imageUrl: json['image_url'] as String?,
|
||||
category: GiftCategory.values.firstWhere((e) => e.name == json['category']),
|
||||
pointsCost: json['points_cost'] as int,
|
||||
cashValue: (json['cash_value'] as num).toDouble(),
|
||||
quantityAvailable: json['quantity_available'] as int,
|
||||
quantityRedeemed: json['quantity_redeemed'] as int? ?? 0,
|
||||
termsConditions: json['terms_conditions'] as String?,
|
||||
isActive: json['is_active'] as bool? ?? true,
|
||||
validFrom: json['valid_from'] != null ? DateTime.parse(json['valid_from']?.toString() ?? '') : null,
|
||||
validUntil: json['valid_until'] != null ? DateTime.parse(json['valid_until']?.toString() ?? '') : null,
|
||||
createdAt: DateTime.parse(json['created_at']?.toString() ?? ''),
|
||||
updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']?.toString() ?? '') : null,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'catalog_id': catalogId,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'image_url': imageUrl,
|
||||
'category': category.name,
|
||||
'points_cost': pointsCost,
|
||||
'cash_value': cashValue,
|
||||
'quantity_available': quantityAvailable,
|
||||
'quantity_redeemed': quantityRedeemed,
|
||||
'terms_conditions': termsConditions,
|
||||
'is_active': isActive,
|
||||
'valid_from': validFrom?.toIso8601String(),
|
||||
'valid_until': validUntil?.toIso8601String(),
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
'updated_at': updatedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
bool get isAvailable => isActive && quantityAvailable > quantityRedeemed;
|
||||
bool get isValid {
|
||||
final now = DateTime.now();
|
||||
if (validFrom != null && now.isBefore(validFrom!)) return false;
|
||||
if (validUntil != null && now.isAfter(validUntil!)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
83
lib/features/loyalty/data/models/gift_catalog_model.g.dart
Normal file
83
lib/features/loyalty/data/models/gift_catalog_model.g.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'gift_catalog_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class GiftCatalogModelAdapter extends TypeAdapter<GiftCatalogModel> {
|
||||
@override
|
||||
final typeId = 11;
|
||||
|
||||
@override
|
||||
GiftCatalogModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return GiftCatalogModel(
|
||||
catalogId: fields[0] as String,
|
||||
name: fields[1] as String,
|
||||
description: fields[2] as String,
|
||||
imageUrl: fields[3] as String?,
|
||||
category: fields[4] as GiftCategory,
|
||||
pointsCost: (fields[5] as num).toInt(),
|
||||
cashValue: (fields[6] as num).toDouble(),
|
||||
quantityAvailable: (fields[7] as num).toInt(),
|
||||
quantityRedeemed: (fields[8] as num).toInt(),
|
||||
termsConditions: fields[9] as String?,
|
||||
isActive: fields[10] as bool,
|
||||
validFrom: fields[11] as DateTime?,
|
||||
validUntil: fields[12] as DateTime?,
|
||||
createdAt: fields[13] as DateTime,
|
||||
updatedAt: fields[14] as DateTime?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, GiftCatalogModel obj) {
|
||||
writer
|
||||
..writeByte(15)
|
||||
..writeByte(0)
|
||||
..write(obj.catalogId)
|
||||
..writeByte(1)
|
||||
..write(obj.name)
|
||||
..writeByte(2)
|
||||
..write(obj.description)
|
||||
..writeByte(3)
|
||||
..write(obj.imageUrl)
|
||||
..writeByte(4)
|
||||
..write(obj.category)
|
||||
..writeByte(5)
|
||||
..write(obj.pointsCost)
|
||||
..writeByte(6)
|
||||
..write(obj.cashValue)
|
||||
..writeByte(7)
|
||||
..write(obj.quantityAvailable)
|
||||
..writeByte(8)
|
||||
..write(obj.quantityRedeemed)
|
||||
..writeByte(9)
|
||||
..write(obj.termsConditions)
|
||||
..writeByte(10)
|
||||
..write(obj.isActive)
|
||||
..writeByte(11)
|
||||
..write(obj.validFrom)
|
||||
..writeByte(12)
|
||||
..write(obj.validUntil)
|
||||
..writeByte(13)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(14)
|
||||
..write(obj.updatedAt);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is GiftCatalogModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'dart:convert';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/core/constants/storage_constants.dart';
|
||||
import 'package:worker/core/database/models/enums.dart';
|
||||
|
||||
part 'loyalty_point_entry_model.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.loyaltyPointEntryModel)
|
||||
class LoyaltyPointEntryModel extends HiveObject {
|
||||
LoyaltyPointEntryModel({required this.entryId, required this.userId, required this.points, required this.entryType, required this.source, required this.description, this.referenceId, this.referenceType, this.complaint, required this.complaintStatus, required this.balanceAfter, this.expiryDate, required this.timestamp, this.erpnextEntryId});
|
||||
|
||||
@HiveField(0) final String entryId;
|
||||
@HiveField(1) final String userId;
|
||||
@HiveField(2) final int points;
|
||||
@HiveField(3) final EntryType entryType;
|
||||
@HiveField(4) final EntrySource source;
|
||||
@HiveField(5) final String description;
|
||||
@HiveField(6) final String? referenceId;
|
||||
@HiveField(7) final String? referenceType;
|
||||
@HiveField(8) final String? complaint;
|
||||
@HiveField(9) final ComplaintStatus complaintStatus;
|
||||
@HiveField(10) final int balanceAfter;
|
||||
@HiveField(11) final DateTime? expiryDate;
|
||||
@HiveField(12) final DateTime timestamp;
|
||||
@HiveField(13) final String? erpnextEntryId;
|
||||
|
||||
factory LoyaltyPointEntryModel.fromJson(Map<String, dynamic> json) => LoyaltyPointEntryModel(
|
||||
entryId: json['entry_id'] as String,
|
||||
userId: json['user_id'] as String,
|
||||
points: json['points'] as int,
|
||||
entryType: EntryType.values.firstWhere((e) => e.name == json['entry_type']),
|
||||
source: EntrySource.values.firstWhere((e) => e.name == json['source']),
|
||||
description: json['description'] as String,
|
||||
referenceId: json['reference_id'] as String?,
|
||||
referenceType: json['reference_type'] as String?,
|
||||
complaint: json['complaint'] != null ? jsonEncode(json['complaint']) : null,
|
||||
complaintStatus: ComplaintStatus.values.firstWhere((e) => e.name == (json['complaint_status'] ?? 'none')),
|
||||
balanceAfter: json['balance_after'] as int,
|
||||
expiryDate: json['expiry_date'] != null ? DateTime.parse(json['expiry_date']?.toString() ?? '') : null,
|
||||
timestamp: DateTime.parse(json['timestamp']?.toString() ?? ''),
|
||||
erpnextEntryId: json['erpnext_entry_id'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'entry_id': entryId,
|
||||
'user_id': userId,
|
||||
'points': points,
|
||||
'entry_type': entryType.name,
|
||||
'source': source.name,
|
||||
'description': description,
|
||||
'reference_id': referenceId,
|
||||
'reference_type': referenceType,
|
||||
'complaint': complaint != null ? jsonDecode(complaint!) : null,
|
||||
'complaint_status': complaintStatus.name,
|
||||
'balance_after': balanceAfter,
|
||||
'expiry_date': expiryDate?.toIso8601String(),
|
||||
'timestamp': timestamp.toIso8601String(),
|
||||
'erpnext_entry_id': erpnextEntryId,
|
||||
};
|
||||
|
||||
Map<String, dynamic>? get complaintMap {
|
||||
if (complaint == null) return null;
|
||||
try {
|
||||
return jsonDecode(complaint!) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool get isExpired => expiryDate != null && DateTime.now().isAfter(expiryDate!);
|
||||
bool get hasComplaint => complaintStatus != ComplaintStatus.none;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'loyalty_point_entry_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class LoyaltyPointEntryModelAdapter
|
||||
extends TypeAdapter<LoyaltyPointEntryModel> {
|
||||
@override
|
||||
final typeId = 10;
|
||||
|
||||
@override
|
||||
LoyaltyPointEntryModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return LoyaltyPointEntryModel(
|
||||
entryId: fields[0] as String,
|
||||
userId: fields[1] as String,
|
||||
points: (fields[2] as num).toInt(),
|
||||
entryType: fields[3] as EntryType,
|
||||
source: fields[4] as EntrySource,
|
||||
description: fields[5] as String,
|
||||
referenceId: fields[6] as String?,
|
||||
referenceType: fields[7] as String?,
|
||||
complaint: fields[8] as String?,
|
||||
complaintStatus: fields[9] as ComplaintStatus,
|
||||
balanceAfter: (fields[10] as num).toInt(),
|
||||
expiryDate: fields[11] as DateTime?,
|
||||
timestamp: fields[12] as DateTime,
|
||||
erpnextEntryId: fields[13] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, LoyaltyPointEntryModel obj) {
|
||||
writer
|
||||
..writeByte(14)
|
||||
..writeByte(0)
|
||||
..write(obj.entryId)
|
||||
..writeByte(1)
|
||||
..write(obj.userId)
|
||||
..writeByte(2)
|
||||
..write(obj.points)
|
||||
..writeByte(3)
|
||||
..write(obj.entryType)
|
||||
..writeByte(4)
|
||||
..write(obj.source)
|
||||
..writeByte(5)
|
||||
..write(obj.description)
|
||||
..writeByte(6)
|
||||
..write(obj.referenceId)
|
||||
..writeByte(7)
|
||||
..write(obj.referenceType)
|
||||
..writeByte(8)
|
||||
..write(obj.complaint)
|
||||
..writeByte(9)
|
||||
..write(obj.complaintStatus)
|
||||
..writeByte(10)
|
||||
..write(obj.balanceAfter)
|
||||
..writeByte(11)
|
||||
..write(obj.expiryDate)
|
||||
..writeByte(12)
|
||||
..write(obj.timestamp)
|
||||
..writeByte(13)
|
||||
..write(obj.erpnextEntryId);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is LoyaltyPointEntryModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
70
lib/features/loyalty/data/models/points_record_model.dart
Normal file
70
lib/features/loyalty/data/models/points_record_model.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
import 'dart:convert';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/core/constants/storage_constants.dart';
|
||||
import 'package:worker/core/database/models/enums.dart';
|
||||
|
||||
part 'points_record_model.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.pointsRecordModel)
|
||||
class PointsRecordModel extends HiveObject {
|
||||
PointsRecordModel({required this.recordId, required this.userId, required this.invoiceNumber, required this.storeName, required this.transactionDate, required this.invoiceAmount, this.notes, this.attachments, required this.status, this.rejectReason, this.pointsEarned, required this.submittedAt, this.processedAt, this.processedBy});
|
||||
|
||||
@HiveField(0) final String recordId;
|
||||
@HiveField(1) final String userId;
|
||||
@HiveField(2) final String invoiceNumber;
|
||||
@HiveField(3) final String storeName;
|
||||
@HiveField(4) final DateTime transactionDate;
|
||||
@HiveField(5) final double invoiceAmount;
|
||||
@HiveField(6) final String? notes;
|
||||
@HiveField(7) final String? attachments;
|
||||
@HiveField(8) final PointsStatus status;
|
||||
@HiveField(9) final String? rejectReason;
|
||||
@HiveField(10) final int? pointsEarned;
|
||||
@HiveField(11) final DateTime submittedAt;
|
||||
@HiveField(12) final DateTime? processedAt;
|
||||
@HiveField(13) final String? processedBy;
|
||||
|
||||
factory PointsRecordModel.fromJson(Map<String, dynamic> json) => PointsRecordModel(
|
||||
recordId: json['record_id'] as String,
|
||||
userId: json['user_id'] as String,
|
||||
invoiceNumber: json['invoice_number'] as String,
|
||||
storeName: json['store_name'] as String,
|
||||
transactionDate: DateTime.parse(json['transaction_date']?.toString() ?? ''),
|
||||
invoiceAmount: (json['invoice_amount'] as num).toDouble(),
|
||||
notes: json['notes'] as String?,
|
||||
attachments: json['attachments'] != null ? jsonEncode(json['attachments']) : null,
|
||||
status: PointsStatus.values.firstWhere((e) => e.name == json['status']),
|
||||
rejectReason: json['reject_reason'] as String?,
|
||||
pointsEarned: json['points_earned'] as int?,
|
||||
submittedAt: DateTime.parse(json['submitted_at']?.toString() ?? ''),
|
||||
processedAt: json['processed_at'] != null ? DateTime.parse(json['processed_at']?.toString() ?? '') : null,
|
||||
processedBy: json['processed_by'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'record_id': recordId,
|
||||
'user_id': userId,
|
||||
'invoice_number': invoiceNumber,
|
||||
'store_name': storeName,
|
||||
'transaction_date': transactionDate.toIso8601String(),
|
||||
'invoice_amount': invoiceAmount,
|
||||
'notes': notes,
|
||||
'attachments': attachments != null ? jsonDecode(attachments!) : null,
|
||||
'status': status.name,
|
||||
'reject_reason': rejectReason,
|
||||
'points_earned': pointsEarned,
|
||||
'submitted_at': submittedAt.toIso8601String(),
|
||||
'processed_at': processedAt?.toIso8601String(),
|
||||
'processed_by': processedBy,
|
||||
};
|
||||
|
||||
List<String>? get attachmentsList {
|
||||
if (attachments == null) return null;
|
||||
try {
|
||||
final decoded = jsonDecode(attachments!) as List;
|
||||
return decoded.map((e) => e.toString()).toList();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
lib/features/loyalty/data/models/points_record_model.g.dart
Normal file
80
lib/features/loyalty/data/models/points_record_model.g.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'points_record_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class PointsRecordModelAdapter extends TypeAdapter<PointsRecordModel> {
|
||||
@override
|
||||
final typeId = 13;
|
||||
|
||||
@override
|
||||
PointsRecordModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return PointsRecordModel(
|
||||
recordId: fields[0] as String,
|
||||
userId: fields[1] as String,
|
||||
invoiceNumber: fields[2] as String,
|
||||
storeName: fields[3] as String,
|
||||
transactionDate: fields[4] as DateTime,
|
||||
invoiceAmount: (fields[5] as num).toDouble(),
|
||||
notes: fields[6] as String?,
|
||||
attachments: fields[7] as String?,
|
||||
status: fields[8] as PointsStatus,
|
||||
rejectReason: fields[9] as String?,
|
||||
pointsEarned: (fields[10] as num?)?.toInt(),
|
||||
submittedAt: fields[11] as DateTime,
|
||||
processedAt: fields[12] as DateTime?,
|
||||
processedBy: fields[13] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, PointsRecordModel obj) {
|
||||
writer
|
||||
..writeByte(14)
|
||||
..writeByte(0)
|
||||
..write(obj.recordId)
|
||||
..writeByte(1)
|
||||
..write(obj.userId)
|
||||
..writeByte(2)
|
||||
..write(obj.invoiceNumber)
|
||||
..writeByte(3)
|
||||
..write(obj.storeName)
|
||||
..writeByte(4)
|
||||
..write(obj.transactionDate)
|
||||
..writeByte(5)
|
||||
..write(obj.invoiceAmount)
|
||||
..writeByte(6)
|
||||
..write(obj.notes)
|
||||
..writeByte(7)
|
||||
..write(obj.attachments)
|
||||
..writeByte(8)
|
||||
..write(obj.status)
|
||||
..writeByte(9)
|
||||
..write(obj.rejectReason)
|
||||
..writeByte(10)
|
||||
..write(obj.pointsEarned)
|
||||
..writeByte(11)
|
||||
..write(obj.submittedAt)
|
||||
..writeByte(12)
|
||||
..write(obj.processedAt)
|
||||
..writeByte(13)
|
||||
..write(obj.processedBy);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PointsRecordModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
69
lib/features/loyalty/data/models/redeemed_gift_model.dart
Normal file
69
lib/features/loyalty/data/models/redeemed_gift_model.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/core/constants/storage_constants.dart';
|
||||
import 'package:worker/core/database/models/enums.dart';
|
||||
|
||||
part 'redeemed_gift_model.g.dart';
|
||||
|
||||
@HiveType(typeId: HiveTypeIds.redeemedGiftModel)
|
||||
class RedeemedGiftModel extends HiveObject {
|
||||
RedeemedGiftModel({required this.giftId, required this.userId, required this.catalogId, required this.name, required this.description, this.voucherCode, this.qrCodeImage, required this.giftType, required this.pointsCost, required this.cashValue, this.expiryDate, required this.status, required this.redeemedAt, this.usedAt, this.usedLocation, this.usedReference});
|
||||
|
||||
@HiveField(0) final String giftId;
|
||||
@HiveField(1) final String userId;
|
||||
@HiveField(2) final String catalogId;
|
||||
@HiveField(3) final String name;
|
||||
@HiveField(4) final String description;
|
||||
@HiveField(5) final String? voucherCode;
|
||||
@HiveField(6) final String? qrCodeImage;
|
||||
@HiveField(7) final GiftCategory giftType;
|
||||
@HiveField(8) final int pointsCost;
|
||||
@HiveField(9) final double cashValue;
|
||||
@HiveField(10) final DateTime? expiryDate;
|
||||
@HiveField(11) final GiftStatus status;
|
||||
@HiveField(12) final DateTime redeemedAt;
|
||||
@HiveField(13) final DateTime? usedAt;
|
||||
@HiveField(14) final String? usedLocation;
|
||||
@HiveField(15) final String? usedReference;
|
||||
|
||||
factory RedeemedGiftModel.fromJson(Map<String, dynamic> json) => RedeemedGiftModel(
|
||||
giftId: json['gift_id'] as String,
|
||||
userId: json['user_id'] as String,
|
||||
catalogId: json['catalog_id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String,
|
||||
voucherCode: json['voucher_code'] as String?,
|
||||
qrCodeImage: json['qr_code_image'] as String?,
|
||||
giftType: GiftCategory.values.firstWhere((e) => e.name == json['gift_type']),
|
||||
pointsCost: json['points_cost'] as int,
|
||||
cashValue: (json['cash_value'] as num).toDouble(),
|
||||
expiryDate: json['expiry_date'] != null ? DateTime.parse(json['expiry_date']?.toString() ?? '') : null,
|
||||
status: GiftStatus.values.firstWhere((e) => e.name == json['status']),
|
||||
redeemedAt: DateTime.parse(json['redeemed_at']?.toString() ?? ''),
|
||||
usedAt: json['used_at'] != null ? DateTime.parse(json['used_at']?.toString() ?? '') : null,
|
||||
usedLocation: json['used_location'] as String?,
|
||||
usedReference: json['used_reference'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'gift_id': giftId,
|
||||
'user_id': userId,
|
||||
'catalog_id': catalogId,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'voucher_code': voucherCode,
|
||||
'qr_code_image': qrCodeImage,
|
||||
'gift_type': giftType.name,
|
||||
'points_cost': pointsCost,
|
||||
'cash_value': cashValue,
|
||||
'expiry_date': expiryDate?.toIso8601String(),
|
||||
'status': status.name,
|
||||
'redeemed_at': redeemedAt.toIso8601String(),
|
||||
'used_at': usedAt?.toIso8601String(),
|
||||
'used_location': usedLocation,
|
||||
'used_reference': usedReference,
|
||||
};
|
||||
|
||||
bool get isExpired => expiryDate != null && DateTime.now().isAfter(expiryDate!);
|
||||
bool get isUsed => status == GiftStatus.used;
|
||||
bool get isActive => status == GiftStatus.active && !isExpired;
|
||||
}
|
||||
86
lib/features/loyalty/data/models/redeemed_gift_model.g.dart
Normal file
86
lib/features/loyalty/data/models/redeemed_gift_model.g.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'redeemed_gift_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class RedeemedGiftModelAdapter extends TypeAdapter<RedeemedGiftModel> {
|
||||
@override
|
||||
final typeId = 12;
|
||||
|
||||
@override
|
||||
RedeemedGiftModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return RedeemedGiftModel(
|
||||
giftId: fields[0] as String,
|
||||
userId: fields[1] as String,
|
||||
catalogId: fields[2] as String,
|
||||
name: fields[3] as String,
|
||||
description: fields[4] as String,
|
||||
voucherCode: fields[5] as String?,
|
||||
qrCodeImage: fields[6] as String?,
|
||||
giftType: fields[7] as GiftCategory,
|
||||
pointsCost: (fields[8] as num).toInt(),
|
||||
cashValue: (fields[9] as num).toDouble(),
|
||||
expiryDate: fields[10] as DateTime?,
|
||||
status: fields[11] as GiftStatus,
|
||||
redeemedAt: fields[12] as DateTime,
|
||||
usedAt: fields[13] as DateTime?,
|
||||
usedLocation: fields[14] as String?,
|
||||
usedReference: fields[15] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, RedeemedGiftModel obj) {
|
||||
writer
|
||||
..writeByte(16)
|
||||
..writeByte(0)
|
||||
..write(obj.giftId)
|
||||
..writeByte(1)
|
||||
..write(obj.userId)
|
||||
..writeByte(2)
|
||||
..write(obj.catalogId)
|
||||
..writeByte(3)
|
||||
..write(obj.name)
|
||||
..writeByte(4)
|
||||
..write(obj.description)
|
||||
..writeByte(5)
|
||||
..write(obj.voucherCode)
|
||||
..writeByte(6)
|
||||
..write(obj.qrCodeImage)
|
||||
..writeByte(7)
|
||||
..write(obj.giftType)
|
||||
..writeByte(8)
|
||||
..write(obj.pointsCost)
|
||||
..writeByte(9)
|
||||
..write(obj.cashValue)
|
||||
..writeByte(10)
|
||||
..write(obj.expiryDate)
|
||||
..writeByte(11)
|
||||
..write(obj.status)
|
||||
..writeByte(12)
|
||||
..write(obj.redeemedAt)
|
||||
..writeByte(13)
|
||||
..write(obj.usedAt)
|
||||
..writeByte(14)
|
||||
..write(obj.usedLocation)
|
||||
..writeByte(15)
|
||||
..write(obj.usedReference);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is RedeemedGiftModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
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