결재 템플릿 단계 적용 구현
- ApprovalTemplate 엔티티·DTO·원격 리포지토리 추가 - ApprovalController에 템플릿 로딩/적용 상태와 assignSteps 호출 연동 - ApprovalPage 단계 탭에 템플릿 선택 UI 및 적용 확인 다이얼로그 구현 - 템플릿 적용 단위 테스트와 IMPLEMENTATION_TASKS 현황 갱신
This commit is contained in:
292
lib/features/approvals/data/dtos/approval_dto.dart
Normal file
292
lib/features/approvals/data/dtos/approval_dto.dart
Normal file
@@ -0,0 +1,292 @@
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
|
||||
import '../../domain/entities/approval.dart';
|
||||
|
||||
class ApprovalDto {
|
||||
ApprovalDto({
|
||||
this.id,
|
||||
required this.approvalNo,
|
||||
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 ApprovalStatusDto status;
|
||||
final ApprovalStepDto? currentStep;
|
||||
final ApprovalRequesterDto requester;
|
||||
final DateTime requestedAt;
|
||||
final DateTime? decidedAt;
|
||||
final String? note;
|
||||
final bool isActive;
|
||||
final bool isDeleted;
|
||||
final List<ApprovalStepDto> steps;
|
||||
final List<ApprovalHistoryDto> histories;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
|
||||
factory ApprovalDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalDto(
|
||||
id: json['id'] as int?,
|
||||
approvalNo: json['approval_no'] as String,
|
||||
transactionNo: json['transaction'] is Map<String, dynamic>
|
||||
? (json['transaction']['transaction_no'] as String?)
|
||||
: json['transaction_no'] as String?,
|
||||
status: ApprovalStatusDto.fromJson(
|
||||
(json['status'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
currentStep: json['current_step'] is Map<String, dynamic>
|
||||
? ApprovalStepDto.fromJson(
|
||||
json['current_step'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
requester: ApprovalRequesterDto.fromJson(
|
||||
(json['requester'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
requestedAt: _parseDate(json['requested_at']) ?? DateTime.now(),
|
||||
decidedAt: _parseDate(json['decided_at']),
|
||||
note: json['note'] as String?,
|
||||
isActive: (json['is_active'] as bool?) ?? true,
|
||||
isDeleted: (json['is_deleted'] as bool?) ?? false,
|
||||
steps: (json['steps'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalStepDto.fromJson)
|
||||
.toList(),
|
||||
histories: (json['histories'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalHistoryDto.fromJson)
|
||||
.toList(),
|
||||
createdAt: _parseDate(json['created_at']),
|
||||
updatedAt: _parseDate(json['updated_at']),
|
||||
);
|
||||
}
|
||||
|
||||
Approval toEntity() => Approval(
|
||||
id: id,
|
||||
approvalNo: approvalNo,
|
||||
transactionNo: transactionNo ?? '-',
|
||||
status: status.toEntity(),
|
||||
currentStep: currentStep?.toEntity(),
|
||||
requester: requester.toEntity(),
|
||||
requestedAt: requestedAt,
|
||||
decidedAt: decidedAt,
|
||||
note: note,
|
||||
isActive: isActive,
|
||||
isDeleted: isDeleted,
|
||||
steps: steps.map((e) => e.toEntity()).toList(),
|
||||
histories: histories.map((e) => e.toEntity()).toList(),
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
);
|
||||
|
||||
static PaginatedResult<Approval> parsePaginated(Map<String, dynamic>? json) {
|
||||
final items = (json?['items'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalDto.fromJson)
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList();
|
||||
return PaginatedResult<Approval>(
|
||||
items: items,
|
||||
page: json?['page'] as int? ?? 1,
|
||||
pageSize: json?['page_size'] as int? ?? items.length,
|
||||
total: json?['total'] as int? ?? items.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ApprovalStatusDto {
|
||||
ApprovalStatusDto({required this.id, required this.name, this.color});
|
||||
|
||||
final int id;
|
||||
final String name;
|
||||
final String? color;
|
||||
|
||||
factory ApprovalStatusDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalStatusDto(
|
||||
id: json['id'] as int? ?? json['status_id'] as int? ?? 0,
|
||||
name: json['name'] as String? ?? json['status_name'] as String? ?? '-',
|
||||
color: json['color'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalStatus toEntity() => ApprovalStatus(id: id, name: name, color: color);
|
||||
}
|
||||
|
||||
class ApprovalRequesterDto {
|
||||
ApprovalRequesterDto({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
|
||||
factory ApprovalRequesterDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalRequesterDto(
|
||||
id: json['id'] as int? ?? json['employee_id'] as int? ?? 0,
|
||||
employeeNo: json['employee_no'] as String? ?? '-',
|
||||
name: json['name'] as String? ?? json['employee_name'] as String? ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalRequester toEntity() =>
|
||||
ApprovalRequester(id: id, employeeNo: employeeNo, name: name);
|
||||
}
|
||||
|
||||
class ApprovalApproverDto {
|
||||
ApprovalApproverDto({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
|
||||
factory ApprovalApproverDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalApproverDto(
|
||||
id: json['id'] as int? ?? json['approver_id'] as int? ?? 0,
|
||||
employeeNo: json['employee_no'] as String? ?? '-',
|
||||
name: json['name'] as String? ?? json['employee_name'] as String? ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalApprover toEntity() =>
|
||||
ApprovalApprover(id: id, employeeNo: employeeNo, name: name);
|
||||
}
|
||||
|
||||
class ApprovalStepDto {
|
||||
ApprovalStepDto({
|
||||
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 ApprovalApproverDto approver;
|
||||
final ApprovalStatusDto status;
|
||||
final DateTime assignedAt;
|
||||
final DateTime? decidedAt;
|
||||
final String? note;
|
||||
|
||||
factory ApprovalStepDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalStepDto(
|
||||
id: json['id'] as int?,
|
||||
stepOrder: json['step_order'] as int? ?? 0,
|
||||
approver: ApprovalApproverDto.fromJson(
|
||||
(json['approver'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
status: ApprovalStatusDto.fromJson(
|
||||
(json['status'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
assignedAt: _parseDate(json['assigned_at']) ?? DateTime.now(),
|
||||
decidedAt: _parseDate(json['decided_at']),
|
||||
note: json['note'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalStep toEntity() => ApprovalStep(
|
||||
id: id,
|
||||
stepOrder: stepOrder,
|
||||
approver: approver.toEntity(),
|
||||
status: status.toEntity(),
|
||||
assignedAt: assignedAt,
|
||||
decidedAt: decidedAt,
|
||||
note: note,
|
||||
);
|
||||
}
|
||||
|
||||
class ApprovalHistoryDto {
|
||||
ApprovalHistoryDto({
|
||||
this.id,
|
||||
required this.action,
|
||||
this.fromStatus,
|
||||
required this.toStatus,
|
||||
required this.approver,
|
||||
required this.actionAt,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final ApprovalActionDto action;
|
||||
final ApprovalStatusDto? fromStatus;
|
||||
final ApprovalStatusDto toStatus;
|
||||
final ApprovalApproverDto approver;
|
||||
final DateTime actionAt;
|
||||
final String? note;
|
||||
|
||||
factory ApprovalHistoryDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalHistoryDto(
|
||||
id: json['id'] as int?,
|
||||
action: ApprovalActionDto.fromJson(
|
||||
(json['action'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
fromStatus: json['from_status'] is Map<String, dynamic>
|
||||
? ApprovalStatusDto.fromJson(
|
||||
json['from_status'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
toStatus: ApprovalStatusDto.fromJson(
|
||||
(json['to_status'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
approver: ApprovalApproverDto.fromJson(
|
||||
(json['approver'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
actionAt: _parseDate(json['action_at']) ?? DateTime.now(),
|
||||
note: json['note'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalHistory toEntity() => ApprovalHistory(
|
||||
id: id,
|
||||
action: action.toEntity(),
|
||||
fromStatus: fromStatus?.toEntity(),
|
||||
toStatus: toStatus.toEntity(),
|
||||
approver: approver.toEntity(),
|
||||
actionAt: actionAt,
|
||||
note: note,
|
||||
);
|
||||
}
|
||||
|
||||
class ApprovalActionDto {
|
||||
ApprovalActionDto({required this.id, required this.name});
|
||||
|
||||
final int id;
|
||||
final String name;
|
||||
|
||||
factory ApprovalActionDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalActionDto(
|
||||
id: json['id'] as int? ?? json['action_id'] as int? ?? 0,
|
||||
name: json['name'] as String? ?? json['action_name'] as String? ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalAction toEntity() => ApprovalAction(id: id, name: name);
|
||||
}
|
||||
|
||||
DateTime? _parseDate(Object? value) {
|
||||
if (value == null) return null;
|
||||
if (value is DateTime) return value;
|
||||
if (value is String) return DateTime.tryParse(value);
|
||||
return null;
|
||||
}
|
||||
149
lib/features/approvals/data/dtos/approval_template_dto.dart
Normal file
149
lib/features/approvals/data/dtos/approval_template_dto.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
import '../../domain/entities/approval_template.dart';
|
||||
|
||||
class ApprovalTemplateDto {
|
||||
ApprovalTemplateDto({
|
||||
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 ApprovalTemplateAuthorDto? createdBy;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final List<ApprovalTemplateStepDto> steps;
|
||||
|
||||
factory ApprovalTemplateDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalTemplateDto(
|
||||
id: json['id'] as int? ?? 0,
|
||||
code: json['template_code'] as String? ?? json['code'] as String? ?? '-',
|
||||
name: json['template_name'] as String? ?? json['name'] as String? ?? '-',
|
||||
description: json['description'] as String?,
|
||||
isActive: (json['is_active'] as bool?) ?? true,
|
||||
createdBy: json['created_by'] is Map<String, dynamic>
|
||||
? ApprovalTemplateAuthorDto.fromJson(
|
||||
json['created_by'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
createdAt: _parseDate(json['created_at']),
|
||||
updatedAt: _parseDate(json['updated_at']),
|
||||
steps: (json['steps'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalTemplateStepDto.fromJson)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalTemplate toEntity({bool includeSteps = true}) {
|
||||
return ApprovalTemplate(
|
||||
id: id,
|
||||
code: code,
|
||||
name: name,
|
||||
description: description,
|
||||
isActive: isActive,
|
||||
createdBy: createdBy?.toEntity(),
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
steps: includeSteps ? steps.map((e) => e.toEntity()).toList() : const [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ApprovalTemplateAuthorDto {
|
||||
ApprovalTemplateAuthorDto({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
|
||||
factory ApprovalTemplateAuthorDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalTemplateAuthorDto(
|
||||
id: json['id'] as int? ?? json['employee_id'] as int? ?? 0,
|
||||
employeeNo: json['employee_no'] as String? ?? '-',
|
||||
name: json['employee_name'] as String? ?? json['name'] as String? ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalTemplateAuthor toEntity() {
|
||||
return ApprovalTemplateAuthor(id: id, employeeNo: employeeNo, name: name);
|
||||
}
|
||||
}
|
||||
|
||||
class ApprovalTemplateStepDto {
|
||||
ApprovalTemplateStepDto({
|
||||
this.id,
|
||||
required this.stepOrder,
|
||||
required this.approver,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final int stepOrder;
|
||||
final ApprovalTemplateApproverDto approver;
|
||||
final String? note;
|
||||
|
||||
factory ApprovalTemplateStepDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalTemplateStepDto(
|
||||
id: json['id'] as int?,
|
||||
stepOrder: json['step_order'] as int? ?? 0,
|
||||
approver: ApprovalTemplateApproverDto.fromJson(
|
||||
(json['approver'] as Map<String, dynamic>? ?? const {}),
|
||||
),
|
||||
note: json['note'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalTemplateStep toEntity() {
|
||||
return ApprovalTemplateStep(
|
||||
id: id,
|
||||
stepOrder: stepOrder,
|
||||
approver: approver.toEntity(),
|
||||
note: note,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ApprovalTemplateApproverDto {
|
||||
ApprovalTemplateApproverDto({
|
||||
required this.id,
|
||||
required this.employeeNo,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String employeeNo;
|
||||
final String name;
|
||||
|
||||
factory ApprovalTemplateApproverDto.fromJson(Map<String, dynamic> json) {
|
||||
return ApprovalTemplateApproverDto(
|
||||
id: json['id'] as int? ?? json['approver_id'] as int? ?? 0,
|
||||
employeeNo: json['employee_no'] as String? ?? '-',
|
||||
name: json['employee_name'] as String? ?? json['name'] as String? ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
ApprovalTemplateApprover toEntity() {
|
||||
return ApprovalTemplateApprover(id: id, employeeNo: employeeNo, name: name);
|
||||
}
|
||||
}
|
||||
|
||||
DateTime? _parseDate(Object? value) {
|
||||
if (value == null) return null;
|
||||
if (value is DateTime) return value;
|
||||
if (value is String) return DateTime.tryParse(value);
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/network/api_client.dart';
|
||||
|
||||
import '../../domain/entities/approval.dart';
|
||||
import '../../domain/repositories/approval_repository.dart';
|
||||
import '../dtos/approval_dto.dart';
|
||||
|
||||
class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
ApprovalRepositoryRemote({required ApiClient apiClient}) : _api = apiClient;
|
||||
|
||||
final ApiClient _api;
|
||||
|
||||
static const _basePath = '/approvals';
|
||||
|
||||
@override
|
||||
Future<PaginatedResult<Approval>> list({
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
String? query,
|
||||
String? status,
|
||||
DateTime? from,
|
||||
DateTime? to,
|
||||
bool includeHistories = false,
|
||||
bool includeSteps = false,
|
||||
}) async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
_basePath,
|
||||
query: {
|
||||
'page': page,
|
||||
'page_size': pageSize,
|
||||
if (query != null && query.isNotEmpty) 'q': query,
|
||||
if (status != null && status.isNotEmpty) 'status': status,
|
||||
if (from != null) 'from': from.toIso8601String(),
|
||||
if (to != null) 'to': to.toIso8601String(),
|
||||
if (includeHistories) 'include_histories': true,
|
||||
if (includeSteps) 'include_steps': true,
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return ApprovalDto.parsePaginated(response.data ?? const {});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> fetchDetail(
|
||||
int id, {
|
||||
bool includeSteps = true,
|
||||
bool includeHistories = true,
|
||||
}) async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
'$_basePath/$id',
|
||||
query: {
|
||||
if (includeSteps) 'include_steps': true,
|
||||
if (includeHistories) 'include_histories': true,
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ApprovalAction>> listActions({bool activeOnly = true}) async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
'/approval-actions',
|
||||
query: {'page': 1, 'page_size': 100, if (activeOnly) 'active': true},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final items = (response.data?['items'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalActionDto.fromJson)
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList();
|
||||
return items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> performStepAction(ApprovalStepActionInput input) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'/approval-steps/${input.stepId}/actions',
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final approvalJson = _extractApprovalFromActionResponse(
|
||||
response.data ?? const <String, dynamic>{},
|
||||
);
|
||||
if (approvalJson == null) {
|
||||
throw StateError('결재 단계 행위 응답에 결재 데이터가 없습니다.');
|
||||
}
|
||||
return ApprovalDto.fromJson(approvalJson).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> assignSteps(ApprovalStepAssignmentInput input) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'/approvals/${input.approvalId}/steps',
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final approvalJson = _extractApprovalFromActionResponse(
|
||||
response.data ?? const <String, dynamic>{},
|
||||
);
|
||||
if (approvalJson == null) {
|
||||
throw StateError('결재 단계 일괄 처리 응답에 결재 데이터가 없습니다.');
|
||||
}
|
||||
return ApprovalDto.fromJson(approvalJson).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> create(ApprovalInput input) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
_basePath,
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> update(int id, ApprovalInput input) async {
|
||||
final response = await _api.patch<Map<String, dynamic>>(
|
||||
'$_basePath/$id',
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(int id) async {
|
||||
await _api.delete<void>('$_basePath/$id');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Approval> restore(int id) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
}
|
||||
|
||||
Map<String, dynamic>? _extractApprovalFromActionResponse(
|
||||
Map<String, dynamic> body,
|
||||
) {
|
||||
final data = body['data'];
|
||||
if (data is Map<String, dynamic>) {
|
||||
if (data['approval'] is Map<String, dynamic>) {
|
||||
return data['approval'] as Map<String, dynamic>;
|
||||
}
|
||||
if (data['approval_data'] is Map<String, dynamic>) {
|
||||
return data['approval_data'] as Map<String, dynamic>;
|
||||
}
|
||||
final hasStatus =
|
||||
data.containsKey('status') || data.containsKey('approval_status');
|
||||
if (data.containsKey('approval_no') && hasStatus) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if (body['approval'] is Map<String, dynamic>) {
|
||||
return body['approval'] as Map<String, dynamic>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../core/network/api_client.dart';
|
||||
import '../../domain/entities/approval_template.dart';
|
||||
import '../../domain/repositories/approval_template_repository.dart';
|
||||
import '../dtos/approval_template_dto.dart';
|
||||
|
||||
class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
ApprovalTemplateRepositoryRemote({required ApiClient apiClient})
|
||||
: _api = apiClient;
|
||||
|
||||
final ApiClient _api;
|
||||
|
||||
static const _basePath = '/approval-templates';
|
||||
|
||||
@override
|
||||
Future<List<ApprovalTemplate>> list({bool activeOnly = true}) async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
_basePath,
|
||||
query: {'page': 1, 'page_size': 100, if (activeOnly) 'active': true},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final items = (response.data?['items'] as List<dynamic>? ?? [])
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(ApprovalTemplateDto.fromJson)
|
||||
.map((dto) => dto.toEntity(includeSteps: false))
|
||||
.toList();
|
||||
return items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApprovalTemplate> fetchDetail(
|
||||
int id, {
|
||||
bool includeSteps = true,
|
||||
}) async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
'$_basePath/$id',
|
||||
query: {if (includeSteps) 'include': 'steps'},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalTemplateDto.fromJson(
|
||||
data,
|
||||
).toEntity(includeSteps: includeSteps);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user