전역 구조 리팩터링 및 테스트 확장
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
import 'package:superport_v2/core/common/utils/json_utils.dart';
|
||||
|
||||
import '../../domain/entities/stock_transaction.dart';
|
||||
|
||||
/// 재고 트랜잭션 DTO
|
||||
///
|
||||
/// - API 응답(JSON)을 도메인 엔티티로 변환하고, 요청 페이로드를 구성한다.
|
||||
class StockTransactionDto {
|
||||
StockTransactionDto({
|
||||
this.id,
|
||||
required this.transactionNo,
|
||||
required this.transactionDate,
|
||||
required this.type,
|
||||
required this.status,
|
||||
required this.warehouse,
|
||||
required this.createdBy,
|
||||
this.note,
|
||||
this.isActive = true,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.lines = const [],
|
||||
this.customers = const [],
|
||||
this.approval,
|
||||
this.expectedReturnDate,
|
||||
});
|
||||
|
||||
final int? id;
|
||||
final String transactionNo;
|
||||
final DateTime transactionDate;
|
||||
final StockTransactionType type;
|
||||
final StockTransactionStatus status;
|
||||
final StockTransactionWarehouse warehouse;
|
||||
final StockTransactionEmployee createdBy;
|
||||
final String? note;
|
||||
final bool isActive;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final List<StockTransactionLine> lines;
|
||||
final List<StockTransactionCustomer> customers;
|
||||
final StockTransactionApprovalSummary? approval;
|
||||
final DateTime? expectedReturnDate;
|
||||
|
||||
/// JSON 객체를 DTO로 변환한다.
|
||||
factory StockTransactionDto.fromJson(Map<String, dynamic> json) {
|
||||
final typeJson = json['transaction_type'] as Map<String, dynamic>?;
|
||||
final statusJson = json['transaction_status'] as Map<String, dynamic>?;
|
||||
final warehouseJson = json['warehouse'] as Map<String, dynamic>?;
|
||||
final createdByJson = json['created_by'] as Map<String, dynamic>?;
|
||||
|
||||
return StockTransactionDto(
|
||||
id: json['id'] as int?,
|
||||
transactionNo: json['transaction_no'] as String? ?? '',
|
||||
transactionDate: _parseDate(json['transaction_date']) ?? DateTime.now(),
|
||||
type: _parseType(typeJson),
|
||||
status: _parseStatus(statusJson),
|
||||
warehouse: _parseWarehouse(warehouseJson),
|
||||
createdBy: _parseEmployee(createdByJson),
|
||||
note: json['note'] as String?,
|
||||
isActive: (json['is_active'] as bool?) ?? true,
|
||||
createdAt: _parseDateTime(json['created_at']),
|
||||
updatedAt: _parseDateTime(json['updated_at']),
|
||||
lines: _parseLines(json),
|
||||
customers: _parseCustomers(json),
|
||||
approval: _parseApproval(json['approval']),
|
||||
expectedReturnDate:
|
||||
_parseDate(json['expected_return_date']) ??
|
||||
_parseDate(json['planned_return_date']) ??
|
||||
_parseDate(json['return_due_date']),
|
||||
);
|
||||
}
|
||||
|
||||
/// 도메인 엔티티로 변환한다.
|
||||
StockTransaction toEntity() {
|
||||
return StockTransaction(
|
||||
id: id,
|
||||
transactionNo: transactionNo,
|
||||
transactionDate: transactionDate,
|
||||
type: type,
|
||||
status: status,
|
||||
warehouse: warehouse,
|
||||
createdBy: createdBy,
|
||||
note: note,
|
||||
isActive: isActive,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
lines: lines,
|
||||
customers: customers,
|
||||
approval: approval,
|
||||
expectedReturnDate: expectedReturnDate,
|
||||
);
|
||||
}
|
||||
|
||||
/// 페이지네이션 응답을 파싱한다.
|
||||
static PaginatedResult<StockTransaction> parsePaginated(dynamic json) {
|
||||
final raw = JsonUtils.extractList(json, keys: const ['items']);
|
||||
final items = raw
|
||||
.map(StockTransactionDto.fromJson)
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList(growable: false);
|
||||
final map = json is Map<String, dynamic> ? json : <String, dynamic>{};
|
||||
return PaginatedResult<StockTransaction>(
|
||||
items: items,
|
||||
page: JsonUtils.readInt(map, 'page', fallback: 1),
|
||||
pageSize: JsonUtils.readInt(map, 'page_size', fallback: items.length),
|
||||
total: JsonUtils.readInt(map, 'total', fallback: items.length),
|
||||
);
|
||||
}
|
||||
|
||||
/// 단건 응답을 파싱한다.
|
||||
static StockTransaction? parseSingle(dynamic json) {
|
||||
final map = JsonUtils.extractMap(json, keys: const ['data']);
|
||||
if (map.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return StockTransactionDto.fromJson(map).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
StockTransactionType _parseType(Map<String, dynamic>? json) {
|
||||
return StockTransactionType(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
name: json?['type_name'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
StockTransactionStatus _parseStatus(Map<String, dynamic>? json) {
|
||||
return StockTransactionStatus(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
name: json?['status_name'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
StockTransactionWarehouse _parseWarehouse(Map<String, dynamic>? json) {
|
||||
final zipcode = json?['zipcode'] as Map<String, dynamic>?;
|
||||
return StockTransactionWarehouse(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
code: json?['warehouse_code'] as String? ?? '',
|
||||
name: json?['warehouse_name'] as String? ?? '',
|
||||
zipcode: zipcode?['zipcode'] as String?,
|
||||
addressLine: zipcode?['road_name'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
StockTransactionEmployee _parseEmployee(Map<String, dynamic>? json) {
|
||||
return StockTransactionEmployee(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
employeeNo: json?['employee_no'] as String? ?? '',
|
||||
name: json?['employee_name'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
List<StockTransactionLine> _parseLines(Map<String, dynamic> json) {
|
||||
final raw = JsonUtils.extractList(json, keys: const ['lines']);
|
||||
return [
|
||||
for (final item in raw)
|
||||
StockTransactionLine(
|
||||
id: item['id'] as int?,
|
||||
lineNo: JsonUtils.readInt(item, 'line_no', fallback: 1),
|
||||
product: _parseProduct(item['product'] as Map<String, dynamic>?),
|
||||
quantity: JsonUtils.readInt(item, 'quantity', fallback: 0),
|
||||
unitPrice: _readDouble(item['unit_price']),
|
||||
note: item['note'] as String?,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
StockTransactionProduct _parseProduct(Map<String, dynamic>? json) {
|
||||
final vendorJson = json?['vendor'] as Map<String, dynamic>?;
|
||||
final uomJson = json?['uom'] as Map<String, dynamic>?;
|
||||
return StockTransactionProduct(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
code: json?['product_code'] as String? ?? json?['code'] as String? ?? '',
|
||||
name: json?['product_name'] as String? ?? json?['name'] as String? ?? '',
|
||||
vendor: vendorJson == null
|
||||
? null
|
||||
: StockTransactionVendorSummary(
|
||||
id: vendorJson['id'] as int? ?? 0,
|
||||
name:
|
||||
vendorJson['vendor_name'] as String? ??
|
||||
vendorJson['name'] as String? ??
|
||||
'',
|
||||
),
|
||||
uom: uomJson == null
|
||||
? null
|
||||
: StockTransactionUomSummary(
|
||||
id: uomJson['id'] as int? ?? 0,
|
||||
name:
|
||||
uomJson['uom_name'] as String? ??
|
||||
uomJson['name'] as String? ??
|
||||
'',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<StockTransactionCustomer> _parseCustomers(Map<String, dynamic> json) {
|
||||
final raw = JsonUtils.extractList(json, keys: const ['customers']);
|
||||
return [
|
||||
for (final item in raw)
|
||||
StockTransactionCustomer(
|
||||
id: item['id'] as int?,
|
||||
customer: _parseCustomer(item['customer'] as Map<String, dynamic>?),
|
||||
note: item['note'] as String?,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
StockTransactionCustomerSummary _parseCustomer(Map<String, dynamic>? json) {
|
||||
return StockTransactionCustomerSummary(
|
||||
id: json?['id'] as int? ?? 0,
|
||||
code: json?['customer_code'] as String? ?? json?['code'] as String? ?? '',
|
||||
name: json?['customer_name'] as String? ?? json?['name'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
StockTransactionApprovalSummary? _parseApproval(dynamic raw) {
|
||||
if (raw is! Map<String, dynamic>) {
|
||||
return null;
|
||||
}
|
||||
final status = raw['approval_status'] as Map<String, dynamic>?;
|
||||
return StockTransactionApprovalSummary(
|
||||
id: raw['id'] as int? ?? 0,
|
||||
approvalNo: raw['approval_no'] as String? ?? '',
|
||||
status: status == null
|
||||
? null
|
||||
: StockTransactionApprovalStatusSummary(
|
||||
id: status['id'] as int? ?? 0,
|
||||
name:
|
||||
status['status_name'] as String? ??
|
||||
status['name'] as String? ??
|
||||
'',
|
||||
isBlocking: status['is_blocking_next'] as bool?,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
DateTime? _parseDate(Object? value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value is DateTime) {
|
||||
return value;
|
||||
}
|
||||
if (value is String && value.isNotEmpty) {
|
||||
return DateTime.tryParse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime? _parseDateTime(Object? value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value is DateTime) {
|
||||
return value;
|
||||
}
|
||||
if (value is String && value.isNotEmpty) {
|
||||
return DateTime.tryParse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
double _readDouble(Object? value) {
|
||||
if (value is double) {
|
||||
return value;
|
||||
}
|
||||
if (value is int) {
|
||||
return value.toDouble();
|
||||
}
|
||||
if (value is String) {
|
||||
return double.tryParse(value) ?? 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user