번호 자동 부여 대응 및 API 공통 처리 보강
This commit is contained in:
@@ -72,8 +72,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
query: {if (includeParts.isNotEmpty) 'include': includeParts.join(',')},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 활성화된 결재 행위 목록을 조회한다.
|
||||
@@ -133,8 +132,9 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
'$_basePath/$id/can-proceed',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalProceedStatusDto.fromJson(data).toEntity();
|
||||
return ApprovalProceedStatusDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 새로운 결재를 생성한다.
|
||||
@@ -145,8 +145,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 기본 정보를 수정한다.
|
||||
@@ -157,8 +156,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재를 삭제(비활성화)한다.
|
||||
@@ -174,8 +172,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계/행위 응답에서 결재 객체 JSON을 추출한다.
|
||||
|
||||
@@ -51,9 +51,8 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
query: {if (includeSteps) 'include': 'steps'},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalTemplateDto.fromJson(
|
||||
data,
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: includeSteps);
|
||||
}
|
||||
|
||||
@@ -68,9 +67,8 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
data: input.toCreatePayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
final created = ApprovalTemplateDto.fromJson(
|
||||
data,
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: false);
|
||||
if (steps.isNotEmpty) {
|
||||
await _postSteps(created.id, steps);
|
||||
@@ -109,8 +107,9 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalTemplateDto.fromJson(data).toEntity(includeSteps: false);
|
||||
return ApprovalTemplateDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: false);
|
||||
}
|
||||
|
||||
/// 템플릿 단계 전체를 신규로 등록한다.
|
||||
|
||||
@@ -204,14 +204,12 @@ extension ApprovalStepActionTypeX on ApprovalStepActionType {
|
||||
class ApprovalCreateInput {
|
||||
ApprovalCreateInput({
|
||||
required this.transactionId,
|
||||
required this.approvalNo,
|
||||
required this.approvalStatusId,
|
||||
required this.requestedById,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final int transactionId;
|
||||
final String approvalNo;
|
||||
final int approvalStatusId;
|
||||
final int requestedById;
|
||||
final String? note;
|
||||
@@ -220,7 +218,6 @@ class ApprovalCreateInput {
|
||||
final trimmedNote = note?.trim();
|
||||
return {
|
||||
'transaction_id': transactionId,
|
||||
'approval_no': approvalNo,
|
||||
'approval_status_id': approvalStatusId,
|
||||
'requested_by_id': requestedById,
|
||||
if (trimmedNote != null && trimmedNote.isNotEmpty) 'note': trimmedNote,
|
||||
|
||||
@@ -116,7 +116,7 @@ class _ApprovalHistoryEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final sortedHistories = _applySorting(histories);
|
||||
|
||||
return AppLayout(
|
||||
|
||||
@@ -135,7 +135,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -378,14 +378,13 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
|
||||
/// 신규 결재 등록 다이얼로그를 열어 UI 단계에서 필요한 필드와 안내를 제공한다.
|
||||
Future<void> _openCreateApprovalDialog() async {
|
||||
final approvalNoController = TextEditingController();
|
||||
final transactionController = TextEditingController();
|
||||
final requesterController = TextEditingController();
|
||||
final noteController = TextEditingController();
|
||||
String? createdApprovalNo;
|
||||
InventoryEmployeeSuggestion? requesterSelection;
|
||||
int? statusId = _controller.defaultApprovalStatusId;
|
||||
String? transactionError;
|
||||
String? approvalNoError;
|
||||
String? statusError;
|
||||
String? requesterError;
|
||||
|
||||
@@ -422,8 +421,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
onPressed: isSubmitting
|
||||
? null
|
||||
: () async {
|
||||
final approvalNo = approvalNoController.text
|
||||
.trim();
|
||||
final transactionText = transactionController.text
|
||||
.trim();
|
||||
final transactionId = int.tryParse(
|
||||
@@ -433,9 +430,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
final hasStatuses = statusOptions.isNotEmpty;
|
||||
|
||||
setState(() {
|
||||
approvalNoError = approvalNo.isEmpty
|
||||
? '결재번호를 입력하세요.'
|
||||
: null;
|
||||
transactionError = transactionText.isEmpty
|
||||
? '트랜잭션 ID를 입력하세요.'
|
||||
: (transactionId == null
|
||||
@@ -449,8 +443,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
: null;
|
||||
});
|
||||
|
||||
if (approvalNoError != null ||
|
||||
transactionError != null ||
|
||||
if (transactionError != null ||
|
||||
statusError != null ||
|
||||
requesterError != null) {
|
||||
return;
|
||||
@@ -458,7 +451,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
|
||||
final input = ApprovalCreateInput(
|
||||
transactionId: transactionId!,
|
||||
approvalNo: approvalNo,
|
||||
approvalStatusId: statusId!,
|
||||
requestedById: requesterSelection!.id,
|
||||
note: note.isEmpty ? null : note,
|
||||
@@ -470,6 +462,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
return;
|
||||
}
|
||||
if (result != null) {
|
||||
createdApprovalNo = result.approvalNo;
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
@@ -489,28 +482,18 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('결재번호', style: shadTheme.textTheme.small),
|
||||
const SizedBox(height: 8),
|
||||
ShadInput(
|
||||
controller: approvalNoController,
|
||||
enabled: !isSubmitting,
|
||||
placeholder: const Text('예: APP-2025-0001'),
|
||||
onChanged: (_) {
|
||||
if (approvalNoError != null) {
|
||||
setState(() => approvalNoError = null);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (approvalNoError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
approvalNoError!,
|
||||
style: shadTheme.textTheme.small.copyWith(
|
||||
color: materialTheme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: shadTheme.colorScheme.mutedForeground
|
||||
.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'결재번호는 저장 시 자동으로 생성됩니다.',
|
||||
style: shadTheme.textTheme.muted,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('트랜잭션 ID', style: shadTheme.textTheme.small),
|
||||
const SizedBox(height: 8),
|
||||
@@ -689,13 +672,16 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
},
|
||||
);
|
||||
|
||||
approvalNoController.dispose();
|
||||
transactionController.dispose();
|
||||
requesterController.dispose();
|
||||
noteController.dispose();
|
||||
|
||||
if (created == true && mounted) {
|
||||
SuperportToast.success(context, '결재를 생성했습니다.');
|
||||
final number = createdApprovalNo ?? '-';
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'결재를 생성했습니다. ($number)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,10 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'page': page,
|
||||
'page_size': pageSize,
|
||||
if (query != null && query.isNotEmpty) 'q': query,
|
||||
if (statusId != null) ...{
|
||||
'status_id': statusId,
|
||||
'step_status_id': statusId,
|
||||
},
|
||||
if (statusId != null) ...{'status_id': statusId},
|
||||
if (approverId != null) 'approver_id': approverId,
|
||||
if (approvalId != null) 'approval_id': approvalId,
|
||||
'include': 'approval,approver,step_status',
|
||||
'include': 'approval,approver,status',
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
@@ -54,8 +51,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'$_basePath/$id',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? const {};
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 생성한다.
|
||||
@@ -66,11 +64,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 수정한다.
|
||||
@@ -81,11 +77,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 비활성화한다.
|
||||
@@ -101,10 +95,8 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
final pageSize = result?.pageSize ?? records.length;
|
||||
final totalPages = pageSize == 0
|
||||
? 1
|
||||
: (totalCount / pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (totalCount / pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -116,7 +116,7 @@ class _ApprovalTemplateEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final showReset =
|
||||
_searchController.text.trim().isNotEmpty ||
|
||||
_controller.statusFilter != ApprovalTemplateStatusFilter.all;
|
||||
|
||||
Reference in New Issue
Block a user