import 'package:superport_v2/core/common/models/paginated_result.dart'; import 'package:superport_v2/core/common/utils/json_utils.dart'; import '../../domain/entities/approval.dart'; /// 결재 API 응답을 표현하는 DTO. /// /// - 원본 JSON 형식을 유지하면서 도메인 엔티티 변환을 제공한다. /// - 일부 필드는 누락 가능성을 고려하여 기본값을 지정한다. class ApprovalDto { ApprovalDto({ this.id, required this.approvalNo, this.transactionNo, required this.status, this.currentStep, required this.requester, required this.requestedAt, this.decidedAt, this.note, this.isActive = true, this.isDeleted = false, this.steps = const [], this.histories = const [], this.createdAt, this.updatedAt, }); final int? id; final String approvalNo; final String? transactionNo; final ApprovalStatusDto status; final ApprovalStepDto? currentStep; final ApprovalRequesterDto requester; final DateTime requestedAt; final DateTime? decidedAt; final String? note; final bool isActive; final bool isDeleted; final List steps; final List histories; final DateTime? createdAt; final DateTime? updatedAt; /// API 응답 JSON을 [ApprovalDto]로 변환한다. factory ApprovalDto.fromJson(Map json) { return ApprovalDto( id: json['id'] as int?, approvalNo: json['approval_no'] as String, transactionNo: json['transaction'] is Map ? (json['transaction']['transaction_no'] as String?) : json['transaction_no'] as String?, status: ApprovalStatusDto.fromJson( (json['status'] as Map? ?? const {}), ), currentStep: json['current_step'] is Map ? ApprovalStepDto.fromJson( json['current_step'] as Map, ) : null, requester: ApprovalRequesterDto.fromJson( (json['requester'] as Map? ?? const {}), ), requestedAt: _parseDate(json['requested_at']) ?? DateTime.now(), decidedAt: _parseDate(json['decided_at']), note: json['note'] as String?, isActive: (json['is_active'] as bool?) ?? true, isDeleted: (json['is_deleted'] as bool?) ?? false, steps: (json['steps'] as List? ?? []) .whereType>() .map(ApprovalStepDto.fromJson) .toList(), histories: (json['histories'] as List? ?? []) .whereType>() .map(ApprovalHistoryDto.fromJson) .toList(), createdAt: _parseDate(json['created_at']), updatedAt: _parseDate(json['updated_at']), ); } /// DTO를 도메인 [Approval] 엔티티로 변환한다. Approval toEntity() => Approval( id: id, approvalNo: approvalNo, transactionNo: transactionNo ?? '-', status: status.toEntity(), currentStep: currentStep?.toEntity(), requester: requester.toEntity(), requestedAt: requestedAt, decidedAt: decidedAt, note: note, isActive: isActive, isDeleted: isDeleted, steps: steps.map((e) => e.toEntity()).toList(), histories: histories.map((e) => e.toEntity()).toList(), createdAt: createdAt, updatedAt: updatedAt, ); /// 페이징 응답을 파싱해 [PaginatedResult]로 변환한다. static PaginatedResult parsePaginated(Map? json) { final rawItems = JsonUtils.extractList(json, keys: const ['items']); final items = rawItems .map(ApprovalDto.fromJson) .map((dto) => dto.toEntity()) .toList(growable: false); return PaginatedResult( items: items, page: JsonUtils.readInt(json, 'page', fallback: 1), pageSize: JsonUtils.readInt(json, 'page_size', fallback: items.length), total: JsonUtils.readInt(json, 'total', fallback: items.length), ); } } /// 결재 상태(Status) DTO. class ApprovalStatusDto { ApprovalStatusDto({required this.id, required this.name, this.color}); final int id; final String name; final String? color; factory ApprovalStatusDto.fromJson(Map json) { return ApprovalStatusDto( id: json['id'] as int? ?? json['status_id'] as int? ?? 0, name: json['name'] as String? ?? json['status_name'] as String? ?? '-', color: json['color'] as String?, ); } /// DTO를 [ApprovalStatus]로 변환한다. ApprovalStatus toEntity() => ApprovalStatus(id: id, name: name, color: color); } /// 결재 요청자 DTO. class ApprovalRequesterDto { ApprovalRequesterDto({ required this.id, required this.employeeNo, required this.name, }); final int id; final String employeeNo; final String name; factory ApprovalRequesterDto.fromJson(Map json) { return ApprovalRequesterDto( id: json['id'] as int? ?? json['employee_id'] as int? ?? 0, employeeNo: json['employee_no'] as String? ?? '-', name: json['name'] as String? ?? json['employee_name'] as String? ?? '-', ); } /// DTO를 [ApprovalRequester]로 변환한다. ApprovalRequester toEntity() => ApprovalRequester(id: id, employeeNo: employeeNo, name: name); } /// 결재 승인자 DTO. class ApprovalApproverDto { ApprovalApproverDto({ required this.id, required this.employeeNo, required this.name, }); final int id; final String employeeNo; final String name; factory ApprovalApproverDto.fromJson(Map json) { return ApprovalApproverDto( id: json['id'] as int? ?? json['approver_id'] as int? ?? 0, employeeNo: json['employee_no'] as String? ?? '-', name: json['name'] as String? ?? json['employee_name'] as String? ?? '-', ); } /// DTO를 [ApprovalApprover]로 변환한다. ApprovalApprover toEntity() => ApprovalApprover(id: id, employeeNo: employeeNo, name: name); } /// 결재 단계 DTO. class ApprovalStepDto { ApprovalStepDto({ this.id, required this.stepOrder, required this.approver, required this.status, required this.assignedAt, this.decidedAt, this.note, this.isDeleted = false, }); final int? id; final int stepOrder; final ApprovalApproverDto approver; final ApprovalStatusDto status; final DateTime assignedAt; final DateTime? decidedAt; final String? note; final bool isDeleted; factory ApprovalStepDto.fromJson(Map json) { return ApprovalStepDto( id: json['id'] as int?, stepOrder: json['step_order'] as int? ?? 0, approver: ApprovalApproverDto.fromJson( (json['approver'] as Map? ?? const {}), ), status: ApprovalStatusDto.fromJson( (json['status'] as Map? ?? const {}), ), assignedAt: _parseDate(json['assigned_at']) ?? DateTime.now(), decidedAt: _parseDate(json['decided_at']), note: json['note'] as String?, isDeleted: json['is_deleted'] as bool? ?? (json['deleted_at'] != null || (json['is_active'] is bool && !(json['is_active'] as bool))), ); } /// DTO를 [ApprovalStep]으로 변환한다. ApprovalStep toEntity() => ApprovalStep( id: id, stepOrder: stepOrder, approver: approver.toEntity(), status: status.toEntity(), assignedAt: assignedAt, decidedAt: decidedAt, note: note, isDeleted: isDeleted, ); } /// 결재 이력 DTO. class ApprovalHistoryDto { ApprovalHistoryDto({ this.id, required this.action, this.fromStatus, required this.toStatus, required this.approver, required this.actionAt, this.note, }); final int? id; final ApprovalActionDto action; final ApprovalStatusDto? fromStatus; final ApprovalStatusDto toStatus; final ApprovalApproverDto approver; final DateTime actionAt; final String? note; factory ApprovalHistoryDto.fromJson(Map json) { return ApprovalHistoryDto( id: json['id'] as int?, action: ApprovalActionDto.fromJson( (json['action'] as Map? ?? const {}), ), fromStatus: json['from_status'] is Map ? ApprovalStatusDto.fromJson( json['from_status'] as Map, ) : null, toStatus: ApprovalStatusDto.fromJson( (json['to_status'] as Map? ?? const {}), ), approver: ApprovalApproverDto.fromJson( (json['approver'] as Map? ?? const {}), ), actionAt: _parseDate(json['action_at']) ?? DateTime.now(), note: json['note'] as String?, ); } /// DTO를 [ApprovalHistory]로 변환한다. ApprovalHistory toEntity() => ApprovalHistory( id: id, action: action.toEntity(), fromStatus: fromStatus?.toEntity(), toStatus: toStatus.toEntity(), approver: approver.toEntity(), actionAt: actionAt, note: note, ); } /// 결재 행위(Action) DTO. class ApprovalActionDto { ApprovalActionDto({required this.id, required this.name}); final int id; final String name; factory ApprovalActionDto.fromJson(Map json) { return ApprovalActionDto( id: json['id'] as int? ?? json['action_id'] as int? ?? 0, name: json['name'] as String? ?? json['action_name'] as String? ?? '-', ); } /// DTO를 [ApprovalAction]으로 변환한다. ApprovalAction toEntity() => ApprovalAction(id: id, name: name); } /// 문자열/DateTime 입력을 DateTime으로 변환한다. DateTime? _parseDate(Object? value) { if (value == null) return null; if (value is DateTime) return value; if (value is String) return DateTime.tryParse(value); return null; }