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

@@ -84,6 +84,7 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
Future<StockTransaction> submit(int id) async {
final response = await _api.post<Map<String, dynamic>>(
'$_basePath/$id/submit',
data: {'id': id},
options: Options(responseType: ResponseType.json),
);
return _parseSingle(response.data);
@@ -93,6 +94,7 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
Future<StockTransaction> complete(int id) async {
final response = await _api.post<Map<String, dynamic>>(
'$_basePath/$id/complete',
data: {'id': id},
options: Options(responseType: ResponseType.json),
);
return _parseSingle(response.data);
@@ -102,6 +104,7 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
Future<StockTransaction> approve(int id) async {
final response = await _api.post<Map<String, dynamic>>(
'$_basePath/$id/approve',
data: {'id': id},
options: Options(responseType: ResponseType.json),
);
return _parseSingle(response.data);
@@ -111,6 +114,7 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
Future<StockTransaction> reject(int id) async {
final response = await _api.post<Map<String, dynamic>>(
'$_basePath/$id/reject',
data: {'id': id},
options: Options(responseType: ResponseType.json),
);
return _parseSingle(response.data);
@@ -120,6 +124,7 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
Future<StockTransaction> cancel(int id) async {
final response = await _api.post<Map<String, dynamic>>(
'$_basePath/$id/cancel',
data: {'id': id},
options: Options(responseType: ResponseType.json),
);
return _parseSingle(response.data);

View File

@@ -1,11 +1,8 @@
import 'package:dio/dio.dart';
import 'package:superport_v2/core/network/api_client.dart';
import 'package:superport_v2/core/network/api_routes.dart';
import '../../domain/entities/stock_transaction.dart';
import '../../domain/entities/stock_transaction_input.dart';
import '../../domain/repositories/stock_transaction_repository.dart';
import '../dtos/stock_transaction_dto.dart';
/// 재고 트랜잭션 고객 연결 API를 호출하는 원격 저장소 구현체.
class TransactionCustomerRepositoryRemote
@@ -19,11 +16,11 @@ class TransactionCustomerRepositoryRemote
static const _customerPath = '${ApiRoutes.apiV1}/transaction-customers';
@override
Future<List<StockTransactionCustomer>> addCustomers(
Future<void> addCustomers(
int transactionId,
List<TransactionCustomerCreateInput> customers,
) async {
final response = await _api.post<Map<String, dynamic>>(
await _api.post<void>(
'$_basePath/$transactionId/customers',
data: {
'id': transactionId,
@@ -31,17 +28,15 @@ class TransactionCustomerRepositoryRemote
.map((customer) => customer.toJson())
.toList(growable: false),
},
options: Options(responseType: ResponseType.json),
);
return _parseCustomers(response.data);
}
@override
Future<List<StockTransactionCustomer>> updateCustomers(
Future<void> updateCustomers(
int transactionId,
List<TransactionCustomerUpdateInput> customers,
) async {
final response = await _api.patch<Map<String, dynamic>>(
await _api.patch<void>(
'$_basePath/$transactionId/customers',
data: {
'id': transactionId,
@@ -49,38 +44,11 @@ class TransactionCustomerRepositoryRemote
.map((customer) => customer.toJson())
.toList(growable: false),
},
options: Options(responseType: ResponseType.json),
);
return _parseCustomers(response.data);
}
@override
Future<void> deleteCustomer(int customerLinkId) async {
await _api.delete<void>('$_customerPath/$customerLinkId');
}
List<StockTransactionCustomer> _parseCustomers(Map<String, dynamic>? body) {
final data = _extractData(body);
if (data['customers'] is List) {
final dto = StockTransactionDto.fromJson(data);
return dto.customers;
}
if (data.containsKey('id')) {
final dto = StockTransactionDto.fromJson({
'customers': [data],
});
return dto.customers;
}
return const [];
}
Map<String, dynamic> _extractData(Map<String, dynamic>? body) {
if (body == null) {
return <String, dynamic>{};
}
if (body['data'] is Map<String, dynamic>) {
return body['data'] as Map<String, dynamic>;
}
return body;
}
}

View File

@@ -1,11 +1,8 @@
import 'package:dio/dio.dart';
import 'package:superport_v2/core/network/api_client.dart';
import 'package:superport_v2/core/network/api_routes.dart';
import '../../domain/entities/stock_transaction.dart';
import '../../domain/entities/stock_transaction_input.dart';
import '../../domain/repositories/stock_transaction_repository.dart';
import '../dtos/stock_transaction_dto.dart';
/// 재고 트랜잭션 라인 API를 호출하는 원격 저장소 구현체.
class TransactionLineRepositoryRemote implements TransactionLineRepository {
@@ -18,35 +15,31 @@ class TransactionLineRepositoryRemote implements TransactionLineRepository {
static const _linePath = '${ApiRoutes.apiV1}/transaction-lines';
@override
Future<List<StockTransactionLine>> addLines(
Future<void> addLines(
int transactionId,
List<TransactionLineCreateInput> lines,
) async {
final response = await _api.post<Map<String, dynamic>>(
await _api.post<void>(
'$_basePath/$transactionId/lines',
data: {
'id': transactionId,
'lines': lines.map((line) => line.toJson()).toList(growable: false),
},
options: Options(responseType: ResponseType.json),
);
return _parseLines(response.data);
}
@override
Future<List<StockTransactionLine>> updateLines(
Future<void> updateLines(
int transactionId,
List<TransactionLineUpdateInput> lines,
) async {
final response = await _api.patch<Map<String, dynamic>>(
await _api.patch<void>(
'$_basePath/$transactionId/lines',
data: {
'id': transactionId,
'lines': lines.map((line) => line.toJson()).toList(growable: false),
},
options: Options(responseType: ResponseType.json),
);
return _parseLines(response.data);
}
@override
@@ -55,40 +48,10 @@ class TransactionLineRepositoryRemote implements TransactionLineRepository {
}
@override
Future<StockTransactionLine> restoreLine(int lineId) async {
final response = await _api.post<Map<String, dynamic>>(
Future<void> restoreLine(int lineId) async {
await _api.post<void>(
'$_linePath/$lineId/restore',
options: Options(responseType: ResponseType.json),
);
final lines = _parseLines(response.data);
if (lines.isEmpty) {
throw StateError('복구된 라인 정보를 찾을 수 없습니다.');
}
return lines.first;
}
List<StockTransactionLine> _parseLines(Map<String, dynamic>? body) {
final data = _extractData(body);
if (data['lines'] is List) {
final dto = StockTransactionDto.fromJson(data);
return dto.lines;
}
if (data.containsKey('id')) {
final dto = StockTransactionDto.fromJson({
'lines': [data],
});
return dto.lines;
}
return const [];
}
Map<String, dynamic> _extractData(Map<String, dynamic>? body) {
if (body == null) {
return <String, dynamic>{};
}
if (body['data'] is Map<String, dynamic>) {
return body['data'] as Map<String, dynamic>;
}
return body;
}
}

View File

@@ -11,6 +11,7 @@ class StockTransactionCreateInput {
this.expectedReturnDate,
this.lines = const [],
this.customers = const [],
this.approval,
});
final String? transactionNo;
@@ -23,6 +24,7 @@ class StockTransactionCreateInput {
final DateTime? expectedReturnDate;
final List<TransactionLineCreateInput> lines;
final List<TransactionCustomerCreateInput> customers;
final StockTransactionApprovalInput? approval;
Map<String, dynamic> toPayload() {
return {
@@ -42,6 +44,7 @@ class StockTransactionCreateInput {
'customers': customers
.map((customer) => customer.toJson())
.toList(growable: false),
if (approval != null) 'approval': approval!.toJson(),
};
}
}
@@ -200,3 +203,27 @@ class StockTransactionListFilter {
};
}
}
/// 재고 트랜잭션 생성 시 결재(Approval) 정보를 담는 입력 모델.
class StockTransactionApprovalInput {
StockTransactionApprovalInput({
required this.approvalNo,
required this.requestedById,
this.approvalStatusId,
this.note,
});
final String approvalNo;
final int requestedById;
final int? approvalStatusId;
final String? note;
Map<String, dynamic> toJson() {
return {
'approval_no': approvalNo,
if (approvalStatusId != null) 'approval_status_id': approvalStatusId,
'requested_by_id': requestedById,
if (note != null && note!.trim().isNotEmpty) 'note': note,
};
}
}

View File

@@ -47,13 +47,13 @@ abstract class StockTransactionRepository {
/// 재고 트랜잭션 라인 저장소 인터페이스.
abstract class TransactionLineRepository {
/// 라인을 추가한다.
Future<List<StockTransactionLine>> addLines(
Future<void> addLines(
int transactionId,
List<TransactionLineCreateInput> lines,
);
/// 라인 정보를 일괄 수정한다.
Future<List<StockTransactionLine>> updateLines(
Future<void> updateLines(
int transactionId,
List<TransactionLineUpdateInput> lines,
);
@@ -62,19 +62,19 @@ abstract class TransactionLineRepository {
Future<void> deleteLine(int lineId);
/// 삭제된 라인을 복구한다.
Future<StockTransactionLine> restoreLine(int lineId);
Future<void> restoreLine(int lineId);
}
/// 재고 트랜잭션 고객 연결 저장소 인터페이스.
abstract class TransactionCustomerRepository {
/// 고객 연결을 추가한다.
Future<List<StockTransactionCustomer>> addCustomers(
Future<void> addCustomers(
int transactionId,
List<TransactionCustomerCreateInput> customers,
);
/// 고객 연결 정보를 수정한다.
Future<List<StockTransactionCustomer>> updateCustomers(
Future<void> updateCustomers(
int transactionId,
List<TransactionCustomerUpdateInput> customers,
);