feat(frontend): 승인 템플릿 API 통합 및 디버그 로그인 확장

- docs 폴더 문서를 최신 API 계약으로 갱신하고 가이드를 다듬었다\n- approvals data/presentation 레이어를 API v4 스펙에 맞춰 리팩터링했다\n- approver 자동완성 위젯을 신규 공유 레포지토리에 맞춰 교체하고 UX를 보강했다\n- inventory/rental 페이지 테이블 초기화 시 승인 기준 연동을 정비했다\n- 로그인 페이지 디버그 버튼을 tera/exa 계정으로 분리해 QA 로그인을 단순화했다\n- get_it 등록과 테스트 케이스를 신규 공유 리포지토리에 맞춰 업데이트했다
This commit is contained in:
JiWoong Sul
2025-11-05 17:05:38 +09:00
parent 3e83408aa7
commit fa0bda5ea4
28 changed files with 1102 additions and 545 deletions

View File

@@ -1,5 +1,9 @@
import 'dart:async';
import 'package:get_it/get_it.dart';
import '../../../domain/entities/approval.dart';
import '../../../shared/approver_catalog.dart';
import '../../../shared/domain/repositories/approval_approver_repository.dart';
import '../controllers/approval_request_controller.dart';
import '../../../../inventory/transactions/domain/entities/stock_transaction_input.dart';
@@ -10,12 +14,13 @@ class ApprovalFormInitializer {
ApprovalFormInitializer._();
/// 결재 구성 컨트롤러에 기본값을 주입한다.
static void populate({
static Future<void> populate({
required ApprovalRequestController controller,
Approval? existingApproval,
StockTransactionApprovalInput? draft,
ApprovalRequestParticipant? defaultRequester,
}) {
ApprovalApproverRepository? repository,
}) async {
if (existingApproval != null) {
_applyExistingApproval(controller, existingApproval);
return;
@@ -24,7 +29,11 @@ class ApprovalFormInitializer {
controller.setRequester(defaultRequester);
}
if (draft != null) {
_applyDraft(controller, draft);
await _applyDraft(
controller,
draft,
repository ?? _resolveRepository(),
);
}
}
@@ -57,40 +66,62 @@ class ApprovalFormInitializer {
}
}
static void _applyDraft(
static Future<void> _applyDraft(
ApprovalRequestController controller,
StockTransactionApprovalInput draft,
) {
final requesterCatalog = ApprovalApproverCatalog.byId(draft.requestedById);
if (requesterCatalog != null) {
controller.setRequester(
ApprovalRequestParticipant(
id: requesterCatalog.id,
name: requesterCatalog.name,
employeeNo: requesterCatalog.employeeNo,
),
);
ApprovalApproverRepository? repository,
) async {
final repo = repository;
if (repo == null) {
return;
}
final steps = draft.steps
.map((step) {
final catalog = ApprovalApproverCatalog.byId(step.approverId);
if (catalog == null) {
final requester = await _fetchParticipant(repo, draft.requestedById);
if (requester != null) {
controller.setRequester(requester);
}
final futures = draft.steps
.map((step) async {
final participant = await _fetchParticipant(repo, step.approverId);
if (participant == null) {
return null;
}
return ApprovalRequestStep(
stepOrder: step.stepOrder,
approver: ApprovalRequestParticipant(
id: catalog.id,
name: catalog.name,
employeeNo: catalog.employeeNo,
),
approver: participant,
note: step.note,
);
})
.whereType<ApprovalRequestStep>()
.toList(growable: false);
final resolvedSteps = await Future.wait(futures);
final steps = resolvedSteps.whereType<ApprovalRequestStep>().toList();
if (steps.isNotEmpty) {
controller.applyTemplateSteps(steps);
}
}
static Future<ApprovalRequestParticipant?> _fetchParticipant(
ApprovalApproverRepository repository,
int id,
) async {
final candidate = await repository.fetchById(id);
if (candidate == null) {
return null;
}
return ApprovalRequestParticipant(
id: candidate.id,
name: candidate.name,
employeeNo: candidate.employeeNo,
);
}
static ApprovalApproverRepository? _resolveRepository() {
final getIt = GetIt.I;
if (!getIt.isRegistered<ApprovalApproverRepository>()) {
return null;
}
return getIt<ApprovalApproverRepository>();
}
}