class FuelEntry { final String id; final String vehicleId; final DateTime date; final double gallons; final double pricePerGallon; final double totalCost; final String? receiptImagePath; final String? receiptDriveFileId; final DateTime updatedAt; /// Soft-delete tombstone: null means active. See [Vehicle.deletedAt] for /// why this is a flag rather than an actual row deletion. final DateTime? deletedAt; FuelEntry({ required this.id, required this.vehicleId, required this.date, required this.gallons, required this.pricePerGallon, required this.totalCost, required this.updatedAt, this.receiptImagePath, this.receiptDriveFileId, this.deletedAt, }); /// True once the receipt photo has been uploaded to Drive and the local /// copy removed. Viewing it then requires downloading it on demand. bool get isReceiptUploadedToDrive => receiptImagePath == null && receiptDriveFileId != null; /// True while a receipt photo exists only locally and still needs to be /// uploaded next sync. A row in this state is never pushed to Drive as-is /// (a local file path is meaningless on another device) — sync uploads /// the image first, which clears this. bool get hasPendingLocalReceipt => receiptImagePath != null; FuelEntry copyWith({ String? receiptImagePath, String? receiptDriveFileId, DateTime? updatedAt, DateTime? deletedAt, bool clearReceiptImagePath = false, }) { return FuelEntry( id: id, vehicleId: vehicleId, date: date, gallons: gallons, pricePerGallon: pricePerGallon, totalCost: totalCost, updatedAt: updatedAt ?? this.updatedAt, receiptImagePath: clearReceiptImagePath ? null : (receiptImagePath ?? this.receiptImagePath), receiptDriveFileId: receiptDriveFileId ?? this.receiptDriveFileId, deletedAt: deletedAt ?? this.deletedAt, ); } Map toMap() => { 'id': id, 'vehicle_id': vehicleId, 'date': date.millisecondsSinceEpoch, 'gallons': gallons, 'price_per_gallon': pricePerGallon, 'total_cost': totalCost, 'receipt_image_path': receiptImagePath, 'receipt_drive_file_id': receiptDriveFileId, 'updated_at': updatedAt.millisecondsSinceEpoch, 'deleted_at': deletedAt?.millisecondsSinceEpoch, }; factory FuelEntry.fromMap(Map map) => FuelEntry( id: map['id'] as String, vehicleId: map['vehicle_id'] as String, date: DateTime.fromMillisecondsSinceEpoch(map['date'] as int), gallons: (map['gallons'] as num).toDouble(), pricePerGallon: (map['price_per_gallon'] as num).toDouble(), totalCost: (map['total_cost'] as num).toDouble(), receiptImagePath: map['receipt_image_path'] as String?, receiptDriveFileId: map['receipt_drive_file_id'] as String?, updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updated_at'] as int, isUtc: true), deletedAt: map['deleted_at'] != null ? DateTime.fromMillisecondsSinceEpoch(map['deleted_at'] as int, isUtc: true) : null, ); }