diff --git a/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart b/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart index c2e198ce..3ebfad03 100644 --- a/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart +++ b/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart @@ -10,6 +10,7 @@ class ClockInAdapter { DateTime? checkInTime, DateTime? checkOutTime, String? activeShiftId, + String? activeApplicationId, }) { final bool isCheckedIn = status == 'CHECKED_IN' || status == 'LATE'; // Assuming LATE is also checked in? @@ -21,6 +22,7 @@ class ClockInAdapter { checkInTime: checkInTime, checkOutTime: checkOutTime, activeShiftId: activeShiftId, + activeApplicationId: activeApplicationId, ); } } diff --git a/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart b/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart index db44377d..84acf58e 100644 --- a/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart +++ b/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart @@ -6,14 +6,22 @@ class AttendanceStatus extends Equatable { final DateTime? checkInTime; final DateTime? checkOutTime; final String? activeShiftId; + final String? activeApplicationId; const AttendanceStatus({ this.isCheckedIn = false, this.checkInTime, this.checkOutTime, this.activeShiftId, + this.activeApplicationId, }); @override - List get props => [isCheckedIn, checkInTime, checkOutTime, activeShiftId]; + List get props => [ + isCheckedIn, + checkInTime, + checkOutTime, + activeShiftId, + activeApplicationId, + ]; } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart index b078102d..ba1f14e8 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart @@ -9,6 +9,7 @@ import '../../domain/repositories/clock_in_repository_interface.dart'; class ClockInRepositoryImpl implements ClockInRepositoryInterface { final dc.ExampleConnector _dataConnect; final Map _shiftToApplicationId = {}; + String? _activeApplicationId; ClockInRepositoryImpl({ required dc.ExampleConnector dataConnect, @@ -187,16 +188,34 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface { return const AttendanceStatus(isCheckedIn: false); } - final dc.GetApplicationsByStaffIdApplications? activeApp = - _getActiveApplication(apps); - final dc.GetApplicationsByStaffIdApplications app = - activeApp ?? apps.last; + dc.GetApplicationsByStaffIdApplications? activeApp; + for (final app in apps) { + if (app.checkInTime != null && app.checkOutTime == null) { + if (activeApp == null) { + activeApp = app; + } else { + final DateTime? current = _toDateTime(activeApp.checkInTime); + final DateTime? next = _toDateTime(app.checkInTime); + if (current == null || (next != null && next.isAfter(current))) { + activeApp = app; + } + } + } + } - return ClockInAdapter.toAttendanceStatus( - status: app.status.stringValue, - checkInTime: _toDateTime(app.checkInTime), - checkOutTime: _toDateTime(app.checkOutTime), - activeShiftId: app.shiftId, + if (activeApp == null) { + _activeApplicationId = null; + return const AttendanceStatus(isCheckedIn: false); + } + + _activeApplicationId = activeApp.id; + print('Active check-in appId=$_activeApplicationId'); + return AttendanceStatus( + isCheckedIn: true, + checkInTime: _toDateTime(activeApp.checkInTime), + checkOutTime: _toDateTime(activeApp.checkOutTime), + activeShiftId: activeApp.shiftId, + activeApplicationId: activeApp.id, ); } @@ -227,6 +246,7 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface { ) .checkInTime(checkInTs) .execute(); + _activeApplicationId = app.id; } catch (e) { print('ClockIn updateApplicationStatus error: $e'); print('ClockIn error type: ${e.runtimeType}'); @@ -245,22 +265,43 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface { } @override - Future clockOut({String? notes, int? breakTimeMinutes}) async { + Future clockOut({ + String? notes, + int? breakTimeMinutes, + String? applicationId, + }) async { final String staffId = await _getStaffId(); - final List apps = - await _getTodaysApplications(staffId); - final dc.GetApplicationsByStaffIdApplications? app = - _getActiveApplication(apps); - if (app == null) throw Exception('No active shift found to clock out'); + print( + 'ClockOut request: applicationId=$applicationId ' + 'activeApplicationId=$_activeApplicationId', + ); + final String? targetAppId = applicationId ?? _activeApplicationId; + if (targetAppId == null || targetAppId.isEmpty) { + throw Exception('No active application id for checkout'); + } + final appResult = await _dataConnect + .getApplicationById(id: targetAppId) + .execute(); + final app = appResult.data.application; + print( + 'ClockOut getApplicationById: id=${app?.id} ' + 'checkIn=${app?.checkInTime?.toJson()} ' + 'checkOut=${app?.checkOutTime?.toJson()}', + ); + if (app == null) { + throw Exception('Application not found for checkout'); + } + if (app.checkInTime == null || app.checkOutTime != null) { + throw Exception('No active shift found to clock out'); + } await _dataConnect .updateApplicationStatus( - id: app.id, + id: targetAppId, ) - .status(dc.ApplicationStatus.CHECKED_OUT) - .checkOutTime(_fromDateTime(DateTime.now())) - .execute(); + .checkOutTime(_fromDateTime(DateTime.now())) + .execute(); return getAttendanceStatus(); } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/arguments/clock_out_arguments.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/arguments/clock_out_arguments.dart index 9b7fd324..04cb55fc 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/arguments/clock_out_arguments.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/arguments/clock_out_arguments.dart @@ -8,12 +8,16 @@ class ClockOutArguments extends UseCaseArgument { /// Optional break time in minutes. final int? breakTimeMinutes; + /// Optional application id for checkout. + final String? applicationId; + /// Creates a [ClockOutArguments] instance. const ClockOutArguments({ this.notes, this.breakTimeMinutes, + this.applicationId, }); @override - List get props => [notes, breakTimeMinutes]; + List get props => [notes, breakTimeMinutes, applicationId]; } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart index f495b65e..3d4795bd 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart @@ -17,5 +17,9 @@ abstract class ClockInRepositoryInterface { /// Checks the user out for the currently active shift. /// Optionally accepts [breakTimeMinutes] if tracked. - Future clockOut({String? notes, int? breakTimeMinutes}); + Future clockOut({ + String? notes, + int? breakTimeMinutes, + String? applicationId, + }); } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart index b4869818..f5b0b14a 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart @@ -14,6 +14,7 @@ class ClockOutUseCase implements UseCase { return _repository.clockOut( notes: arguments.notes, breakTimeMinutes: arguments.breakTimeMinutes, + applicationId: arguments.applicationId, ); } } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart index 968a7e6c..98c9a078 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart @@ -220,6 +220,7 @@ class ClockInBloc extends Bloc { ClockOutArguments( notes: event.notes, breakTimeMinutes: 0, // Should be passed from event if supported + applicationId: state.attendance.activeApplicationId, ), ); emit(state.copyWith(