import '../entities/approval.dart'; /// 결재 흐름(Approval Flow)을 표현하는 도메인 엔티티. /// /// - 상신자, 최종 승인자, 단계 목록, 이력, 상태 요약을 한 번에 제공한다. /// - presentation 레이어에서는 이 엔티티만 의존해 UI를 구성한다. class ApprovalFlow { ApprovalFlow({ required Approval approval, ApprovalApprover? finalApprover, ApprovalFlowStatusSummary? statusSummary, }) : _approval = approval, finalApprover = finalApprover ?? _inferFinalApprover(approval.steps), statusSummary = statusSummary ?? ApprovalFlowStatusSummary.from( status: approval.status, steps: approval.steps, currentStep: approval.currentStep, ), _steps = List.unmodifiable(approval.steps), _histories = List.unmodifiable(approval.histories); /// 결재 원본 데이터 final Approval _approval; /// 결재 단계 목록 final List _steps; /// 결재 이력 목록 final List _histories; /// 최종 승인자 정보 (단계 목록 기반 추론 결과) final ApprovalApprover? finalApprover; /// 결재 상태 요약 정보 final ApprovalFlowStatusSummary statusSummary; /// 원본 결재 엔티티에 접근한다. Approval get approval => _approval; /// 결재 식별자(ID) int? get id => _approval.id; /// 결재 번호(APP-YYYYMMDDNNNN 형식) String get approvalNo => _approval.approvalNo; /// 연동된 전표 번호 String get transactionNo => _approval.transactionNo; /// 연동된 전표 ID int? get transactionId => _approval.transactionId; /// 연동된 전표 최신 수정 시각 DateTime? get transactionUpdatedAt => _approval.transactionUpdatedAt; /// 현재 결재 상태 ApprovalStatus get status => _approval.status; /// 현재 진행 중인 단계 정보 ApprovalStep? get currentStep => _approval.currentStep; /// 상신자 정보 ApprovalRequester get requester => _approval.requester; /// 상신 일시 DateTime get requestedAt => _approval.requestedAt; /// 최종 결정 일시 DateTime? get decidedAt => _approval.decidedAt; /// 결재 메모 String? get note => _approval.note; /// 생성 일시 DateTime? get createdAt => _approval.createdAt; /// 변경 일시 DateTime? get updatedAt => _approval.updatedAt; /// 단계 목록을 반환한다. List get steps => _steps; /// 이력 목록을 반환한다. List get histories => _histories; /// [Approval] 엔티티에서 [ApprovalFlow]를 생성하는 팩토리. factory ApprovalFlow.fromApproval(Approval approval) => ApprovalFlow(approval: approval); static ApprovalApprover? _inferFinalApprover(List steps) { if (steps.isEmpty) { return null; } final sorted = List.from(steps) ..sort((a, b) => a.stepOrder.compareTo(b.stepOrder)); return sorted.last.approver; } } /// 결재 상태 요약 정보. /// /// - 전체 단계 수, 완료 단계 수, 대기 단계 수, 현재 단계 순번을 제공한다. class ApprovalFlowStatusSummary { ApprovalFlowStatusSummary({ required this.status, required this.totalSteps, required this.completedSteps, required this.pendingSteps, this.currentStepOrder, }); /// 전체 결재 상태 final ApprovalStatus status; /// 총 단계 수 final int totalSteps; /// 완료된 단계 수 final int completedSteps; /// 대기 중인 단계 수 final int pendingSteps; /// 현재 진행 중인 단계 순번 (없으면 null) final int? currentStepOrder; /// 완료율(%)을 정수로 반환한다. int get completionRate { if (totalSteps <= 0) { return 0; } final ratio = (completedSteps / totalSteps) * 100; return ratio.isFinite ? ratio.round() : 0; } /// 결재 상태와 단계 목록을 기반으로 요약 정보를 생성한다. factory ApprovalFlowStatusSummary.from({ required ApprovalStatus status, required List steps, ApprovalStep? currentStep, }) { final total = steps.length; final completed = steps.where((step) => step.decidedAt != null).length; final pending = total - completed; final currentOrder = currentStep?.stepOrder ?? _findCurrentStepOrder(steps); return ApprovalFlowStatusSummary( status: status, totalSteps: total, completedSteps: completed, pendingSteps: pending < 0 ? 0 : pending, currentStepOrder: currentOrder, ); } static int? _findCurrentStepOrder(List steps) { for (final step in steps) { if (step.decidedAt == null) { return step.stepOrder; } } if (steps.isEmpty) { return null; } return steps.last.stepOrder; } }