301 lines
8.4 KiB
Dart
301 lines
8.4 KiB
Dart
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 'user_model.g.dart';
|
|
|
|
/// User Model
|
|
///
|
|
/// Hive CE model for caching user data locally.
|
|
/// Maps to the 'users' table in the database.
|
|
///
|
|
/// Type ID: 0
|
|
@HiveType(typeId: HiveTypeIds.userModel)
|
|
class UserModel extends HiveObject {
|
|
UserModel({
|
|
required this.userId,
|
|
required this.phoneNumber,
|
|
this.passwordHash,
|
|
required this.fullName,
|
|
this.email,
|
|
required this.role,
|
|
required this.status,
|
|
required this.loyaltyTier,
|
|
required this.totalPoints,
|
|
this.companyInfo,
|
|
this.cccd,
|
|
this.attachments,
|
|
this.address,
|
|
this.avatarUrl,
|
|
this.referralCode,
|
|
this.referredBy,
|
|
this.erpnextCustomerId,
|
|
required this.createdAt,
|
|
this.updatedAt,
|
|
this.lastLoginAt,
|
|
});
|
|
|
|
/// User ID (Primary Key)
|
|
@HiveField(0)
|
|
final String userId;
|
|
|
|
/// Phone number (unique, used for login)
|
|
@HiveField(1)
|
|
final String phoneNumber;
|
|
|
|
/// Password hash (stored encrypted)
|
|
@HiveField(2)
|
|
final String? passwordHash;
|
|
|
|
/// Full name of the user
|
|
@HiveField(3)
|
|
final String fullName;
|
|
|
|
/// Email address
|
|
@HiveField(4)
|
|
final String? email;
|
|
|
|
/// User role (customer, distributor, admin, staff)
|
|
@HiveField(5)
|
|
final UserRole role;
|
|
|
|
/// Account status (active, inactive, suspended, pending)
|
|
@HiveField(6)
|
|
final UserStatus status;
|
|
|
|
/// Loyalty tier (bronze, silver, gold, platinum, diamond, titan)
|
|
@HiveField(7)
|
|
final LoyaltyTier loyaltyTier;
|
|
|
|
/// Total accumulated loyalty points
|
|
@HiveField(8)
|
|
final int totalPoints;
|
|
|
|
/// Company information (JSON encoded)
|
|
/// Contains: company_name, tax_id, business_type, etc.
|
|
@HiveField(9)
|
|
final String? companyInfo;
|
|
|
|
/// Citizen ID (CCCD/CMND)
|
|
@HiveField(10)
|
|
final String? cccd;
|
|
|
|
/// Attachments (JSON encoded list)
|
|
/// Contains: identity_card_images, business_license, etc.
|
|
@HiveField(11)
|
|
final String? attachments;
|
|
|
|
/// Address
|
|
@HiveField(12)
|
|
final String? address;
|
|
|
|
/// Avatar URL
|
|
@HiveField(13)
|
|
final String? avatarUrl;
|
|
|
|
/// Referral code for this user
|
|
@HiveField(14)
|
|
final String? referralCode;
|
|
|
|
/// ID of user who referred this user
|
|
@HiveField(15)
|
|
final String? referredBy;
|
|
|
|
/// ERPNext customer ID for integration
|
|
@HiveField(16)
|
|
final String? erpnextCustomerId;
|
|
|
|
/// Account creation timestamp
|
|
@HiveField(17)
|
|
final DateTime createdAt;
|
|
|
|
/// Last update timestamp
|
|
@HiveField(18)
|
|
final DateTime? updatedAt;
|
|
|
|
/// Last login timestamp
|
|
@HiveField(19)
|
|
final DateTime? lastLoginAt;
|
|
|
|
// =========================================================================
|
|
// JSON SERIALIZATION
|
|
// =========================================================================
|
|
|
|
/// Create UserModel from JSON
|
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
|
return UserModel(
|
|
userId: json['user_id'] as String,
|
|
phoneNumber: json['phone_number'] as String,
|
|
passwordHash: json['password_hash'] as String?,
|
|
fullName: json['full_name'] as String,
|
|
email: json['email'] as String?,
|
|
role: UserRole.values.firstWhere(
|
|
(e) => e.name == (json['role'] as String),
|
|
orElse: () => UserRole.customer,
|
|
),
|
|
status: UserStatus.values.firstWhere(
|
|
(e) => e.name == (json['status'] as String),
|
|
orElse: () => UserStatus.pending,
|
|
),
|
|
loyaltyTier: LoyaltyTier.values.firstWhere(
|
|
(e) => e.name == (json['loyalty_tier'] as String),
|
|
orElse: () => LoyaltyTier.bronze,
|
|
),
|
|
totalPoints: json['total_points'] as int? ?? 0,
|
|
companyInfo: json['company_info'] != null
|
|
? jsonEncode(json['company_info'])
|
|
: null,
|
|
cccd: json['cccd'] as String?,
|
|
attachments: json['attachments'] != null
|
|
? jsonEncode(json['attachments'])
|
|
: null,
|
|
address: json['address'] as String?,
|
|
avatarUrl: json['avatar_url'] as String?,
|
|
referralCode: json['referral_code'] as String?,
|
|
referredBy: json['referred_by'] as String?,
|
|
erpnextCustomerId: json['erpnext_customer_id'] as String?,
|
|
createdAt: DateTime.parse(json['created_at'] as String),
|
|
updatedAt: json['updated_at'] != null
|
|
? DateTime.parse(json['updated_at'] as String)
|
|
: null,
|
|
lastLoginAt: json['last_login_at'] != null
|
|
? DateTime.parse(json['last_login_at'] as String)
|
|
: null,
|
|
);
|
|
}
|
|
|
|
/// Convert UserModel to JSON
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'user_id': userId,
|
|
'phone_number': phoneNumber,
|
|
'password_hash': passwordHash,
|
|
'full_name': fullName,
|
|
'email': email,
|
|
'role': role.name,
|
|
'status': status.name,
|
|
'loyalty_tier': loyaltyTier.name,
|
|
'total_points': totalPoints,
|
|
'company_info': companyInfo != null ? jsonDecode(companyInfo!) : null,
|
|
'cccd': cccd,
|
|
'attachments': attachments != null ? jsonDecode(attachments!) : null,
|
|
'address': address,
|
|
'avatar_url': avatarUrl,
|
|
'referral_code': referralCode,
|
|
'referred_by': referredBy,
|
|
'erpnext_customer_id': erpnextCustomerId,
|
|
'created_at': createdAt.toIso8601String(),
|
|
'updated_at': updatedAt?.toIso8601String(),
|
|
'last_login_at': lastLoginAt?.toIso8601String(),
|
|
};
|
|
}
|
|
|
|
// =========================================================================
|
|
// HELPER METHODS
|
|
// =========================================================================
|
|
|
|
/// Get company info as Map
|
|
Map<String, dynamic>? get companyInfoMap {
|
|
if (companyInfo == null) return null;
|
|
try {
|
|
return jsonDecode(companyInfo!) as Map<String, dynamic>;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Get attachments as List
|
|
List<dynamic>? get attachmentsList {
|
|
if (attachments == null) return null;
|
|
try {
|
|
return jsonDecode(attachments!) as List<dynamic>;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Check if user is active
|
|
bool get isActive => status == UserStatus.active;
|
|
|
|
/// Check if user is admin or staff
|
|
bool get isStaff => role == UserRole.admin || role == UserRole.staff;
|
|
|
|
/// Get user initials for avatar
|
|
String get initials {
|
|
final parts = fullName.trim().split(' ');
|
|
if (parts.length >= 2) {
|
|
return '${parts.first[0]}${parts.last[0]}'.toUpperCase();
|
|
}
|
|
return fullName.isNotEmpty ? fullName[0].toUpperCase() : '?';
|
|
}
|
|
|
|
// =========================================================================
|
|
// COPY WITH
|
|
// =========================================================================
|
|
|
|
/// Create a copy with updated fields
|
|
UserModel copyWith({
|
|
String? userId,
|
|
String? phoneNumber,
|
|
String? passwordHash,
|
|
String? fullName,
|
|
String? email,
|
|
UserRole? role,
|
|
UserStatus? status,
|
|
LoyaltyTier? loyaltyTier,
|
|
int? totalPoints,
|
|
String? companyInfo,
|
|
String? cccd,
|
|
String? attachments,
|
|
String? address,
|
|
String? avatarUrl,
|
|
String? referralCode,
|
|
String? referredBy,
|
|
String? erpnextCustomerId,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
DateTime? lastLoginAt,
|
|
}) {
|
|
return UserModel(
|
|
userId: userId ?? this.userId,
|
|
phoneNumber: phoneNumber ?? this.phoneNumber,
|
|
passwordHash: passwordHash ?? this.passwordHash,
|
|
fullName: fullName ?? this.fullName,
|
|
email: email ?? this.email,
|
|
role: role ?? this.role,
|
|
status: status ?? this.status,
|
|
loyaltyTier: loyaltyTier ?? this.loyaltyTier,
|
|
totalPoints: totalPoints ?? this.totalPoints,
|
|
companyInfo: companyInfo ?? this.companyInfo,
|
|
cccd: cccd ?? this.cccd,
|
|
attachments: attachments ?? this.attachments,
|
|
address: address ?? this.address,
|
|
avatarUrl: avatarUrl ?? this.avatarUrl,
|
|
referralCode: referralCode ?? this.referralCode,
|
|
referredBy: referredBy ?? this.referredBy,
|
|
erpnextCustomerId: erpnextCustomerId ?? this.erpnextCustomerId,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'UserModel(userId: $userId, fullName: $fullName, role: $role, tier: $loyaltyTier, points: $totalPoints)';
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) return true;
|
|
|
|
return other is UserModel && other.userId == userId;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => userId.hashCode;
|
|
}
|