attendence records, extra hours , rewards and punishment funnctionality have been added

This commit is contained in:
Daniah Ayad Al-sultani
2026-02-10 16:27:08 +03:00
parent cd7ba8e9d5
commit 1002937045
25 changed files with 1048 additions and 181 deletions

View File

@@ -3,6 +3,10 @@ import 'package:dio/dio.dart';
import '../../core/error/exceptions.dart';
import '../../core/network/api_client.dart';
import '../dto/attendance_response_dto.dart';
import '../dto/attendance_record_dto.dart';
import '../dto/overtime_dto.dart';
import '../dto/reward_dto.dart';
import '../dto/punishment_dto.dart';
abstract class AttendanceRemoteDataSource {
Future<AttendanceResponseDto> login({
@@ -14,6 +18,13 @@ abstract class AttendanceRemoteDataSource {
required String employeeId,
required File faceImage,
});
Future<List<AttendanceRecordDto>> getAttendanceRecords({
required String employeeId,
});
Future<List<OvertimeDto>> getExtraHours({required String employeeId});
Future<List<RewardDto>> getRewards({required String employeeId});
Future<List<PunishmentDto>> getPunishments({required String employeeId});
}
class AttendanceRemoteDataSourceImpl implements AttendanceRemoteDataSource {
@@ -140,9 +151,138 @@ class AttendanceRemoteDataSourceImpl implements AttendanceRemoteDataSource {
throw NetworkException(message: 'خطأ في الانترنيت يرجى المحاولة لاحقا');
}
} catch (e) {
if (e is ServerException || e is NetworkException) {
rethrow;
throw ServerException(message: 'خطأ غير متوقع');
}
}
@override
Future<List<AttendanceRecordDto>> getAttendanceRecords({
required String employeeId,
}) async {
try {
final response = await apiClient.get(
'/Attendance',
queryParameters: {'IsDeleted': false, 'EmployeeId': employeeId},
);
if (response.statusCode == 200) {
final data = response.data;
if (data is Map<String, dynamic> &&
data['data'] != null &&
data['data']['items'] is List) {
final items = data['data']['items'] as List;
return items.map((e) => AttendanceRecordDto.fromJson(e)).toList();
}
return [];
} else {
throw ServerException(
message: 'فشل في جلب البيانات',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw ServerException(
message: e.message ?? 'Unknown error',
statusCode: e.response?.statusCode,
);
} catch (e) {
throw ServerException(message: 'خطأ غير متوقع');
}
}
@override
Future<List<OvertimeDto>> getExtraHours({required String employeeId}) async {
try {
final response = await apiClient.get(
'/ExtraHours',
queryParameters: {'IsDeleted': false, 'EmployeeId': employeeId},
);
if (response.statusCode == 200) {
final responseData = response.data;
if (responseData is Map<String, dynamic>) {
return OvertimeListResponseDto.fromJson(responseData).items;
} else {
throw ServerException(
message: 'استجابة غير صحيحة من الخادم',
statusCode: response.statusCode,
);
}
} else {
throw ServerException(
message: 'فشل في جلب البيانات',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw ServerException(
message: e.message ?? 'Unknown error',
statusCode: e.response?.statusCode,
);
} catch (e) {
throw ServerException(message: 'خطأ غير متوقع');
}
}
@override
Future<List<RewardDto>> getRewards({required String employeeId}) async {
try {
final response = await apiClient.get(
'/Reward',
queryParameters: {'IsDeleted': false, 'EmployeeId': employeeId},
);
if (response.statusCode == 200) {
final responseData = response.data;
if (responseData is Map<String, dynamic>) {
return RewardListResponseDto.fromJson(responseData).items;
}
return [];
} else {
throw ServerException(
message: 'فشل في جلب المكافآت',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw ServerException(
message: e.message ?? 'Unknown error',
statusCode: e.response?.statusCode,
);
} catch (e) {
throw ServerException(message: 'خطأ غير متوقع');
}
}
@override
Future<List<PunishmentDto>> getPunishments({
required String employeeId,
}) async {
try {
final response = await apiClient.get(
'/Punishment',
queryParameters: {'IsDeleted': false, 'EmployeeId': employeeId},
);
if (response.statusCode == 200) {
final responseData = response.data;
if (responseData is Map<String, dynamic>) {
return PunishmentListResponseDto.fromJson(responseData).items;
}
return [];
} else {
throw ServerException(
message: 'فشل في جلب البيانات',
statusCode: response.statusCode,
);
}
} on DioException catch (e) {
throw ServerException(
message: e.message ?? 'Unknown error',
statusCode: e.response?.statusCode,
);
} catch (e) {
throw ServerException(message: 'خطأ غير متوقع');
}
}

View File

@@ -0,0 +1,45 @@
class AttendanceRecordDto {
final String id;
final String employeeId;
final DateTime? login;
final DateTime? logout;
final String? reason;
final DateTime? createdAt;
final bool isDeleted;
AttendanceRecordDto({
required this.id,
required this.employeeId,
this.login,
this.logout,
this.reason,
this.createdAt,
required this.isDeleted,
});
factory AttendanceRecordDto.fromJson(Map<String, dynamic> json) {
return AttendanceRecordDto(
id: json['id']?.toString() ?? '',
employeeId: json['employeeId']?.toString() ?? '',
login: _parseDateTime(json['login']),
logout: _parseDateTime(json['logout']),
reason: json['reason'],
createdAt: _parseDateTime(json['createdAt']),
isDeleted: json['isDeleted'] ?? false,
);
}
static DateTime? _parseDateTime(dynamic value) {
if (value == null) return null;
if (value is DateTime) return value;
if (value is String) {
if (value.isEmpty) return null;
try {
return DateTime.parse(value);
} catch (e) {
return null; // Handle parse error gracefully
}
}
return null;
}
}

View File

@@ -0,0 +1,49 @@
class ExtraPaymentDto {
final String id;
final DateTime date;
final double amount;
final String? reason;
final String? note;
final String employeeId;
final bool isDeleted;
ExtraPaymentDto({
required this.id,
required this.date,
required this.amount,
this.reason,
this.note,
required this.employeeId,
required this.isDeleted,
});
factory ExtraPaymentDto.fromJson(Map<String, dynamic> json) {
return ExtraPaymentDto(
id: json['id']?.toString() ?? '',
date:
json['date'] != null ? DateTime.parse(json['date']) : DateTime.now(),
amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
reason: json['reason'],
note: json['note'],
employeeId: json['employeeId']?.toString() ?? '',
isDeleted: json['isDeleted'] ?? false,
);
}
}
class ExtraPaymentListResponseDto {
final List<ExtraPaymentDto> items;
ExtraPaymentListResponseDto({required this.items});
factory ExtraPaymentListResponseDto.fromJson(Map<String, dynamic> json) {
final data = json['data'];
if (data == null || data is! Map<String, dynamic>) {
return ExtraPaymentListResponseDto(items: []);
}
final itemsJson = data['items'] as List? ?? [];
return ExtraPaymentListResponseDto(
items: itemsJson.map((e) => ExtraPaymentDto.fromJson(e)).toList(),
);
}
}

View File

@@ -0,0 +1,46 @@
class OvertimeDto {
final String id;
final DateTime date;
final double hours;
final double hourlyRateAtTheTime;
final String employeeId;
final bool isDeleted;
OvertimeDto({
required this.id,
required this.employeeId,
required this.date,
required this.hours,
required this.hourlyRateAtTheTime,
required this.isDeleted,
});
factory OvertimeDto.fromJson(Map<String, dynamic> json) {
return OvertimeDto(
id: json['id']?.toString() ?? '',
date: DateTime.parse(json['date']),
hours: (json['hours'] as num?)?.toDouble() ?? 0.0,
hourlyRateAtTheTime:
(json['hourlyRateAtTheTime'] as num?)?.toDouble() ?? 0.0,
employeeId: json['employeeId']?.toString() ?? '',
isDeleted: json['isDeleted'] ?? false,
);
}
}
class OvertimeListResponseDto {
final List<OvertimeDto> items;
OvertimeListResponseDto({required this.items});
factory OvertimeListResponseDto.fromJson(Map<String, dynamic> json) {
final data = json['data'];
if (data == null || data is! Map<String, dynamic>) {
return OvertimeListResponseDto(items: []);
}
final itemsJson = data['items'] as List? ?? [];
return OvertimeListResponseDto(
items: itemsJson.map((e) => OvertimeDto.fromJson(e)).toList(),
);
}
}

View File

@@ -0,0 +1,46 @@
class PunishmentDto {
final String id;
final DateTime date;
final double amount;
final String? reason;
final String? note;
final String employeeId;
PunishmentDto({
required this.id,
required this.date,
required this.amount,
this.reason,
this.note,
required this.employeeId,
});
factory PunishmentDto.fromJson(Map<String, dynamic> json) {
return PunishmentDto(
id: json['id']?.toString() ?? '',
date:
json['date'] != null ? DateTime.parse(json['date']) : DateTime.now(),
amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
reason: json['reason'],
note: json['note'],
employeeId: json['employeeId']?.toString() ?? '',
);
}
}
class PunishmentListResponseDto {
final List<PunishmentDto> items;
PunishmentListResponseDto({required this.items});
factory PunishmentListResponseDto.fromJson(Map<String, dynamic> json) {
final data = json['data'];
if (data == null || data is! Map<String, dynamic>) {
return PunishmentListResponseDto(items: []);
}
final itemsJson = data['items'] as List? ?? [];
return PunishmentListResponseDto(
items: itemsJson.map((e) => PunishmentDto.fromJson(e)).toList(),
);
}
}

View File

@@ -0,0 +1,46 @@
class RewardDto {
final String id;
final DateTime date;
final double amount;
final String? reason;
final String? note;
final String employeeId;
RewardDto({
required this.id,
required this.date,
required this.amount,
this.reason,
this.note,
required this.employeeId,
});
factory RewardDto.fromJson(Map<String, dynamic> json) {
return RewardDto(
id: json['id']?.toString() ?? '',
date:
json['date'] != null ? DateTime.parse(json['date']) : DateTime.now(),
amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
reason: json['reason'],
note: json['note'],
employeeId: json['employeeId']?.toString() ?? '',
);
}
}
class RewardListResponseDto {
final List<RewardDto> items;
RewardListResponseDto({required this.items});
factory RewardListResponseDto.fromJson(Map<String, dynamic> json) {
final data = json['data'];
if (data == null || data is! Map<String, dynamic>) {
return RewardListResponseDto(items: []);
}
final itemsJson = data['items'] as List? ?? [];
return RewardListResponseDto(
items: itemsJson.map((e) => RewardDto.fromJson(e)).toList(),
);
}
}

View File

@@ -1,6 +1,9 @@
import '../../domain/models/attendance_login_request.dart';
import '../../domain/models/attendance_logout_request.dart';
import '../../domain/models/attendance_response_model.dart';
import '../../domain/models/attendance_model.dart';
import '../../domain/models/overtime_model.dart';
import '../../domain/models/extra_payment_model.dart';
import '../../domain/repositories/attendance_repository.dart';
import '../datasources/attendance_remote_data_source.dart';
@@ -38,4 +41,94 @@ class AttendanceRepositoryImpl implements AttendanceRepository {
logout: dto.logout,
);
}
@override
Future<List<AttendanceModel>> getAttendanceRecords({
required String employeeId,
}) async {
final dtos = await remoteDataSource.getAttendanceRecords(
employeeId: employeeId,
);
return dtos.map((dto) {
int? hours;
if (dto.login != null && dto.logout != null) {
hours = dto.logout!.difference(dto.login!).inHours;
}
return AttendanceModel(
id: dto.id,
employeeId: dto.employeeId,
date: dto.createdAt ?? dto.login,
loginTime: dto.login,
logoutTime: dto.logout,
workHours: hours,
createdAt: dto.createdAt ?? dto.login,
reason: dto.reason,
isDeleted: dto.isDeleted,
);
}).toList();
}
@override
Future<List<OvertimeModel>> getExtraHours({
required String employeeId,
}) async {
final dtos = await remoteDataSource.getExtraHours(employeeId: employeeId);
return dtos
.map(
(dto) => OvertimeModel(
id: dto.id,
employeeId: dto.employeeId,
date: dto.date,
hours: dto.hours,
hourlyRateAtTheTime: dto.hourlyRateAtTheTime,
totalAmount: dto.hours * dto.hourlyRateAtTheTime,
),
)
.toList();
}
@override
Future<List<ExtraPaymentModel>> getRewards({
required String employeeId,
}) async {
final dtos = await remoteDataSource.getRewards(employeeId: employeeId);
return dtos
.map(
(dto) => ExtraPaymentModel(
id: dto.id,
employeeId: dto.employeeId,
date: dto.date,
amount: dto.amount,
reason: dto.reason,
note: dto.note,
isPenalty: false,
),
)
.toList();
}
@override
Future<List<ExtraPaymentModel>> getPunishments({
required String employeeId,
}) async {
final dtos = await remoteDataSource.getPunishments(employeeId: employeeId);
return dtos
.map(
(dto) => ExtraPaymentModel(
id: dto.id,
employeeId: dto.employeeId,
date: dto.date,
amount: dto.amount,
reason: dto.reason,
note: dto.note,
isPenalty: true,
),
)
.toList();
}
}