MO-Fuel-Tax-Back/lib/models/vehicle.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,
);
}