결재 템플릿 단계 적용 구현
- ApprovalTemplate 엔티티·DTO·원격 리포지토리 추가 - ApprovalController에 템플릿 로딩/적용 상태와 assignSteps 호출 연동 - ApprovalPage 단계 탭에 템플릿 선택 UI 및 적용 확인 다이얼로그 구현 - 템플릿 적용 단위 테스트와 IMPLEMENTATION_TASKS 현황 갱신
This commit is contained in:
248
lib/features/approvals/domain/entities/approval.dart
Normal file
248
lib/features/approvals/domain/entities/approval.dart
Normal file
@@ -0,0 +1,248 @@
|
||||
/// 결재(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<ApprovalStep> steps;
|
||||
final List<ApprovalHistory> 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<ApprovalStep>? steps,
|
||||
List<ApprovalHistory>? 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<String, dynamic> 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<String, dynamic> 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<ApprovalStepAssignmentItem> steps;
|
||||
|
||||
Map<String, dynamic> 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<String, dynamic> toJson() {
|
||||
return {
|
||||
'step_order': stepOrder,
|
||||
'approver_id': approverId,
|
||||
if (note != null && note!.trim().isNotEmpty) 'note': note,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/// 결재 템플릿 엔티티
|
||||
///
|
||||
/// - 반복되는 결재 단계를 사전에 정의해두고 요청 시 불러온다.
|
||||
class ApprovalTemplate {
|
||||
ApprovalTemplate({
|
||||
required this.id,
|
||||
required this.code,
|
||||
required this.name,
|
||||
this.description,
|
||||
required this.isActive,
|
||||
this.createdBy,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.steps = const [],
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String code;
|
||||
final String name;
|
||||
final String? description;
|
||||
final bool isActive;
|
||||
final ApprovalTemplateAuthor? createdBy;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final List<ApprovalTemplateStep> steps;
|
||||
}
|
||||
|
||||
class ApprovalTemplateAuthor {
|
||||
ApprovalTemplateAuthor({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
}
|
||||
|
||||
class ApprovalTemplateStep {
|
||||
ApprovalTemplateStep({
|
||||
this.id,
|
||||
required this.stepOrder,
|
||||
required this.approver,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final int stepOrder;
|
||||
final ApprovalTemplateApprover approver;
|
||||
final String? note;
|
||||
}
|
||||
|
||||
class ApprovalTemplateApprover {
|
||||
ApprovalTemplateApprover({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
}
|
||||
Reference in New Issue
Block a user