feat(approvals): Approval Flow v2 프런트엔드 전면 개편
- 환경/라우터 모듈에 approval_flow_v2 토글을 추가하고 FeatureFlags 초기화를 연결 (.env*, lib/core/**) - ApiClient 빌더·ApiRoutes 확장과 ApprovalRepositoryRemote 리팩터링으로 include·액션 시그니처를 정합화 - ApprovalFlow·ApprovalDraft 엔티티/레포/유즈케이스를 도입해 서버 초안과 단계 액션(승인·회수·재상신)을 지원 - Approval 컨트롤러·히스토리·템플릿 페이지와 공유 위젯을 재작성해 감사 로그·회수 UX·템플릿 CRUD를 반영 - Inbound/Outbound/Rental 컨트롤러·페이지에 결재 섹션을 삽입하고 대시보드 pending 카드 요약을 갱신 - SuperportDialog·FormField 등 공통 위젯을 보강하고 승인 위젯 가이드를 추가해 UI 가이드를 정리 - 결재/재고 테스트 픽스처와 단위·위젯·통합 테스트를 확장하고 flutter_test_config로 스테이징 호스트를 허용 - Approval Flow 레포트/플랜 문서를 업데이트하고 ApprovalFlow_System_Integration_and_ChangePlan.md를 추가 - 실행: flutter analyze, flutter test
This commit is contained in:
237
test/fixtures/approvals/approval_five_step_pending.json
vendored
Normal file
237
test/fixtures/approvals/approval_five_step_pending.json
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"id": 51001,
|
||||
"approval_no": "APP-202511100201",
|
||||
"transaction": {
|
||||
"id": 91001,
|
||||
"transaction_no": "IN-20251110-0001"
|
||||
},
|
||||
"status": {
|
||||
"id": 2,
|
||||
"name": "진행중",
|
||||
"color": "#3B82F6",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"current_step": {
|
||||
"id": 72003,
|
||||
"step_order": 3,
|
||||
"status": {
|
||||
"id": 2,
|
||||
"name": "진행중",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 104,
|
||||
"employee_id": "E20250104",
|
||||
"employee_no": "E20250104",
|
||||
"name": "박팀장"
|
||||
},
|
||||
"assigned_at": "2025-09-18T07:10:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
},
|
||||
"requester": {
|
||||
"id": 77,
|
||||
"employee_id": "E20250077",
|
||||
"employee_no": "E20250077",
|
||||
"name": "김상신"
|
||||
},
|
||||
"requested_at": "2025-09-18T06:55:00Z",
|
||||
"decided_at": null,
|
||||
"note": "입고 5단계 결재",
|
||||
"is_active": true,
|
||||
"is_deleted": false,
|
||||
"steps": [
|
||||
{
|
||||
"id": 72001,
|
||||
"step_order": 1,
|
||||
"status": {
|
||||
"id": 3,
|
||||
"name": "승인",
|
||||
"is_blocking_next": false,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 101,
|
||||
"employee_id": "E20250101",
|
||||
"employee_no": "E20250101",
|
||||
"name": "이검토"
|
||||
},
|
||||
"assigned_at": "2025-09-18T06:58:00Z",
|
||||
"decided_at": "2025-09-18T06:59:30Z",
|
||||
"note": "조건부 승인"
|
||||
},
|
||||
{
|
||||
"id": 72002,
|
||||
"step_order": 2,
|
||||
"status": {
|
||||
"id": 3,
|
||||
"name": "승인",
|
||||
"is_blocking_next": false,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 102,
|
||||
"employee_id": "E20250102",
|
||||
"employee_no": "E20250102",
|
||||
"name": "최검수"
|
||||
},
|
||||
"assigned_at": "2025-09-18T07:00:00Z",
|
||||
"decided_at": "2025-09-18T07:04:10Z",
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 72003,
|
||||
"step_order": 3,
|
||||
"status": {
|
||||
"id": 2,
|
||||
"name": "진행중",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 104,
|
||||
"employee_id": "E20250104",
|
||||
"employee_no": "E20250104",
|
||||
"name": "박팀장"
|
||||
},
|
||||
"assigned_at": "2025-09-18T07:10:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 72004,
|
||||
"step_order": 4,
|
||||
"status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 105,
|
||||
"employee_id": "E20250105",
|
||||
"employee_no": "E20250105",
|
||||
"name": "정차장"
|
||||
},
|
||||
"assigned_at": "2025-09-18T07:10:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 72005,
|
||||
"step_order": 5,
|
||||
"status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 201,
|
||||
"employee_id": "E20250201",
|
||||
"employee_no": "E20250201",
|
||||
"name": "한임원"
|
||||
},
|
||||
"assigned_at": "2025-09-18T07:10:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
}
|
||||
],
|
||||
"histories": [
|
||||
{
|
||||
"id": 93001,
|
||||
"action": {
|
||||
"id": 1,
|
||||
"name": "상신"
|
||||
},
|
||||
"from_status": null,
|
||||
"to_status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 77,
|
||||
"employee_id": "E20250077",
|
||||
"employee_no": "E20250077",
|
||||
"name": "김상신"
|
||||
},
|
||||
"action_at": "2025-09-18T06:55:00Z",
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 93002,
|
||||
"action": {
|
||||
"id": 2,
|
||||
"name": "승인"
|
||||
},
|
||||
"from_status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"to_status": {
|
||||
"id": 3,
|
||||
"name": "승인",
|
||||
"is_blocking_next": false,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 101,
|
||||
"employee_id": "E20250101",
|
||||
"employee_no": "E20250101",
|
||||
"name": "이검토"
|
||||
},
|
||||
"action_at": "2025-09-18T06:59:30Z",
|
||||
"note": "조건 수용"
|
||||
},
|
||||
{
|
||||
"id": 93003,
|
||||
"action": {
|
||||
"id": 2,
|
||||
"name": "승인"
|
||||
},
|
||||
"from_status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"to_status": {
|
||||
"id": 3,
|
||||
"name": "승인",
|
||||
"is_blocking_next": false,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 102,
|
||||
"employee_id": "E20250102",
|
||||
"employee_no": "E20250102",
|
||||
"name": "최검수"
|
||||
},
|
||||
"action_at": "2025-09-18T07:04:10Z",
|
||||
"note": null
|
||||
}
|
||||
],
|
||||
"created_at": "2025-09-18T06:55:00Z",
|
||||
"updated_at": "2025-09-18T07:10:00Z",
|
||||
"visibility": {
|
||||
"allowed_roles": [
|
||||
"requester",
|
||||
"completed_approver"
|
||||
],
|
||||
"restricted_roles": [
|
||||
"pending_approver",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"can_recall": true,
|
||||
"can_resubmit": false,
|
||||
"can_comment": true
|
||||
}
|
||||
}
|
||||
13
test/fixtures/approvals/approval_permissions.json
vendored
Normal file
13
test/fixtures/approvals/approval_permissions.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"viewer": [
|
||||
"view"
|
||||
],
|
||||
"approver": [
|
||||
"view",
|
||||
"approve"
|
||||
],
|
||||
"auditor": [
|
||||
"view",
|
||||
"restore"
|
||||
]
|
||||
}
|
||||
134
test/fixtures/approvals/approval_recalled.json
vendored
Normal file
134
test/fixtures/approvals/approval_recalled.json
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"id": 52011,
|
||||
"approval_no": "APP-202511120045",
|
||||
"transaction": {
|
||||
"id": 93010,
|
||||
"transaction_no": "OUT-20251112-0003"
|
||||
},
|
||||
"status": {
|
||||
"id": 6,
|
||||
"name": "회수",
|
||||
"color": "#8B5CF6",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"current_step": null,
|
||||
"requester": {
|
||||
"id": 88,
|
||||
"employee_id": "E20250088",
|
||||
"employee_no": "E20250088",
|
||||
"name": "오요청"
|
||||
},
|
||||
"requested_at": "2025-09-20T01:10:00Z",
|
||||
"decided_at": "2025-09-20T01:22:30Z",
|
||||
"note": "출고 회수 테스트",
|
||||
"is_active": true,
|
||||
"is_deleted": false,
|
||||
"steps": [
|
||||
{
|
||||
"id": 73011,
|
||||
"step_order": 1,
|
||||
"status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 110,
|
||||
"employee_id": "E20250110",
|
||||
"employee_no": "E20250110",
|
||||
"name": "문검토"
|
||||
},
|
||||
"assigned_at": "2025-09-20T01:11:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 73012,
|
||||
"step_order": 2,
|
||||
"status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 210,
|
||||
"employee_id": "E20250210",
|
||||
"employee_no": "E20250210",
|
||||
"name": "강팀장"
|
||||
},
|
||||
"assigned_at": "2025-09-20T01:11:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
}
|
||||
],
|
||||
"histories": [
|
||||
{
|
||||
"id": 94011,
|
||||
"action": {
|
||||
"id": 1,
|
||||
"name": "상신"
|
||||
},
|
||||
"from_status": null,
|
||||
"to_status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 88,
|
||||
"employee_id": "E20250088",
|
||||
"employee_no": "E20250088",
|
||||
"name": "오요청"
|
||||
},
|
||||
"action_at": "2025-09-20T01:10:00Z",
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 94012,
|
||||
"action": {
|
||||
"id": 5,
|
||||
"name": "회수"
|
||||
},
|
||||
"from_status": {
|
||||
"id": 2,
|
||||
"name": "진행중",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"to_status": {
|
||||
"id": 6,
|
||||
"name": "회수",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 88,
|
||||
"employee_id": "E20250088",
|
||||
"employee_no": "E20250088",
|
||||
"name": "오요청"
|
||||
},
|
||||
"action_at": "2025-09-20T01:22:30Z",
|
||||
"note": "승인 지연으로 회수"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-09-20T01:10:00Z",
|
||||
"updated_at": "2025-09-20T01:22:30Z",
|
||||
"visibility": {
|
||||
"allowed_roles": [
|
||||
"requester"
|
||||
],
|
||||
"restricted_roles": [
|
||||
"pending_approver",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"can_recall": false,
|
||||
"can_resubmit": true,
|
||||
"can_comment": true
|
||||
}
|
||||
}
|
||||
153
test/fixtures/approvals/approval_rejected.json
vendored
Normal file
153
test/fixtures/approvals/approval_rejected.json
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"id": 53021,
|
||||
"approval_no": "APP-202511150089",
|
||||
"transaction": {
|
||||
"id": 94090,
|
||||
"transaction_no": "RENT-20251115-0002"
|
||||
},
|
||||
"status": {
|
||||
"id": 4,
|
||||
"name": "반려",
|
||||
"color": "#EF4444",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": true
|
||||
},
|
||||
"current_step": {
|
||||
"id": 74021,
|
||||
"step_order": 1,
|
||||
"status": {
|
||||
"id": 4,
|
||||
"name": "반려",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": true
|
||||
},
|
||||
"approver": {
|
||||
"id": 120,
|
||||
"employee_id": "E20250120",
|
||||
"employee_no": "E20250120",
|
||||
"name": "신품질"
|
||||
},
|
||||
"assigned_at": "2025-09-21T00:05:00Z",
|
||||
"decided_at": "2025-09-21T00:16:40Z",
|
||||
"note": "입력 수량 오류"
|
||||
},
|
||||
"requester": {
|
||||
"id": 91,
|
||||
"employee_id": "E20250091",
|
||||
"employee_no": "E20250091",
|
||||
"name": "장요청"
|
||||
},
|
||||
"requested_at": "2025-09-21T00:02:00Z",
|
||||
"decided_at": "2025-09-21T00:16:40Z",
|
||||
"note": "대여 반려 케이스",
|
||||
"is_active": true,
|
||||
"is_deleted": false,
|
||||
"steps": [
|
||||
{
|
||||
"id": 74021,
|
||||
"step_order": 1,
|
||||
"status": {
|
||||
"id": 4,
|
||||
"name": "반려",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": true
|
||||
},
|
||||
"approver": {
|
||||
"id": 120,
|
||||
"employee_id": "E20250120",
|
||||
"employee_no": "E20250120",
|
||||
"name": "신품질"
|
||||
},
|
||||
"assigned_at": "2025-09-21T00:05:00Z",
|
||||
"decided_at": "2025-09-21T00:16:40Z",
|
||||
"note": "입력 수량 오류"
|
||||
},
|
||||
{
|
||||
"id": 74022,
|
||||
"step_order": 2,
|
||||
"status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 221,
|
||||
"employee_id": "E20250221",
|
||||
"employee_no": "E20250221",
|
||||
"name": "노부장"
|
||||
},
|
||||
"assigned_at": "2025-09-21T00:05:00Z",
|
||||
"decided_at": null,
|
||||
"note": null
|
||||
}
|
||||
],
|
||||
"histories": [
|
||||
{
|
||||
"id": 95021,
|
||||
"action": {
|
||||
"id": 1,
|
||||
"name": "상신"
|
||||
},
|
||||
"from_status": null,
|
||||
"to_status": {
|
||||
"id": 1,
|
||||
"name": "대기",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"approver": {
|
||||
"id": 91,
|
||||
"employee_id": "E20250091",
|
||||
"employee_no": "E20250091",
|
||||
"name": "장요청"
|
||||
},
|
||||
"action_at": "2025-09-21T00:02:00Z",
|
||||
"note": null
|
||||
},
|
||||
{
|
||||
"id": 95022,
|
||||
"action": {
|
||||
"id": 3,
|
||||
"name": "반려"
|
||||
},
|
||||
"from_status": {
|
||||
"id": 2,
|
||||
"name": "진행중",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": false
|
||||
},
|
||||
"to_status": {
|
||||
"id": 4,
|
||||
"name": "반려",
|
||||
"is_blocking_next": true,
|
||||
"is_terminal": true
|
||||
},
|
||||
"approver": {
|
||||
"id": 120,
|
||||
"employee_id": "E20250120",
|
||||
"employee_no": "E20250120",
|
||||
"name": "신품질"
|
||||
},
|
||||
"action_at": "2025-09-21T00:16:40Z",
|
||||
"note": "수량 2배 입력"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-09-21T00:02:00Z",
|
||||
"updated_at": "2025-09-21T00:16:40Z",
|
||||
"visibility": {
|
||||
"allowed_roles": [
|
||||
"requester",
|
||||
"completed_approver"
|
||||
],
|
||||
"restricted_roles": [
|
||||
"pending_approver",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"can_recall": false,
|
||||
"can_resubmit": true,
|
||||
"can_comment": false
|
||||
}
|
||||
}
|
||||
47
test/fixtures/approvals/approval_template_sample.json
vendored
Normal file
47
test/fixtures/approvals/approval_template_sample.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 901,
|
||||
"template_code": "IN-STD",
|
||||
"template_name": "입고 표준 결재",
|
||||
"description": "팀장 → 센터장 → 본부장 순 결재",
|
||||
"note": "입고 공통 사용",
|
||||
"is_active": true,
|
||||
"created_by": {
|
||||
"id": 77,
|
||||
"employee_no": "EMP077",
|
||||
"name": "김상신"
|
||||
},
|
||||
"created_at": "2025-09-15T00:10:00Z",
|
||||
"updated_at": "2025-09-18T05:20:00Z",
|
||||
"steps": [
|
||||
{
|
||||
"id": 9101,
|
||||
"step_order": 1,
|
||||
"approver": {
|
||||
"id": 101,
|
||||
"employee_no": "EMP101",
|
||||
"name": "이검토"
|
||||
},
|
||||
"note": "입고 검수"
|
||||
},
|
||||
{
|
||||
"id": 9102,
|
||||
"step_order": 2,
|
||||
"approver": {
|
||||
"id": 105,
|
||||
"employee_no": "EMP105",
|
||||
"name": "정차장"
|
||||
},
|
||||
"note": "센터장 승인"
|
||||
},
|
||||
{
|
||||
"id": 9103,
|
||||
"step_order": 3,
|
||||
"approver": {
|
||||
"id": 201,
|
||||
"employee_no": "EMP201",
|
||||
"name": "한임원"
|
||||
},
|
||||
"note": "최종 승인"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user