144 lines
4.9 KiB
Dart
144 lines
4.9 KiB
Dart
|
|
// lib/models/report_transaction.dart
|
||
|
|
|
||
|
|
class ReportTransaction {
|
||
|
|
final String id;
|
||
|
|
DateTime date;
|
||
|
|
String description;
|
||
|
|
double amount;
|
||
|
|
final String direction; // 'pemasukan' atau 'pengeluaran'
|
||
|
|
final String counterpartyName; // Nama akun lawan atau keterangan
|
||
|
|
List<String> tags;
|
||
|
|
|
||
|
|
// Metadata Firefly III untuk keperluan sinkronisasi/edit
|
||
|
|
String? transactionGroupId;
|
||
|
|
String? transactionJournalId;
|
||
|
|
|
||
|
|
// File attachments lokal untuk keperluan SPJ/Laporan
|
||
|
|
String? fotoBarangPath;
|
||
|
|
String? fotoBarangName;
|
||
|
|
String? invoicePath;
|
||
|
|
String? invoiceName;
|
||
|
|
String? invoiceType; // 'pdf' atau 'image'
|
||
|
|
String? invoiceSnapshotPath; // Path gambar PNG hasil render halaman PDF invoice
|
||
|
|
|
||
|
|
ReportTransaction({
|
||
|
|
required this.id,
|
||
|
|
required this.date,
|
||
|
|
required this.description,
|
||
|
|
required this.amount,
|
||
|
|
required this.direction,
|
||
|
|
required this.counterpartyName,
|
||
|
|
this.tags = const [],
|
||
|
|
this.transactionGroupId,
|
||
|
|
this.transactionJournalId,
|
||
|
|
this.fotoBarangPath,
|
||
|
|
this.fotoBarangName,
|
||
|
|
this.invoicePath,
|
||
|
|
this.invoiceName,
|
||
|
|
this.invoiceType,
|
||
|
|
this.invoiceSnapshotPath,
|
||
|
|
});
|
||
|
|
|
||
|
|
bool get isPengeluaran => direction == 'pengeluaran';
|
||
|
|
bool get isPemasukan => direction == 'pemasukan';
|
||
|
|
|
||
|
|
bool get hasFotoBarang => fotoBarangPath != null && fotoBarangPath!.isNotEmpty;
|
||
|
|
bool get hasInvoice => invoicePath != null && invoicePath!.isNotEmpty;
|
||
|
|
bool get hasInvoiceSnapshot =>
|
||
|
|
invoiceSnapshotPath != null && invoiceSnapshotPath!.isNotEmpty;
|
||
|
|
|
||
|
|
/// Pengeluaran wajib foto barang & invoice. Pemasukan selalu dianggap lengkap.
|
||
|
|
bool get isComplete => !isPengeluaran || (hasFotoBarang && hasInvoice);
|
||
|
|
|
||
|
|
/// Status badge attachment untuk pengeluaran: 'complete', 'partial', atau 'missing'
|
||
|
|
String get attachmentStatus {
|
||
|
|
if (!isPengeluaran) return 'not_required';
|
||
|
|
if (hasFotoBarang && hasInvoice) return 'complete';
|
||
|
|
if (hasFotoBarang || hasInvoice) return 'partial';
|
||
|
|
return 'missing';
|
||
|
|
}
|
||
|
|
|
||
|
|
ReportTransaction copyWith({
|
||
|
|
String? id,
|
||
|
|
DateTime? date,
|
||
|
|
String? description,
|
||
|
|
double? amount,
|
||
|
|
String? direction,
|
||
|
|
String? counterpartyName,
|
||
|
|
List<String>? tags,
|
||
|
|
String? transactionGroupId,
|
||
|
|
String? transactionJournalId,
|
||
|
|
String? fotoBarangPath,
|
||
|
|
String? fotoBarangName,
|
||
|
|
String? invoicePath,
|
||
|
|
String? invoiceName,
|
||
|
|
String? invoiceType,
|
||
|
|
String? invoiceSnapshotPath,
|
||
|
|
}) {
|
||
|
|
return ReportTransaction(
|
||
|
|
id: id ?? this.id,
|
||
|
|
date: date ?? this.date,
|
||
|
|
description: description ?? this.description,
|
||
|
|
amount: amount ?? this.amount,
|
||
|
|
direction: direction ?? this.direction,
|
||
|
|
counterpartyName: counterpartyName ?? this.counterpartyName,
|
||
|
|
tags: tags ?? List<String>.from(this.tags),
|
||
|
|
transactionGroupId: transactionGroupId ?? this.transactionGroupId,
|
||
|
|
transactionJournalId: transactionJournalId ?? this.transactionJournalId,
|
||
|
|
fotoBarangPath: fotoBarangPath ?? this.fotoBarangPath,
|
||
|
|
fotoBarangName: fotoBarangName ?? this.fotoBarangName,
|
||
|
|
invoicePath: invoicePath ?? this.invoicePath,
|
||
|
|
invoiceName: invoiceName ?? this.invoiceName,
|
||
|
|
invoiceType: invoiceType ?? this.invoiceType,
|
||
|
|
invoiceSnapshotPath: invoiceSnapshotPath ?? this.invoiceSnapshotPath,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
factory ReportTransaction.fromJson(Map<String, dynamic> json) {
|
||
|
|
List<String> parsedTags = [];
|
||
|
|
if (json['tags'] is List) {
|
||
|
|
parsedTags = (json['tags'] as List).map((e) => e.toString()).toList();
|
||
|
|
}
|
||
|
|
|
||
|
|
return ReportTransaction(
|
||
|
|
id: json['id']?.toString() ?? '',
|
||
|
|
date: DateTime.tryParse(json['date']?.toString() ?? '') ?? DateTime.now(),
|
||
|
|
description: json['description']?.toString() ?? '-',
|
||
|
|
amount: (json['amount'] is num)
|
||
|
|
? (json['amount'] as num).toDouble()
|
||
|
|
: double.tryParse(json['amount']?.toString() ?? '0') ?? 0.0,
|
||
|
|
direction: json['direction']?.toString() ?? 'pengeluaran',
|
||
|
|
counterpartyName: json['counterpartyName']?.toString() ?? '-',
|
||
|
|
tags: parsedTags,
|
||
|
|
transactionGroupId: json['transactionGroupId']?.toString(),
|
||
|
|
transactionJournalId: json['transactionJournalId']?.toString(),
|
||
|
|
fotoBarangPath: json['fotoBarangPath']?.toString(),
|
||
|
|
fotoBarangName: json['fotoBarangName']?.toString(),
|
||
|
|
invoicePath: json['invoicePath']?.toString(),
|
||
|
|
invoiceName: json['invoiceName']?.toString(),
|
||
|
|
invoiceType: json['invoiceType']?.toString(),
|
||
|
|
invoiceSnapshotPath: json['invoiceSnapshotPath']?.toString(),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Map<String, dynamic> toJson() {
|
||
|
|
return {
|
||
|
|
'id': id,
|
||
|
|
'date': date.toIso8601String(),
|
||
|
|
'description': description,
|
||
|
|
'amount': amount,
|
||
|
|
'direction': direction,
|
||
|
|
'counterpartyName': counterpartyName,
|
||
|
|
'tags': tags,
|
||
|
|
'transactionGroupId': transactionGroupId,
|
||
|
|
'transactionJournalId': transactionJournalId,
|
||
|
|
'fotoBarangPath': fotoBarangPath,
|
||
|
|
'fotoBarangName': fotoBarangName,
|
||
|
|
'invoicePath': invoicePath,
|
||
|
|
'invoiceName': invoiceName,
|
||
|
|
'invoiceType': invoiceType,
|
||
|
|
'invoiceSnapshotPath': invoiceSnapshotPath,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|