Files
superport/lib/services/inventory_history_service.dart
JiWoong Sul 519e1883a3
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled
feat: V/R 유지보수 시스템 전환 및 대시보드 테이블 형태 완성
- V/R 시스템 완전 전환: WARRANTY/CONTRACT/INSPECTION → V(방문)/R(원격)
- 유지보수 대시보드 카드 → StandardDataTable 테이블 형태 전환
- "조회중..." 문제 해결: 백엔드 직접 필드 사용 (equipment_model, company_name)
- MaintenanceDto 신규 필드 추가: company_id, company_name, equipment_serial, equipment_model
- preloadEquipmentData 비활성화로 불필요한 equipment-history API 호출 제거
- CO-STAR 프레임워크 적용 및 CLAUDE.md v3.0 업데이트
- Flutter Analyze ERROR: 0 유지, 100% shadcn_ui 컴플라이언스

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-05 14:33:20 +09:00

248 lines
8.7 KiB
Dart

import 'package:get_it/get_it.dart';
import 'package:superport/data/models/inventory_history_view_model.dart';
import 'package:superport/data/models/equipment_history_dto.dart';
import 'package:superport/data/models/equipment/equipment_dto.dart';
import 'package:superport/data/repositories/equipment_history_repository.dart';
import 'package:superport/domain/usecases/equipment/get_equipment_detail_usecase.dart';
import 'package:superport/core/constants/app_constants.dart';
/// 재고 이력 관리 화면 전용 서비스
/// 백엔드 여러 API를 조합하여 화면용 데이터 제공
class InventoryHistoryService {
final EquipmentHistoryRepository _historyRepository;
final GetEquipmentDetailUseCase _equipmentDetailUseCase;
InventoryHistoryService({
EquipmentHistoryRepository? historyRepository,
GetEquipmentDetailUseCase? equipmentDetailUseCase,
}) : _historyRepository = historyRepository ?? GetIt.instance<EquipmentHistoryRepository>(),
_equipmentDetailUseCase = equipmentDetailUseCase ?? GetIt.instance<GetEquipmentDetailUseCase>();
/// 재고 이력 목록 로드 (여러 API 조합)
Future<InventoryHistoryListResponse> loadInventoryHistories({
int page = 1,
int pageSize = AppConstants.historyPageSize,
String? searchKeyword,
String? transactionType,
int? equipmentId,
int? warehouseId,
int? companyId,
DateTime? dateFrom,
DateTime? dateTo,
}) async {
try {
// 1. Equipment History 기본 데이터 로드
final historyResponse = await _historyRepository.getEquipmentHistories(
page: page,
pageSize: pageSize,
transactionType: transactionType,
equipmentsId: equipmentId,
warehousesId: warehouseId,
startDate: dateFrom?.toIso8601String(),
endDate: dateTo?.toIso8601String(),
);
// 2. 각 이력에 대해 추가 정보 조합
final List<InventoryHistoryViewModel> enrichedItems = [];
for (final history in historyResponse.items) {
try {
final viewModel = await _enrichHistoryWithDetails(history, searchKeyword);
if (viewModel != null) {
enrichedItems.add(viewModel);
}
} catch (e) {
print('[InventoryHistoryService] Failed to enrich history ${history.id}: $e');
// 에러 발생해도 기본 정보로라도 표시
final fallbackViewModel = _createFallbackViewModel(history);
enrichedItems.add(fallbackViewModel);
}
}
// 3. 검색 키워드 필터링 (서버 검색 후 추가 로컬 필터링)
List<InventoryHistoryViewModel> filteredItems = enrichedItems;
if (searchKeyword != null && searchKeyword.isNotEmpty) {
filteredItems = _applyLocalSearch(enrichedItems, searchKeyword);
}
return InventoryHistoryListResponse(
items: filteredItems,
totalCount: historyResponse.totalCount,
currentPage: historyResponse.currentPage,
totalPages: historyResponse.totalPages,
pageSize: historyResponse.pageSize,
);
} catch (e) {
print('[InventoryHistoryService] Error loading inventory histories: $e');
rethrow;
}
}
/// 특정 장비의 전체 이력 로드 (상세보기용)
Future<List<InventoryHistoryViewModel>> loadEquipmentHistory(int equipmentId) async {
try {
// 해당 장비의 모든 이력을 시간순(최신순)으로 로드
final historyResponse = await _historyRepository.getEquipmentHistories(
equipmentsId: equipmentId,
page: 1,
pageSize: AppConstants.maxBulkPageSize, // 모든 이력 로드
);
final List<InventoryHistoryViewModel> items = [];
for (final history in historyResponse.items) {
try {
final viewModel = await _enrichHistoryWithDetails(history);
if (viewModel != null) {
items.add(viewModel);
}
} catch (e) {
print('[InventoryHistoryService] Failed to enrich equipment history ${history.id}: $e');
final fallbackViewModel = _createFallbackViewModel(history);
items.add(fallbackViewModel);
}
}
// 시간순 정렬 (최신순)
items.sort((a, b) => b.changedDate.compareTo(a.changedDate));
return items;
} catch (e) {
print('[InventoryHistoryService] Error loading equipment history: $e');
rethrow;
}
}
/// History 데이터에 추가 정보를 조합하여 ViewModel 생성
Future<InventoryHistoryViewModel?> _enrichHistoryWithDetails(
EquipmentHistoryDto history,
[String? searchKeyword]
) async {
try {
// Equipment 상세 정보 로드
EquipmentDto? equipmentDetail;
if (history.equipmentsId != null) {
final equipmentResult = await _equipmentDetailUseCase(history.equipmentsId);
equipmentResult.fold(
(failure) {
print('[InventoryHistoryService] Failed to load equipment ${history.equipmentsId}: ${failure.message}');
},
(equipment) {
equipmentDetail = equipment;
},
);
}
// 장비명 결정 (Equipment API에서 가져오거나 fallback)
final String equipmentName = _determineEquipmentName(equipmentDetail, history);
// 시리얼번호 결정
final String serialNumber = _determineSerialNumber(equipmentDetail, history);
// 위치 결정 (transaction_type에 따라 다르게)
final String location = _determineLocation(history);
return InventoryHistoryViewModel(
historyId: history.id ?? 0,
equipmentId: history.equipmentsId,
equipmentName: equipmentName,
serialNumber: serialNumber,
location: location,
changedDate: history.transactedAt,
remark: history.remark,
transactionType: history.transactionType,
quantity: history.quantity,
originalHistory: history,
);
} catch (e) {
print('[InventoryHistoryService] Error enriching history ${history.id}: $e');
return null;
}
}
/// 장비명 결정 로직
String _determineEquipmentName(EquipmentDto? equipment, EquipmentHistoryDto history) {
if (equipment != null) {
// Equipment API에서 가져온 정보 우선 사용
if (equipment.modelName != null && equipment.vendorName != null) {
return '${equipment.vendorName} ${equipment.modelName}';
} else if (equipment.modelName != null) {
return equipment.modelName!;
}
}
// Fallback: History의 equipment_serial 사용
if (history.equipmentSerial != null) {
return history.equipmentSerial!;
}
return 'Unknown Equipment';
}
/// 시리얼번호 결정 로직
String _determineSerialNumber(EquipmentDto? equipment, EquipmentHistoryDto history) {
if (equipment != null && equipment.serialNumber != null) {
return equipment.serialNumber!;
}
if (history.equipmentSerial != null) {
return history.equipmentSerial!;
}
return 'N/A';
}
/// 위치 결정 로직 (transaction_type에 따라 다르게)
String _determineLocation(EquipmentHistoryDto history) {
switch (history.transactionType) {
case 'O': // 출고
case 'R': // 대여
// 고객사 정보 사용
if (history.companies.isNotEmpty) {
final company = history.companies.first;
return company['name']?.toString() ?? 'Unknown Company';
}
return 'Unknown Company';
case 'I': // 입고
case 'D': // 폐기
// 창고 정보 사용
return history.warehouseName ?? 'Unknown Warehouse';
default:
return 'N/A';
}
}
/// 에러 발생 시 fallback ViewModel 생성
InventoryHistoryViewModel _createFallbackViewModel(EquipmentHistoryDto history) {
return InventoryHistoryViewModel(
historyId: history.id ?? 0,
equipmentId: history.equipmentsId,
equipmentName: history.equipmentSerial ?? 'Unknown Equipment',
serialNumber: history.equipmentSerial ?? 'N/A',
location: _determineLocation(history),
changedDate: history.transactedAt,
remark: history.remark,
transactionType: history.transactionType,
quantity: history.quantity,
originalHistory: history,
);
}
/// 로컬 검색 필터링 적용
List<InventoryHistoryViewModel> _applyLocalSearch(
List<InventoryHistoryViewModel> items,
String searchKeyword
) {
final keyword = searchKeyword.toLowerCase();
return items.where((item) {
return [
item.equipmentName,
item.serialNumber,
item.location,
item.remark ?? '',
item.transactionTypeDisplay,
].any((field) => field.toLowerCase().contains(keyword));
}).toList();
}
}