API v4 계약 반영하고 보고서·입출고 화면 실연동 강화

This commit is contained in:
JiWoong Sul
2025-10-16 14:57:07 +09:00
parent 7e0f7b1c55
commit d5c99627db
34 changed files with 1767 additions and 327 deletions

View File

@@ -214,7 +214,9 @@ class ApprovalStepDto {
(json['approver'] as Map<String, dynamic>? ?? const {}),
),
status: ApprovalStatusDto.fromJson(
(json['status'] as Map<String, dynamic>? ?? const {}),
(json['status'] as Map<String, dynamic>? ??
json['step_status'] as Map<String, dynamic>? ??
const {}),
),
assignedAt: _parseDate(json['assigned_at']) ?? DateTime.now(),
decidedAt: _parseDate(json['decided_at']),
@@ -263,7 +265,12 @@ class ApprovalHistoryDto {
return ApprovalHistoryDto(
id: json['id'] as int?,
action: ApprovalActionDto.fromJson(
(json['action'] as Map<String, dynamic>? ?? const {}),
json['action'] is Map<String, dynamic>
? json['action'] as Map<String, dynamic>
: {
'id': json['approval_action_id'],
'name': json['approval_action_name'],
},
),
fromStatus: json['from_status'] is Map<String, dynamic>
? ApprovalStatusDto.fromJson(

View File

@@ -175,22 +175,199 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
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>;
final dataMap = data is Map<String, dynamic> ? data : null;
Map<String, dynamic>? approval = _selectApprovalPayload(dataMap);
approval ??= _selectApprovalPayload(body);
if (approval == null) {
return null;
}
final merged = Map<String, dynamic>.from(approval);
if (dataMap != null) {
final steps = _mergeStepsPayload(
existing: merged['steps'],
data: dataMap,
);
if (steps != null) {
merged['steps'] = steps;
}
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;
final histories = _mergeHistoriesPayload(
existing: merged['histories'],
data: dataMap,
);
if (histories != null) {
merged['histories'] = histories;
}
}
if (body['approval'] is Map<String, dynamic>) {
return body['approval'] as Map<String, dynamic>;
return merged;
}
Map<String, dynamic>? _selectApprovalPayload(Map<String, dynamic>? source) {
if (source == null) {
return null;
}
if (source['approval'] is Map<String, dynamic>) {
return Map<String, dynamic>.from(
source['approval'] as Map<String, dynamic>,
);
}
if (source['approval_data'] is Map<String, dynamic>) {
return Map<String, dynamic>.from(
source['approval_data'] as Map<String, dynamic>,
);
}
final hasStatus =
source.containsKey('status') || source.containsKey('approval_status');
if (source.containsKey('approval_no') && hasStatus) {
return Map<String, dynamic>.from(source);
}
if (source['approval'] == null && source['data'] is Map<String, dynamic>) {
return _selectApprovalPayload(source['data'] as Map<String, dynamic>);
}
return null;
}
List<Map<String, dynamic>>? _mergeStepsPayload({
required dynamic existing,
required Map<String, dynamic> data,
}) {
final steps = <Map<String, dynamic>>[];
void upsert(Map<String, dynamic> step) {
final id = step['id'] as int?;
final order = step['step_order'] as int?;
final index = steps.indexWhere((element) {
final elementId = element['id'] as int?;
if (elementId != null && id != null) {
return elementId == id;
}
if (order != null) {
return element['step_order'] == order;
}
return false;
});
final copy = Map<String, dynamic>.from(step);
if (index >= 0) {
steps[index] = copy;
} else {
steps.add(copy);
}
}
if (existing is List) {
for (final item in existing) {
if (item is Map<String, dynamic>) {
upsert(item);
}
}
}
final responseSteps = data['steps'];
if (responseSteps is List) {
for (final item in responseSteps) {
if (item is Map<String, dynamic>) {
upsert(item);
}
}
}
if (data['step'] is Map<String, dynamic>) {
upsert(data['step'] as Map<String, dynamic>);
}
if (data['next_step'] is Map<String, dynamic>) {
upsert(data['next_step'] as Map<String, dynamic>);
}
if (steps.isEmpty) {
return existing is List
? existing
.whereType<Map<String, dynamic>>()
.map((step) => Map<String, dynamic>.from(step))
.toList()
: null;
}
steps.sort((a, b) {
final orderA = a['step_order'] as int? ?? 0;
final orderB = b['step_order'] as int? ?? 0;
return orderA.compareTo(orderB);
});
return steps;
}
List<Map<String, dynamic>>? _mergeHistoriesPayload({
required dynamic existing,
required Map<String, dynamic> data,
}) {
final histories = <Map<String, dynamic>>[];
void append(Map<String, dynamic> history) {
histories.add(Map<String, dynamic>.from(history));
}
if (existing is List) {
for (final item in existing) {
if (item is Map<String, dynamic>) {
append(item);
}
}
}
final responseHistories = data['histories'];
if (responseHistories is List) {
for (final item in responseHistories) {
if (item is Map<String, dynamic>) {
append(item);
}
}
}
if (data['history'] is Map<String, dynamic>) {
append(data['history'] as Map<String, dynamic>);
}
if (histories.isEmpty) {
return existing is List
? existing
.whereType<Map<String, dynamic>>()
.map((history) => Map<String, dynamic>.from(history))
.toList()
: null;
}
DateTime? parseTime(Map<String, dynamic> json) {
String? read(dynamic value) {
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
return null;
}
final raw =
read(json['action_at']) ??
read(json['created_at']) ??
read(json['updated_at']);
if (raw == null) {
return null;
}
return DateTime.tryParse(raw);
}
histories.sort((a, b) {
final timeA = parseTime(a);
final timeB = parseTime(b);
if (timeA == null && timeB == null) {
return 0;
}
if (timeA == null) {
return 1;
}
if (timeB == null) {
return -1;
}
return timeA.compareTo(timeB);
});
return histories;
}
}