79 lines
2.7 KiB
Dart
79 lines
2.7 KiB
Dart
class Vehicle {
|
|
/// Hidden, immutable, generated primary key — never shown in the UI and
|
|
/// never editable. This is what fuel entries actually reference and what
|
|
/// sync merges on, so that [vin] itself is free to be edited without
|
|
/// breaking those references or losing sync history.
|
|
final String id;
|
|
|
|
/// The vehicle's identification number. Required and must be unique
|
|
/// among active (non-deleted) vehicles, but — unlike [id] — the user can
|
|
/// edit it later (e.g. to fix a typo from a misread VIN scan).
|
|
final String vin;
|
|
|
|
/// User-chosen label, e.g. "Mom's Car" or "Red Ford F-150". Optional —
|
|
/// when absent, screens fall back to other identifying info instead.
|
|
final String? nickname;
|
|
|
|
final DateTime updatedAt;
|
|
|
|
/// Soft-delete tombstone: null means active. Deleting sets this instead
|
|
/// of removing the row, so the deletion itself can be merged/synced like
|
|
/// any other change (newest `updatedAt` wins) instead of silently
|
|
/// disappearing and later being resurrected by a device that hasn't seen
|
|
/// the deletion yet.
|
|
final DateTime? deletedAt;
|
|
|
|
Vehicle({
|
|
required this.id,
|
|
required this.vin,
|
|
required this.updatedAt,
|
|
this.nickname,
|
|
this.deletedAt,
|
|
});
|
|
|
|
/// What screens should show as the vehicle's primary label: the nickname
|
|
/// if one was given, otherwise the VIN itself.
|
|
String get displayLabel {
|
|
final trimmedNickname = nickname?.trim();
|
|
if (trimmedNickname != null && trimmedNickname.isNotEmpty) {
|
|
return trimmedNickname;
|
|
}
|
|
return vin;
|
|
}
|
|
|
|
/// Note: [id] is intentionally not overridable here — it's the hidden
|
|
/// identifier, not an editable field.
|
|
Vehicle copyWith({
|
|
String? vin,
|
|
String? nickname,
|
|
bool clearNickname = false,
|
|
DateTime? updatedAt,
|
|
DateTime? deletedAt,
|
|
}) {
|
|
return Vehicle(
|
|
id: id,
|
|
vin: vin ?? this.vin,
|
|
nickname: clearNickname ? null : (nickname ?? this.nickname),
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
deletedAt: deletedAt ?? this.deletedAt,
|
|
);
|
|
}
|
|
|
|
Map<String, Object?> toMap() => {
|
|
'id': id,
|
|
'vin': vin,
|
|
'nickname': nickname,
|
|
'updated_at': updatedAt.millisecondsSinceEpoch,
|
|
'deleted_at': deletedAt?.millisecondsSinceEpoch,
|
|
};
|
|
|
|
factory Vehicle.fromMap(Map<String, Object?> map) => Vehicle(
|
|
id: map['id'] as String,
|
|
vin: map['vin'] as String,
|
|
nickname: map['nickname'] 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,
|
|
);
|
|
}
|