import 'receipt_item.dart'; import 'receipt_group.dart'; class LocalReceipt { final String id; final List groups; final String? destinationAccountId; final String? destinationAccountName; final DateTime transactionDate; final String? transactionDescription; // Deskripsi transaksi untuk tampilan di daftar // ID transaksi tunggal (format lama / fallback). Prefer [fireflyTransactionIds]. final String? fireflyTransactionId; final List fireflyTransactionIds; // ID per grup (format baru) final String? fireflyTransactionUrl; // URL utama (format lama) final List fireflyTransactionUrls; // URL per grup (format baru) final bool isSubmitted; final String? submissionError; final DateTime? submittedAt; final DateTime createdAt; final double paymentAmount; final String? tipSourceAccountId; // Akun sumber untuk transaksi tip (null = bukan tip) final String? tipSourceAccountName; final List tags; final String customerName; LocalReceipt({ required this.id, required this.groups, this.destinationAccountId, this.destinationAccountName, required this.transactionDate, this.transactionDescription, this.fireflyTransactionId, this.fireflyTransactionIds = const [], this.fireflyTransactionUrl, this.fireflyTransactionUrls = const [], this.isSubmitted = false, this.submissionError, this.submittedAt, required this.createdAt, this.paymentAmount = 0.0, this.tipSourceAccountId, this.tipSourceAccountName, this.tags = const [], this.customerName = '', }); double get total => groups.fold(0.0, (sum, group) => sum + group.total); bool get hasTip => tipSourceAccountId != null && paymentAmount > total; double get tipAmount => hasTip ? (paymentAmount - total) : 0.0; /// Gabungan semua URL transaksi (untuk tampilan daftar link). List get allFireflyUrls { final urls = [...fireflyTransactionUrls]; if (fireflyTransactionUrl != null && fireflyTransactionUrl!.isNotEmpty && !urls.contains(fireflyTransactionUrl)) { urls.insert(0, fireflyTransactionUrl!); } return urls; } Map toJson() { return { 'id': id, 'groups': groups.map((group) => group.toJson()).toList(), 'destinationAccountId': destinationAccountId, 'destinationAccountName': destinationAccountName, 'transactionDate': transactionDate.toIso8601String(), 'transactionDescription': transactionDescription, 'fireflyTransactionId': fireflyTransactionId, 'fireflyTransactionIds': fireflyTransactionIds, 'fireflyTransactionUrl': fireflyTransactionUrl, 'fireflyTransactionUrls': fireflyTransactionUrls, 'isSubmitted': isSubmitted, 'submissionError': submissionError, 'submittedAt': submittedAt?.toIso8601String(), 'createdAt': createdAt.toIso8601String(), 'paymentAmount': paymentAmount, 'tipSourceAccountId': tipSourceAccountId, 'tipSourceAccountName': tipSourceAccountName, 'tags': tags, 'customerName': customerName, }; } factory LocalReceipt.fromJson(Map json) { // Migrasi format lama: jika ada key "items" (list tunggal) tapi tidak ada "groups", // bungkus menjadi satu grup dengan akun sumber lama. List groups; if (json.containsKey('groups') && json['groups'] is List) { groups = (json['groups'] as List) .map((g) => ReceiptGroup.fromJson(g as Map)) .toList(); } else { // Format lama final items = (json['items'] as List? ?? []) .map((item) => ReceiptItem.fromJson(item as Map)) .toList(); groups = [ ReceiptGroup( id: 'legacy_${json['id'] ?? DateTime.now().millisecondsSinceEpoch}', items: items, sourceAccountId: json['sourceAccountId'] as String?, sourceAccountName: json['sourceAccountName'] as String?, ), ]; } // fireflyTransactionIds (format baru) - fallback ke ID tunggal lama final txIds = (json['fireflyTransactionIds'] as List?) ?.map((e) => e.toString()) .toList() ?? (json['fireflyTransactionId'] != null ? [json['fireflyTransactionId'].toString()] : []); // fireflyTransactionUrls (format baru) - fallback ke URL tunggal lama final txUrls = (json['fireflyTransactionUrls'] as List?) ?.map((e) => e.toString()) .toList() ?? (json['fireflyTransactionUrl'] != null ? [json['fireflyTransactionUrl'].toString()] : []); return LocalReceipt( id: json['id'], groups: groups, destinationAccountId: json['destinationAccountId'] as String?, destinationAccountName: json['destinationAccountName'] as String?, transactionDate: DateTime.parse(json['transactionDate']), transactionDescription: json['transactionDescription'], fireflyTransactionId: json['fireflyTransactionId'], fireflyTransactionIds: txIds, fireflyTransactionUrl: json['fireflyTransactionUrl'], fireflyTransactionUrls: txUrls, isSubmitted: json['isSubmitted'] ?? false, submissionError: json['submissionError'], submittedAt: json['submittedAt'] != null ? DateTime.parse(json['submittedAt']) : null, createdAt: DateTime.parse(json['createdAt']), paymentAmount: json['paymentAmount']?.toDouble() ?? 0.0, // Format lama pakai boolean isTip; tidak bisa dipulihkan menjadi akun sumber, // jadi tipSourceAccountId tetap null (user dapat set ulang sebelum kirim). tipSourceAccountId: json['tipSourceAccountId'] as String?, tipSourceAccountName: json['tipSourceAccountName'] as String?, tags: (json['tags'] as List?)?.map((e) => e.toString()).toList() ?? [], customerName: (json['customerName'] as String?) ?? '', ); } LocalReceipt copyWith({ String? id, List? groups, String? destinationAccountId, String? destinationAccountName, DateTime? transactionDate, String? transactionDescription, String? fireflyTransactionId, List? fireflyTransactionIds, String? fireflyTransactionUrl, List? fireflyTransactionUrls, bool? isSubmitted, String? submissionError, DateTime? submittedAt, DateTime? createdAt, double? paymentAmount, String? tipSourceAccountId, String? tipSourceAccountName, List? tags, String? customerName, }) { return LocalReceipt( id: id ?? this.id, groups: groups ?? this.groups, destinationAccountId: destinationAccountId ?? this.destinationAccountId, destinationAccountName: destinationAccountName ?? this.destinationAccountName, transactionDate: transactionDate ?? this.transactionDate, transactionDescription: transactionDescription ?? this.transactionDescription, fireflyTransactionId: fireflyTransactionId ?? this.fireflyTransactionId, fireflyTransactionIds: fireflyTransactionIds ?? this.fireflyTransactionIds, fireflyTransactionUrl: fireflyTransactionUrl ?? this.fireflyTransactionUrl, fireflyTransactionUrls: fireflyTransactionUrls ?? this.fireflyTransactionUrls, isSubmitted: isSubmitted ?? this.isSubmitted, submissionError: submissionError ?? this.submissionError, submittedAt: submittedAt ?? this.submittedAt, createdAt: createdAt ?? this.createdAt, paymentAmount: paymentAmount ?? this.paymentAmount, tipSourceAccountId: tipSourceAccountId ?? this.tipSourceAccountId, tipSourceAccountName: tipSourceAccountName ?? this.tipSourceAccountName, tags: tags ?? this.tags, customerName: customerName ?? this.customerName, ); } }