/// 결재(Approval) 엔티티 /// /// - 결재 기본 정보와 현재 단계, 라인(단계/이력) 데이터를 포함한다. /// - presentation/data 레이어 구현에 의존하지 않는다. class Approval { Approval({ this.id, required this.approvalNo, required 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 ApprovalStatus status; final ApprovalStep? currentStep; final ApprovalRequester 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; Approval copyWith({ int? id, String? approvalNo, String? transactionNo, ApprovalStatus? status, ApprovalStep? currentStep, ApprovalRequester? requester, DateTime? requestedAt, DateTime? decidedAt, String? note, bool? isActive, bool? isDeleted, List? steps, List? histories, DateTime? createdAt, DateTime? updatedAt, }) { return Approval( id: id ?? this.id, approvalNo: approvalNo ?? this.approvalNo, transactionNo: transactionNo ?? this.transactionNo, status: status ?? this.status, currentStep: currentStep ?? this.currentStep, requester: requester ?? this.requester, requestedAt: requestedAt ?? this.requestedAt, decidedAt: decidedAt ?? this.decidedAt, note: note ?? this.note, isActive: isActive ?? this.isActive, isDeleted: isDeleted ?? this.isDeleted, steps: steps ?? this.steps, histories: histories ?? this.histories, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, ); } } class ApprovalStatus { ApprovalStatus({required this.id, required this.name, this.color}); final int id; final String name; final String? color; } class ApprovalRequester { ApprovalRequester({ required this.id, required this.employeeNo, required this.name, }); final int id; final String employeeNo; final String name; } class ApprovalStep { ApprovalStep({ this.id, required this.stepOrder, required this.approver, required this.status, required this.assignedAt, this.decidedAt, this.note, }); final int? id; final int stepOrder; final ApprovalApprover approver; final ApprovalStatus status; final DateTime assignedAt; final DateTime? decidedAt; final String? note; } class ApprovalApprover { ApprovalApprover({ required this.id, required this.employeeNo, required this.name, }); final int id; final String employeeNo; final String name; } class ApprovalHistory { ApprovalHistory({ this.id, required this.action, this.fromStatus, required this.toStatus, required this.approver, required this.actionAt, this.note, }); final int? id; final ApprovalAction action; final ApprovalStatus? fromStatus; final ApprovalStatus toStatus; final ApprovalApprover approver; final DateTime actionAt; final String? note; } class ApprovalAction { ApprovalAction({required this.id, required this.name}); final int id; final String name; } /// 결재 단계에서 수행 가능한 행위 타입 /// /// - API `approval_actions` 테이블의 대표 코드와 매핑된다. /// - UI에서는 이 타입을 기반으로 표시 라벨과 권한을 제어한다. enum ApprovalStepActionType { approve, reject, comment } extension ApprovalStepActionTypeX on ApprovalStepActionType { /// API 호출 시 사용되는 행위 코드 String get code { switch (this) { case ApprovalStepActionType.approve: return 'approve'; case ApprovalStepActionType.reject: return 'reject'; case ApprovalStepActionType.comment: return 'comment'; } } } /// 결재 생성 입력 모델 class ApprovalInput { ApprovalInput({required this.transactionId, this.note}); final int transactionId; final String? note; Map toPayload() { return {'transaction_id': transactionId, 'note': note}; } } /// 결재 단계 행위 입력 모델 /// /// - `POST /approval-steps/{id}/actions` 요청 바디를 구성한다. /// - `note`는 비고가 있을 때만 포함한다. class ApprovalStepActionInput { ApprovalStepActionInput({ required this.stepId, required this.actionId, this.note, }); final int stepId; final int actionId; final String? note; Map toPayload() { return { 'id': stepId, 'approval_action_id': actionId, if (note != null && note!.trim().isNotEmpty) 'note': note, }; } } /// 결재 단계를 일괄 등록/재배치하기 위한 입력 모델 class ApprovalStepAssignmentInput { ApprovalStepAssignmentInput({required this.approvalId, required this.steps}); final int approvalId; final List steps; Map toPayload() { return {'id': approvalId, 'steps': steps.map((e) => e.toJson()).toList()}; } } class ApprovalStepAssignmentItem { ApprovalStepAssignmentItem({ required this.stepOrder, required this.approverId, this.note, }); final int stepOrder; final int approverId; final String? note; Map toJson() { return { 'step_order': stepOrder, 'approver_id': approverId, if (note != null && note!.trim().isNotEmpty) 'note': note, }; } }