96 lines
2.5 KiB
Dart
96 lines
2.5 KiB
Dart
enum ShiftStatus { open, booked, active, completed, cancelled, noShow }
|
|
|
|
extension ShiftStatusX on ShiftStatus {
|
|
String get label => switch (this) {
|
|
ShiftStatus.open => 'Open',
|
|
ShiftStatus.booked => 'Confirmed',
|
|
ShiftStatus.active => 'In progress',
|
|
ShiftStatus.completed => 'Completed',
|
|
ShiftStatus.cancelled => 'Cancelled',
|
|
ShiftStatus.noShow => 'No show',
|
|
};
|
|
}
|
|
|
|
class Shift {
|
|
Shift({
|
|
required this.id,
|
|
required this.role,
|
|
required this.business,
|
|
required this.venue,
|
|
required this.address,
|
|
required this.start,
|
|
required this.end,
|
|
required this.rate,
|
|
required this.status,
|
|
this.breakMinutes = 30,
|
|
this.distanceMiles = 2.4,
|
|
this.manager,
|
|
this.clockIn,
|
|
this.clockOut,
|
|
this.requirements = const [],
|
|
});
|
|
|
|
final String id;
|
|
final String role;
|
|
final String business;
|
|
final String venue;
|
|
final String address;
|
|
final DateTime start;
|
|
final DateTime end;
|
|
final double rate;
|
|
final ShiftStatus status;
|
|
final int breakMinutes;
|
|
final double distanceMiles;
|
|
final String? manager;
|
|
final DateTime? clockIn;
|
|
final DateTime? clockOut;
|
|
final List<String> requirements;
|
|
|
|
/// Paid duration: the scheduled window less the unpaid break.
|
|
Duration get scheduledDuration =>
|
|
end.difference(start) - Duration(minutes: breakMinutes);
|
|
|
|
/// What was actually worked, once clocked out.
|
|
Duration? get workedDuration {
|
|
if (clockIn == null || clockOut == null) return null;
|
|
final d = clockOut!.difference(clockIn!) - Duration(minutes: breakMinutes);
|
|
return d.isNegative ? Duration.zero : d;
|
|
}
|
|
|
|
double get scheduledPay => rate * scheduledDuration.inMinutes / 60;
|
|
|
|
/// Gross, pre-tax. Falls back to the scheduled figure until clocked out.
|
|
double get pay {
|
|
if (status == ShiftStatus.cancelled || status == ShiftStatus.noShow) {
|
|
return 0;
|
|
}
|
|
final worked = workedDuration;
|
|
if (worked != null) return rate * worked.inMinutes / 60;
|
|
return scheduledPay;
|
|
}
|
|
|
|
String get logoLetter => business.isEmpty ? '?' : business[0].toUpperCase();
|
|
|
|
Shift copyWith({
|
|
ShiftStatus? status,
|
|
DateTime? clockIn,
|
|
DateTime? clockOut,
|
|
}) => Shift(
|
|
id: id,
|
|
role: role,
|
|
business: business,
|
|
venue: venue,
|
|
address: address,
|
|
start: start,
|
|
end: end,
|
|
rate: rate,
|
|
status: status ?? this.status,
|
|
breakMinutes: breakMinutes,
|
|
distanceMiles: distanceMiles,
|
|
manager: manager,
|
|
clockIn: clockIn ?? this.clockIn,
|
|
clockOut: clockOut ?? this.clockOut,
|
|
requirements: requirements,
|
|
);
|
|
}
|