class Category { final int id; final String name; final String? iconName; Category({required this.id, required this.name, this.iconName}); factory Category.fromJson(Map json) { return Category( id: json['id'], name: json['name'], iconName: json['iconName'], ); } Map toJson() => {'name': name, 'iconName': iconName}; } class TransactionItem { final int? id; final String name; final double amount; TransactionItem({this.id, required this.name, required this.amount}); factory TransactionItem.fromJson(Map json) { return TransactionItem( id: json['id'], name: json['name'], amount: (json['amount'] is int) ? (json['amount'] as int).toDouble() : json['amount'], ); } Map toJson() => { 'name': name, 'amount': amount, }; } class TransactionAttachment { final int id; final String fileName; final String filePath; final String contentType; TransactionAttachment({required this.id, required this.fileName, required this.filePath, required this.contentType}); factory TransactionAttachment.fromJson(Map json) { return TransactionAttachment( id: json['id'], fileName: json['fileName'], filePath: json['filePath'], contentType: json['contentType'], ); } } class Transaction { final int id; final int? categoryId; final int? fromWalletId; final int? toWalletId; final String? type; // 'INCOME' or 'EXPENSE' or 'INVESTMENT' (kept for backward compatibility during migration) final double amount; final DateTime date; final String? description; final String? notes; final String? investmentStatus; final double? maturityAmount; final double? profitLoss; final DateTime? closingDate; final DateTime? dueDate; final String? alertSchedule; final String? alertTime; final List? items; final List? attachments; Transaction({ required this.id, this.categoryId, this.fromWalletId, this.toWalletId, this.type, required this.amount, required this.date, this.description, this.notes, this.investmentStatus, this.maturityAmount, this.profitLoss, this.closingDate, this.dueDate, this.alertSchedule, this.alertTime, this.items, this.attachments, }); factory Transaction.fromJson(Map json) { return Transaction( id: json['id'], categoryId: json['categoryId'], fromWalletId: json['fromWalletId'], toWalletId: json['toWalletId'], type: json['type'], amount: (json['amount'] is int) ? (json['amount'] as int).toDouble() : json['amount'], date: DateTime.parse(json['date']), description: json['description'], notes: json['notes'], investmentStatus: json['investmentStatus'], maturityAmount: json['maturityAmount'] != null ? ((json['maturityAmount'] is int) ? (json['maturityAmount'] as int).toDouble() : json['maturityAmount']) : null, profitLoss: json['profitLoss'] != null ? ((json['profitLoss'] is int) ? (json['profitLoss'] as int).toDouble() : json['profitLoss']) : null, closingDate: json['closingDate'] != null ? DateTime.parse(json['closingDate']) : null, dueDate: json['dueDate'] != null ? DateTime.parse(json['dueDate']) : null, alertSchedule: json['alertSchedule'], alertTime: json['alertTime'], items: json['items'] != null ? (json['items'] as List).map((i) => TransactionItem.fromJson(i)).toList() : null, attachments: json['attachments'] != null ? (json['attachments'] as List).map((i) => TransactionAttachment.fromJson(i)).toList() : null, ); } Map toJson() { final map = { 'categoryId': categoryId, 'fromWalletId': fromWalletId, 'toWalletId': toWalletId, 'type': type, 'amount': amount, 'date': date.toIso8601String().split('T')[0], 'description': description, 'notes': notes, 'investmentStatus': investmentStatus, 'maturityAmount': maturityAmount, 'profitLoss': profitLoss, 'closingDate': closingDate?.toIso8601String().split('T')[0], 'dueDate': dueDate?.toIso8601String().split('T')[0], 'alertSchedule': alertSchedule, 'alertTime': alertTime, }; if (items != null) { map['items'] = items!.map((i) => i.toJson()).toList(); } return map; } } class Budget { final int id; final int? categoryId; final int? walletId; final double monthlyLimit; final bool isShared; Budget({required this.id, this.categoryId, this.walletId, required this.monthlyLimit, this.isShared = false}); factory Budget.fromJson(Map json) { double limit = 0.0; if (json['monthlyLimit'] != null) { if (json['monthlyLimit'] is num) { limit = (json['monthlyLimit'] as num).toDouble(); } else if (json['monthlyLimit'] is String) { limit = double.tryParse(json['monthlyLimit']) ?? 0.0; } } int parseId(dynamic val, [int defaultVal = 0]) { if (val == null) return defaultVal; if (val is int) return val; if (val is num) return val.toInt(); if (val is String) return int.tryParse(val) ?? double.tryParse(val)?.toInt() ?? defaultVal; return defaultVal; } return Budget( id: parseId(json['id']), categoryId: json['categoryId'] != null ? parseId(json['categoryId']) : null, walletId: json['walletId'] != null ? parseId(json['walletId']) : null, monthlyLimit: limit, isShared: json['isShared'] ?? false, ); } Map toJson() => { 'categoryId': categoryId, 'walletId': walletId, 'monthlyLimit': monthlyLimit, 'isShared': isShared, }; } class RecurringTransaction { final int id; final int? categoryId; final int? fromWalletId; final int? toWalletId; final String type; final double amount; final String frequency; // DAILY, WEEKLY, MONTHLY final DateTime? nextExecutionDate; final DateTime? endDate; final String? status; final String? description; RecurringTransaction({ required this.id, this.categoryId, this.fromWalletId, this.toWalletId, required this.type, required this.amount, required this.frequency, this.nextExecutionDate, this.endDate, this.status, this.description, }); factory RecurringTransaction.fromJson(Map json) { return RecurringTransaction( id: json['id'], categoryId: json['categoryId'], fromWalletId: json['fromWalletId'], toWalletId: json['toWalletId'], type: json['type'], amount: (json['amount'] is int) ? (json['amount'] as int).toDouble() : json['amount'], frequency: json['frequency'], nextExecutionDate: json['nextExecutionDate'] != null ? DateTime.parse(json['nextExecutionDate']) : null, endDate: json['endDate'] != null ? DateTime.parse(json['endDate']) : null, status: json['status'], description: json['description'], ); } Map toJson() { return { 'id': id, 'categoryId': categoryId, 'fromWalletId': fromWalletId, 'toWalletId': toWalletId, 'type': type, 'amount': amount, 'frequency': frequency, 'nextExecutionDate': nextExecutionDate?.toIso8601String(), 'endDate': endDate?.toIso8601String(), 'status': status ?? 'ACTIVE', 'description': description, }; } } class Wallet { final int id; final String name; final int ownerId; final String? nature; final double balance; final String? currency; final String? icon; final String? color; Wallet({ required this.id, required this.name, required this.ownerId, this.nature, this.balance = 0.0, this.currency, this.icon, this.color, }); factory Wallet.fromJson(Map json) { int parseId(dynamic val, [int defaultVal = 0]) { if (val == null) return defaultVal; if (val is int) return val; if (val is num) return val.toInt(); if (val is String) return int.tryParse(val) ?? double.tryParse(val)?.toInt() ?? defaultVal; return defaultVal; } double parseDouble(dynamic val, [double defaultVal = 0.0]) { if (val == null) return defaultVal; if (val is num) return val.toDouble(); if (val is String) return double.tryParse(val) ?? defaultVal; return defaultVal; } return Wallet( id: parseId(json['id']), name: json['name'], ownerId: parseId(json['ownerId']), nature: json['nature'], balance: parseDouble(json['balance']), currency: json['currency'], icon: json['icon'], color: json['color'], ); } Map toJson() => { 'name': name, 'nature': nature, 'currency': currency, 'initialBalance': balance, // Note: backend uses initialBalance on creation 'icon': icon, 'color': color, }; } class WalletInvitation { final int id; final int walletId; final int inviterId; final String inviteeEmail; final String status; final String createdAt; WalletInvitation({ required this.id, required this.walletId, required this.inviterId, required this.inviteeEmail, required this.status, required this.createdAt, }); factory WalletInvitation.fromJson(Map json) { return WalletInvitation( id: json['id'], walletId: json['walletId'], inviterId: json['inviterId'], inviteeEmail: json['inviteeEmail'], status: json['status'], createdAt: json['createdAt'] ?? '', ); } } class WalletMember { final int userId; final String email; final String role; final String joinedAt; WalletMember({ required this.userId, required this.email, required this.role, required this.joinedAt, }); factory WalletMember.fromJson(Map json) { return WalletMember( userId: json['userId'], email: json['email'], role: json['role'], joinedAt: json['joinedAt'] ?? '', ); } }