refactor: 프로젝트 구조 개선 및 테스트 시스템 강화
주요 변경사항: - CLAUDE.md: 프로젝트 규칙 v2.0으로 업데이트, 아키텍처 명확화 - 불필요한 문서 제거: NEXT_TASKS.md, TEST_PROGRESS.md, test_results 파일들 - 테스트 시스템 개선: 실제 API 테스트 스위트 추가 (15개 새 테스트 파일) - License 관리: DTO 모델 개선, API 응답 처리 최적화 - 에러 처리: Interceptor 로직 강화, 상세 로깅 추가 - Company/User/Warehouse 테스트: 자동화 테스트 안정성 향상 - Phone Utils: 전화번호 포맷팅 로직 개선 - Overview Controller: 대시보드 데이터 로딩 최적화 - Analysis Options: Flutter 린트 규칙 추가 테스트 개선: - company_real_api_test.dart: 실제 API 회사 관리 테스트 - equipment_in/out_real_api_test.dart: 장비 입출고 API 테스트 - license_real_api_test.dart: 라이선스 관리 API 테스트 - user_real_api_test.dart: 사용자 관리 API 테스트 - warehouse_location_real_api_test.dart: 창고 위치 API 테스트 - filter_sort_test.dart: 필터링/정렬 기능 테스트 - pagination_test.dart: 페이지네이션 테스트 - interactive_search_test.dart: 검색 기능 테스트 - overview_dashboard_test.dart: 대시보드 통합 테스트 코드 품질: - 모든 서비스에 에러 처리 강화 - DTO 모델 null safety 개선 - 테스트 커버리지 확대 - 불필요한 로그 파일 제거로 리포지토리 정리 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,11 @@ import 'package:superport/models/equipment_unified_model.dart';
|
||||
import 'package:superport/models/company_model.dart';
|
||||
import 'package:superport/services/equipment_service.dart';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
import 'package:superport/services/warehouse_service.dart';
|
||||
import 'package:superport/services/company_service.dart';
|
||||
import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/core/utils/debug_logger.dart';
|
||||
|
||||
/// 장비 입고 폼 컨트롤러
|
||||
///
|
||||
@@ -13,6 +16,8 @@ import 'package:superport/core/errors/failures.dart';
|
||||
class EquipmentInFormController extends ChangeNotifier {
|
||||
final MockDataService dataService;
|
||||
final EquipmentService _equipmentService = GetIt.instance<EquipmentService>();
|
||||
final WarehouseService _warehouseService = GetIt.instance<WarehouseService>();
|
||||
final CompanyService _companyService = GetIt.instance<CompanyService>();
|
||||
final int? equipmentInId;
|
||||
|
||||
bool _isLoading = false;
|
||||
@@ -55,6 +60,9 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
List<String> categories = [];
|
||||
List<String> subCategories = [];
|
||||
List<String> subSubCategories = [];
|
||||
|
||||
// 창고 위치 전체 데이터 (이름-ID 매핑용)
|
||||
Map<String, int> warehouseLocationMap = {};
|
||||
|
||||
// 편집 모드 여부
|
||||
bool isEditMode = false;
|
||||
@@ -108,19 +116,66 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// 입고지 목록 로드
|
||||
void _loadWarehouseLocations() {
|
||||
warehouseLocations =
|
||||
dataService.getAllWarehouseLocations().map((e) => e.name).toList();
|
||||
void _loadWarehouseLocations() async {
|
||||
if (_useApi) {
|
||||
try {
|
||||
DebugLogger.log('입고지 목록 API 로드 시작', tag: 'EQUIPMENT_IN');
|
||||
final locations = await _warehouseService.getWarehouseLocations();
|
||||
warehouseLocations = locations.map((e) => e.name).toList();
|
||||
// 이름-ID 매핑 저장
|
||||
warehouseLocationMap = {for (var loc in locations) loc.name: loc.id};
|
||||
DebugLogger.log('입고지 목록 로드 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'count': warehouseLocations.length,
|
||||
'locations': warehouseLocations,
|
||||
'locationMap': warehouseLocationMap,
|
||||
});
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
DebugLogger.logError('입고지 목록 로드 실패', error: e);
|
||||
// 실패 시 Mock 데이터 사용
|
||||
final mockLocations = dataService.getAllWarehouseLocations();
|
||||
warehouseLocations = mockLocations.map((e) => e.name).toList();
|
||||
warehouseLocationMap = {for (var loc in mockLocations) loc.name: loc.id};
|
||||
notifyListeners();
|
||||
}
|
||||
} else {
|
||||
final mockLocations = dataService.getAllWarehouseLocations();
|
||||
warehouseLocations = mockLocations.map((e) => e.name).toList();
|
||||
warehouseLocationMap = {for (var loc in mockLocations) loc.name: loc.id};
|
||||
}
|
||||
}
|
||||
|
||||
// 파트너사 목록 로드
|
||||
void _loadPartnerCompanies() {
|
||||
partnerCompanies =
|
||||
dataService
|
||||
.getAllCompanies()
|
||||
.where((c) => c.companyTypes.contains(CompanyType.partner))
|
||||
.map((c) => c.name)
|
||||
.toList();
|
||||
void _loadPartnerCompanies() async {
|
||||
if (_useApi) {
|
||||
try {
|
||||
DebugLogger.log('파트너사 목록 API 로드 시작', tag: 'EQUIPMENT_IN');
|
||||
final companies = await _companyService.getCompanies();
|
||||
partnerCompanies = companies.map((c) => c.name).toList();
|
||||
DebugLogger.log('파트너사 목록 로드 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'count': partnerCompanies.length,
|
||||
'companies': partnerCompanies,
|
||||
});
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
DebugLogger.logError('파트너사 목록 로드 실패', error: e);
|
||||
// 실패 시 Mock 데이터 사용
|
||||
partnerCompanies =
|
||||
dataService
|
||||
.getAllCompanies()
|
||||
.where((c) => c.companyTypes.contains(CompanyType.partner))
|
||||
.map((c) => c.name)
|
||||
.toList();
|
||||
notifyListeners();
|
||||
}
|
||||
} else {
|
||||
partnerCompanies =
|
||||
dataService
|
||||
.getAllCompanies()
|
||||
.where((c) => c.companyTypes.contains(CompanyType.partner))
|
||||
.map((c) => c.name)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
// 워런티 라이센스 목록 로드
|
||||
@@ -304,30 +359,50 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
await _equipmentService.updateEquipment(equipmentInId!, equipment);
|
||||
} else {
|
||||
// 생성 모드
|
||||
// 1. 먼저 장비 생성
|
||||
final createdEquipment = await _equipmentService.createEquipment(equipment);
|
||||
|
||||
// 2. 입고 처리 (warehouse location ID 필요)
|
||||
int? warehouseLocationId;
|
||||
if (warehouseLocation != null) {
|
||||
// TODO: 창고 위치 ID 가져오기 - 현재는 목 데이터에서 찾기
|
||||
try {
|
||||
final warehouse = dataService.getAllWarehouseLocations().firstWhere(
|
||||
(w) => w.name == warehouseLocation,
|
||||
);
|
||||
warehouseLocationId = warehouse.id;
|
||||
} catch (e) {
|
||||
// 창고를 찾을 수 없는 경우
|
||||
warehouseLocationId = null;
|
||||
try {
|
||||
// 1. 먼저 장비 생성
|
||||
DebugLogger.log('장비 생성 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'manufacturer': manufacturer,
|
||||
'name': name,
|
||||
'serialNumber': serialNumber,
|
||||
});
|
||||
|
||||
final createdEquipment = await _equipmentService.createEquipment(equipment);
|
||||
|
||||
DebugLogger.log('장비 생성 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'equipmentId': createdEquipment.id,
|
||||
});
|
||||
|
||||
// 2. 입고 처리 (warehouse location ID 필요)
|
||||
int? warehouseLocationId;
|
||||
if (warehouseLocation != null) {
|
||||
// 저장된 매핑에서 ID 가져오기
|
||||
warehouseLocationId = warehouseLocationMap[warehouseLocation];
|
||||
|
||||
if (warehouseLocationId == null) {
|
||||
DebugLogger.logError('창고 위치 ID를 찾을 수 없음', error: 'Warehouse: $warehouseLocation');
|
||||
}
|
||||
}
|
||||
|
||||
DebugLogger.log('입고 처리 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'equipmentId': createdEquipment.id,
|
||||
'quantity': quantity,
|
||||
'warehouseLocationId': warehouseLocationId,
|
||||
});
|
||||
|
||||
await _equipmentService.equipmentIn(
|
||||
equipmentId: createdEquipment.id!,
|
||||
quantity: quantity,
|
||||
warehouseLocationId: warehouseLocationId,
|
||||
notes: remarkController.text.trim(),
|
||||
);
|
||||
|
||||
DebugLogger.log('입고 처리 성공', tag: 'EQUIPMENT_IN');
|
||||
|
||||
} catch (e) {
|
||||
DebugLogger.logError('장비 입고 처리 실패', error: e);
|
||||
throw e; // 에러를 상위로 전파하여 적절한 에러 메시지 표시
|
||||
}
|
||||
|
||||
await _equipmentService.equipmentIn(
|
||||
equipmentId: createdEquipment.id!,
|
||||
quantity: quantity,
|
||||
warehouseLocationId: warehouseLocationId,
|
||||
notes: remarkController.text.trim(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Mock 데이터 사용
|
||||
|
||||
@@ -22,6 +22,7 @@ class EquipmentListController extends ChangeNotifier {
|
||||
|
||||
List<UnifiedEquipment> equipments = [];
|
||||
String? selectedStatusFilter;
|
||||
String searchKeyword = ''; // 검색어 추가
|
||||
final Set<String> selectedEquipmentIds = {}; // 'id:status' 형식
|
||||
|
||||
bool _isLoading = false;
|
||||
@@ -42,7 +43,7 @@ class EquipmentListController extends ChangeNotifier {
|
||||
EquipmentListController({required this.dataService});
|
||||
|
||||
// 데이터 로드 및 상태 필터 적용
|
||||
Future<void> loadData({bool isRefresh = false}) async {
|
||||
Future<void> loadData({bool isRefresh = false, String? search}) async {
|
||||
if (isRefresh) {
|
||||
_currentPage = 1;
|
||||
_hasMore = true;
|
||||
@@ -69,6 +70,7 @@ class EquipmentListController extends ChangeNotifier {
|
||||
page: _currentPage,
|
||||
perPage: _perPage,
|
||||
status: selectedStatusFilter != null ? EquipmentStatusConverter.clientToServer(selectedStatusFilter) : null,
|
||||
search: search ?? searchKeyword,
|
||||
);
|
||||
|
||||
DebugLogger.log('장비 목록 API 응답', tag: 'EQUIPMENT', data: {
|
||||
@@ -137,6 +139,12 @@ class EquipmentListController extends ChangeNotifier {
|
||||
selectedStatusFilter = status;
|
||||
await loadData(isRefresh: true);
|
||||
}
|
||||
|
||||
// 검색어 변경
|
||||
Future<void> updateSearchKeyword(String keyword) async {
|
||||
searchKeyword = keyword;
|
||||
await loadData(isRefresh: true, search: keyword);
|
||||
}
|
||||
|
||||
// 장비 선택/해제 (모든 상태 지원)
|
||||
void selectEquipment(int? id, String status, bool? isSelected) {
|
||||
|
||||
@@ -116,11 +116,12 @@ class _EquipmentListRedesignState extends State<EquipmentListRedesign> {
|
||||
}
|
||||
|
||||
/// 검색 실행
|
||||
void _onSearch() {
|
||||
void _onSearch() async {
|
||||
setState(() {
|
||||
_appliedSearchKeyword = _searchController.text;
|
||||
_currentPage = 1;
|
||||
});
|
||||
await _controller.updateSearchKeyword(_searchController.text);
|
||||
}
|
||||
|
||||
/// 장비 선택/해제
|
||||
|
||||
Reference in New Issue
Block a user