Files
superport/lib/screens/equipment/controllers/equipment_form_controller.dart

414 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:injectable/injectable.dart';
import '../../../data/models/equipment/equipment_dto.dart';
import '../../../data/models/company/company_dto.dart';
import '../../../data/models/model_dto.dart';
import '../../../domain/usecases/equipment/create_equipment_usecase.dart';
import '../../../domain/usecases/equipment/update_equipment_usecase.dart';
import '../../../domain/usecases/equipment/get_equipment_detail_usecase.dart';
import '../../../domain/usecases/company/get_companies_usecase.dart';
import '../../../domain/usecases/model_usecase.dart';
import '../../../core/errors/failures.dart';
/// 장비 폼 컨트롤러 (생성/수정)
/// Phase 8 - Clean Architecture 패턴, 복합 FK 지원
@injectable
class EquipmentFormController extends ChangeNotifier {
final CreateEquipmentUseCase _createEquipmentUseCase;
final UpdateEquipmentUseCase _updateEquipmentUseCase;
final GetEquipmentDetailUseCase _getEquipmentDetailUseCase;
final GetCompaniesUseCase _getCompaniesUseCase;
final ModelUseCase _modelUseCase;
EquipmentFormController(
this._createEquipmentUseCase,
this._updateEquipmentUseCase,
this._getEquipmentDetailUseCase,
this._getCompaniesUseCase,
this._modelUseCase,
);
// 상태 관리
bool _isLoading = false;
bool _isLoadingCompanies = false;
bool _isLoadingModels = false;
bool _isSaving = false;
String? _error;
// 폼 데이터
EquipmentDto? _currentEquipment;
int? _equipmentId; // 수정 모드일 때 사용
// 드롭다운 데이터
List<CompanyDto> _companies = [];
List<ModelDto> _models = [];
List<ModelDto> _filteredModels = [];
// 선택된 값
int? _selectedCompanyId;
int? _selectedModelId;
// 폼 컨트롤러들
final TextEditingController serialNumberController = TextEditingController();
final TextEditingController barcodeController = TextEditingController();
final TextEditingController purchasePriceController = TextEditingController();
final TextEditingController warrantyNumberController = TextEditingController();
final TextEditingController remarkController = TextEditingController();
// 날짜 필드들
DateTime? _purchasedAt;
DateTime _warrantyStartedAt = DateTime.now();
DateTime _warrantyEndedAt = DateTime.now().add(const Duration(days: 365));
// Getters
bool get isLoading => _isLoading;
bool get isLoadingCompanies => _isLoadingCompanies;
bool get isLoadingModels => _isLoadingModels;
bool get isSaving => _isSaving;
String? get error => _error;
EquipmentDto? get currentEquipment => _currentEquipment;
bool get isEditMode => _equipmentId != null;
List<CompanyDto> get companies => _companies;
List<ModelDto> get filteredModels => _filteredModels;
int? get selectedCompanyId => _selectedCompanyId;
int? get selectedModelId => _selectedModelId;
DateTime? get purchasedAt => _purchasedAt;
DateTime get warrantyStartedAt => _warrantyStartedAt;
DateTime get warrantyEndedAt => _warrantyEndedAt;
/// 초기화 (생성 모드)
Future<void> initializeForCreate() async {
_equipmentId = null;
_currentEquipment = null;
_clearForm();
await _loadInitialData();
}
/// 초기화 (수정 모드)
Future<void> initializeForEdit(int equipmentId) async {
_equipmentId = equipmentId;
_isLoading = true;
_error = null;
notifyListeners();
try {
await _loadInitialData();
await _loadEquipmentDetail(equipmentId);
} catch (e) {
_error = '장비 정보를 불러오는데 실패했습니다: $e';
} finally {
_isLoading = false;
notifyListeners();
}
}
/// 초기 데이터 로드 (회사, 모델)
Future<void> _loadInitialData() async {
await Future.wait([
_loadCompanies(),
_loadModels(),
]);
}
/// 회사 목록 로드
Future<void> _loadCompanies() async {
_isLoadingCompanies = true;
notifyListeners();
try {
final params = GetCompaniesParams(page: 1, perPage: 1000); // 모든 회사 가져오기
final result = await _getCompaniesUseCase(params);
result.fold(
(failure) {
_error = _getErrorMessage(failure);
},
(paginatedResponse) {
_companies = paginatedResponse.items
.cast<CompanyDto>() // 타입 캐스팅 추가
.where((company) => company.isActive)
.toList()
..sort((a, b) => a.name.compareTo(b.name));
},
);
} catch (e) {
_error = '회사 목록을 불러오는데 실패했습니다: $e';
} finally {
_isLoadingCompanies = false;
notifyListeners();
}
}
/// 모델 목록 로드
Future<void> _loadModels() async {
_isLoadingModels = true;
notifyListeners();
try {
_models = await _modelUseCase.getModels();
_filteredModels = _models;
} catch (e) {
_error = '모델 목록을 불러오는데 실패했습니다: $e';
} finally {
_isLoadingModels = false;
notifyListeners();
}
}
/// 장비 상세 정보 로드 (수정 모드)
Future<void> _loadEquipmentDetail(int equipmentId) async {
final result = await _getEquipmentDetailUseCase(equipmentId);
result.fold(
(failure) {
_error = _getErrorMessage(failure);
},
(equipment) {
_currentEquipment = equipment;
_populateForm(equipment);
},
);
}
/// 폼 필드에 데이터 채우기 (수정 모드)
void _populateForm(EquipmentDto equipment) {
_selectedCompanyId = equipment.companiesId;
_selectedModelId = equipment.modelsId;
serialNumberController.text = equipment.serialNumber;
barcodeController.text = equipment.barcode ?? '';
purchasePriceController.text = equipment.purchasePrice.toString();
warrantyNumberController.text = equipment.warrantyNumber;
remarkController.text = equipment.remark ?? '';
_purchasedAt = equipment.purchasedAt;
_warrantyStartedAt = equipment.warrantyStartedAt;
_warrantyEndedAt = equipment.warrantyEndedAt;
// 선택된 회사에 따라 모델 필터링
_filterModelsByCompany(_selectedCompanyId);
notifyListeners();
}
/// 회사 선택
void selectCompany(int? companyId) {
_selectedCompanyId = companyId;
_selectedModelId = null; // 모델 선택 초기화
_filterModelsByCompany(companyId);
notifyListeners();
}
/// 모델 선택
void selectModel(int? modelId) {
_selectedModelId = modelId;
notifyListeners();
}
/// 회사별 모델 필터링
void _filterModelsByCompany(int? companyId) {
if (companyId == null) {
_filteredModels = _models;
} else {
// 실제로는 vendor로 필터링해야 하지만,
// 현재 구조에서는 모든 모델을 보여주고 사용자가 선택하도록 함
_filteredModels = _models;
}
notifyListeners();
}
/// 구매일 선택
void setPurchasedAt(DateTime? date) {
_purchasedAt = date;
notifyListeners();
}
/// 워런티 시작일 선택
void setWarrantyStartedAt(DateTime date) {
_warrantyStartedAt = date;
// 시작일이 종료일보다 늦으면 종료일을 1년 후로 설정
if (_warrantyStartedAt.isAfter(_warrantyEndedAt)) {
_warrantyEndedAt = _warrantyStartedAt.add(const Duration(days: 365));
}
notifyListeners();
}
/// 워런티 종료일 선택
void setWarrantyEndedAt(DateTime date) {
_warrantyEndedAt = date;
notifyListeners();
}
/// 폼 유효성 검증
String? validateForm() {
if (_selectedCompanyId == null) {
return '회사를 선택해주세요.';
}
if (_selectedModelId == null) {
return '모델을 선택해주세요.';
}
if (serialNumberController.text.trim().isEmpty) {
return '시리얼 번호를 입력해주세요.';
}
if (warrantyNumberController.text.trim().isEmpty) {
return '워런티 번호를 입력해주세요.';
}
if (_warrantyStartedAt.isAfter(_warrantyEndedAt)) {
return '워런티 시작일이 종료일보다 늦을 수 없습니다.';
}
return null;
}
/// 장비 저장 (생성 또는 수정)
Future<bool> saveEquipment() async {
final validationError = validateForm();
if (validationError != null) {
_error = validationError;
notifyListeners();
return false;
}
_isSaving = true;
_error = null;
notifyListeners();
try {
if (isEditMode) {
return await _updateEquipment();
} else {
return await _createEquipment();
}
} catch (e) {
_error = '저장 중 오류가 발생했습니다: $e';
return false;
} finally {
_isSaving = false;
notifyListeners();
}
}
/// 장비 생성
Future<bool> _createEquipment() async {
final request = EquipmentRequestDto(
companiesId: _selectedCompanyId!,
modelsId: _selectedModelId!,
serialNumber: serialNumberController.text.trim(),
barcode: barcodeController.text.trim().isNotEmpty
? barcodeController.text.trim()
: null,
purchasedAt: _purchasedAt,
purchasePrice: int.tryParse(purchasePriceController.text) ?? 0,
warrantyNumber: warrantyNumberController.text.trim(),
warrantyStartedAt: _warrantyStartedAt,
warrantyEndedAt: _warrantyEndedAt,
remark: remarkController.text.trim().isNotEmpty
? remarkController.text.trim()
: null,
);
final result = await _createEquipmentUseCase(request);
return result.fold(
(failure) {
_error = _getErrorMessage(failure);
return false;
},
(equipment) {
_currentEquipment = equipment;
return true;
},
);
}
/// 장비 수정
Future<bool> _updateEquipment() async {
final request = EquipmentUpdateRequestDto(
companiesId: _selectedCompanyId!,
modelsId: _selectedModelId!,
serialNumber: serialNumberController.text.trim(),
barcode: barcodeController.text.trim().isNotEmpty
? barcodeController.text.trim()
: null,
purchasedAt: _purchasedAt,
purchasePrice: int.tryParse(purchasePriceController.text) ?? 0,
warrantyNumber: warrantyNumberController.text.trim(),
warrantyStartedAt: _warrantyStartedAt,
warrantyEndedAt: _warrantyEndedAt,
remark: remarkController.text.trim().isNotEmpty
? remarkController.text.trim()
: null,
);
final result = await _updateEquipmentUseCase(UpdateEquipmentParams(
id: _equipmentId!,
request: request,
));
return result.fold(
(failure) {
_error = _getErrorMessage(failure);
return false;
},
(equipment) {
_currentEquipment = equipment;
return true;
},
);
}
/// 폼 초기화
void _clearForm() {
_selectedCompanyId = null;
_selectedModelId = null;
serialNumberController.clear();
barcodeController.clear();
purchasePriceController.text = '0';
warrantyNumberController.clear();
remarkController.clear();
_purchasedAt = null;
_warrantyStartedAt = DateTime.now();
_warrantyEndedAt = DateTime.now().add(const Duration(days: 365));
_error = null;
}
/// 에러 메시지 변환
String _getErrorMessage(Failure failure) {
switch (failure.runtimeType) {
case ServerFailure:
return (failure as ServerFailure).message;
case NetworkFailure:
return '네트워크 연결을 확인해주세요.';
case ValidationFailure:
return (failure as ValidationFailure).message;
default:
return '알 수 없는 오류가 발생했습니다.';
}
}
/// 에러 초기화
void clearError() {
_error = null;
notifyListeners();
}
@override
void dispose() {
serialNumberController.dispose();
barcodeController.dispose();
purchasePriceController.dispose();
warrantyNumberController.dispose();
remarkController.dispose();
super.dispose();
}
}