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>
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:superport/data/models/inventory_history_view_model.dart';
|
||||
import 'package:superport/services/inventory_history_service.dart';
|
||||
import 'package:superport/core/constants/app_constants.dart';
|
||||
|
||||
/// 재고 이력 관리 화면 전용 컨트롤러
|
||||
/// InventoryHistoryService를 통해 여러 API를 조합한 데이터 관리
|
||||
class InventoryHistoryController extends ChangeNotifier {
|
||||
final InventoryHistoryService _service;
|
||||
|
||||
InventoryHistoryController({
|
||||
InventoryHistoryService? service,
|
||||
}) : _service = service ?? InventoryHistoryService();
|
||||
|
||||
// 상태 변수
|
||||
List<InventoryHistoryViewModel> _historyItems = [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
// 페이지네이션
|
||||
int _currentPage = 1;
|
||||
int _pageSize = AppConstants.historyPageSize;
|
||||
int _totalCount = 0;
|
||||
int _totalPages = 0;
|
||||
|
||||
// 검색 및 필터
|
||||
String _searchKeyword = '';
|
||||
String? _selectedTransactionType;
|
||||
int? _selectedEquipmentId;
|
||||
int? _selectedWarehouseId;
|
||||
int? _selectedCompanyId;
|
||||
DateTime? _dateFrom;
|
||||
DateTime? _dateTo;
|
||||
|
||||
// Getters
|
||||
List<InventoryHistoryViewModel> get historyItems => _historyItems;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
int get currentPage => _currentPage;
|
||||
int get totalPages => _totalPages;
|
||||
int get totalCount => _totalCount;
|
||||
int get pageSize => _pageSize;
|
||||
String get searchKeyword => _searchKeyword;
|
||||
String? get selectedTransactionType => _selectedTransactionType;
|
||||
|
||||
// 통계 정보
|
||||
int get totalTransactions => _historyItems.length;
|
||||
int get inStockCount => _historyItems.where((item) => item.transactionType == 'I').length;
|
||||
int get outStockCount => _historyItems.where((item) => item.transactionType == 'O').length;
|
||||
int get rentCount => _historyItems.where((item) => item.transactionType == 'R').length;
|
||||
int get disposeCount => _historyItems.where((item) => item.transactionType == 'D').length;
|
||||
|
||||
/// 재고 이력 목록 로드
|
||||
Future<void> loadHistories({bool refresh = false}) async {
|
||||
if (refresh) {
|
||||
_currentPage = 1;
|
||||
_historyItems.clear();
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
print('[InventoryHistoryController] Loading histories - Page: $_currentPage, Search: "$_searchKeyword", Type: $_selectedTransactionType');
|
||||
|
||||
final response = await _service.loadInventoryHistories(
|
||||
page: _currentPage,
|
||||
pageSize: _pageSize,
|
||||
searchKeyword: _searchKeyword.isEmpty ? null : _searchKeyword,
|
||||
transactionType: _selectedTransactionType,
|
||||
equipmentId: _selectedEquipmentId,
|
||||
warehouseId: _selectedWarehouseId,
|
||||
companyId: _selectedCompanyId,
|
||||
dateFrom: _dateFrom,
|
||||
dateTo: _dateTo,
|
||||
);
|
||||
|
||||
if (refresh) {
|
||||
_historyItems = response.items;
|
||||
} else {
|
||||
_historyItems.addAll(response.items);
|
||||
}
|
||||
|
||||
_totalCount = response.totalCount;
|
||||
_totalPages = response.totalPages;
|
||||
|
||||
print('[InventoryHistoryController] Loaded ${response.items.length} items, Total: $_totalCount');
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
print('[InventoryHistoryController] Error loading histories: $e');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 장비의 전체 이력 로드 (상세보기용)
|
||||
Future<List<InventoryHistoryViewModel>> loadEquipmentHistory(int equipmentId) async {
|
||||
try {
|
||||
print('[InventoryHistoryController] Loading equipment history for ID: $equipmentId');
|
||||
|
||||
final histories = await _service.loadEquipmentHistory(equipmentId);
|
||||
|
||||
print('[InventoryHistoryController] Loaded ${histories.length} equipment histories');
|
||||
return histories;
|
||||
} catch (e) {
|
||||
print('[InventoryHistoryController] Error loading equipment history: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// 검색 키워드 설정
|
||||
void setSearchKeyword(String keyword) {
|
||||
if (_searchKeyword != keyword) {
|
||||
_searchKeyword = keyword;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 거래 유형 필터 설정
|
||||
void setTransactionTypeFilter(String? transactionType) {
|
||||
if (_selectedTransactionType != transactionType) {
|
||||
_selectedTransactionType = transactionType;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 장비 필터 설정
|
||||
void setEquipmentFilter(int? equipmentId) {
|
||||
if (_selectedEquipmentId != equipmentId) {
|
||||
_selectedEquipmentId = equipmentId;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 창고 필터 설정
|
||||
void setWarehouseFilter(int? warehouseId) {
|
||||
if (_selectedWarehouseId != warehouseId) {
|
||||
_selectedWarehouseId = warehouseId;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 고객사 필터 설정
|
||||
void setCompanyFilter(int? companyId) {
|
||||
if (_selectedCompanyId != companyId) {
|
||||
_selectedCompanyId = companyId;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 날짜 범위 필터 설정
|
||||
void setDateRangeFilter(DateTime? dateFrom, DateTime? dateTo) {
|
||||
if (_dateFrom != dateFrom || _dateTo != dateTo) {
|
||||
_dateFrom = dateFrom;
|
||||
_dateTo = dateTo;
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 복합 필터 설정 (한 번에 여러 필터 적용)
|
||||
void setFilters({
|
||||
String? searchKeyword,
|
||||
String? transactionType,
|
||||
int? equipmentId,
|
||||
int? warehouseId,
|
||||
int? companyId,
|
||||
DateTime? dateFrom,
|
||||
DateTime? dateTo,
|
||||
}) {
|
||||
bool hasChanges = false;
|
||||
|
||||
if (searchKeyword != null && _searchKeyword != searchKeyword) {
|
||||
_searchKeyword = searchKeyword;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (_selectedTransactionType != transactionType) {
|
||||
_selectedTransactionType = transactionType;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (_selectedEquipmentId != equipmentId) {
|
||||
_selectedEquipmentId = equipmentId;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (_selectedWarehouseId != warehouseId) {
|
||||
_selectedWarehouseId = warehouseId;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (_selectedCompanyId != companyId) {
|
||||
_selectedCompanyId = companyId;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (_dateFrom != dateFrom || _dateTo != dateTo) {
|
||||
_dateFrom = dateFrom;
|
||||
_dateTo = dateTo;
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
if (hasChanges) {
|
||||
_currentPage = 1;
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 모든 필터 초기화
|
||||
void clearFilters() {
|
||||
_searchKeyword = '';
|
||||
_selectedTransactionType = null;
|
||||
_selectedEquipmentId = null;
|
||||
_selectedWarehouseId = null;
|
||||
_selectedCompanyId = null;
|
||||
_dateFrom = null;
|
||||
_dateTo = null;
|
||||
_currentPage = 1;
|
||||
|
||||
loadHistories(refresh: true);
|
||||
}
|
||||
|
||||
/// 다음 페이지 로드
|
||||
Future<void> loadNextPage() async {
|
||||
if (_currentPage < _totalPages && !_isLoading) {
|
||||
_currentPage++;
|
||||
await loadHistories();
|
||||
}
|
||||
}
|
||||
|
||||
/// 이전 페이지 로드
|
||||
Future<void> loadPreviousPage() async {
|
||||
if (_currentPage > 1 && !_isLoading) {
|
||||
_currentPage--;
|
||||
await loadHistories();
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 페이지로 이동
|
||||
Future<void> goToPage(int page) async {
|
||||
if (page > 0 && page <= _totalPages && page != _currentPage && !_isLoading) {
|
||||
_currentPage = page;
|
||||
await loadHistories();
|
||||
}
|
||||
}
|
||||
|
||||
/// 데이터 새로고침
|
||||
Future<void> refresh() async {
|
||||
await loadHistories(refresh: true);
|
||||
}
|
||||
|
||||
/// 에러 초기화
|
||||
void clearError() {
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 통계 정보 맵 형태로 반환
|
||||
Map<String, dynamic> getStatistics() {
|
||||
return {
|
||||
'total': totalCount,
|
||||
'current_page_count': totalTransactions,
|
||||
'in_stock': inStockCount,
|
||||
'out_stock': outStockCount,
|
||||
'rent': rentCount,
|
||||
'dispose': disposeCount,
|
||||
};
|
||||
}
|
||||
|
||||
/// 검색 상태 확인
|
||||
bool get hasActiveFilters {
|
||||
return _searchKeyword.isNotEmpty ||
|
||||
_selectedTransactionType != null ||
|
||||
_selectedEquipmentId != null ||
|
||||
_selectedWarehouseId != null ||
|
||||
_selectedCompanyId != null ||
|
||||
_dateFrom != null ||
|
||||
_dateTo != null;
|
||||
}
|
||||
|
||||
/// 필터 상태 텍스트
|
||||
String get filterStatusText {
|
||||
List<String> filters = [];
|
||||
|
||||
if (_searchKeyword.isNotEmpty) {
|
||||
filters.add('검색: "$_searchKeyword"');
|
||||
}
|
||||
|
||||
if (_selectedTransactionType != null) {
|
||||
final typeMap = {
|
||||
'I': '입고',
|
||||
'O': '출고',
|
||||
'R': '대여',
|
||||
'D': '폐기',
|
||||
};
|
||||
filters.add('유형: ${typeMap[_selectedTransactionType]}');
|
||||
}
|
||||
|
||||
if (_dateFrom != null || _dateTo != null) {
|
||||
String dateFilter = '기간: ';
|
||||
if (_dateFrom != null) {
|
||||
dateFilter += '${_dateFrom!.toString().substring(0, 10)}';
|
||||
}
|
||||
if (_dateTo != null) {
|
||||
dateFilter += ' ~ ${_dateTo!.toString().substring(0, 10)}';
|
||||
}
|
||||
filters.add(dateFilter);
|
||||
}
|
||||
|
||||
return filters.join(', ');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_historyItems.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user