import 'dart:async'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:superport/data/models/equipment/equipment_dto.dart'; import 'package:superport/data/models/company/company_dto.dart'; import 'package:superport/data/models/stock_status_dto.dart'; import 'package:superport/data/repositories/equipment_history_repository.dart'; import 'package:superport/domain/repositories/company_repository.dart'; import 'package:superport/data/repositories/company_repository_impl.dart'; import 'package:superport/data/datasources/remote/company_remote_datasource.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/services/equipment_warehouse_cache_service.dart'; class EquipmentOutboundController extends ChangeNotifier { final List selectedEquipments; late final CompanyRepository _companyRepository; late final EquipmentHistoryRepository _equipmentHistoryRepository; late final EquipmentWarehouseCacheService _warehouseCacheService; // Form controllers final TextEditingController remarkController = TextEditingController(); // State variables bool _isLoading = false; bool _isLoadingCompanies = false; bool _isLoadingWarehouseInfo = false; String? _errorMessage; String? _companyError; String? _warehouseError; // Form data DateTime _transactionDate = DateTime.now(); List _companies = []; CompanyDto? _selectedCompany; final Map _warrantyDates = {}; // 각 장비의 워런티 날짜 관리 // Getters bool get isLoading => _isLoading; bool get isLoadingCompanies => _isLoadingCompanies; bool get isLoadingWarehouseInfo => _isLoadingWarehouseInfo; String? get errorMessage => _errorMessage; String? get companyError => _companyError; String? get warehouseError => _warehouseError; DateTime get transactionDate => _transactionDate; List get companies => _companies; CompanyDto? get selectedCompany => _selectedCompany; bool get canSubmit => !_isLoading && _selectedCompany != null && selectedEquipments.isNotEmpty; EquipmentOutboundController({ required this.selectedEquipments, }) { // Initialize repositories directly with proper dependencies final apiClient = ApiClient(); final companyRemoteDataSource = CompanyRemoteDataSourceImpl(apiClient); _companyRepository = CompanyRepositoryImpl(remoteDataSource: companyRemoteDataSource); // Initialize EquipmentHistoryRepository with ApiClient's Dio instance // ApiClient has proper auth headers and base URL configuration final dio = apiClient.dio; // Use the authenticated Dio instance from ApiClient _equipmentHistoryRepository = EquipmentHistoryRepositoryImpl(dio); // Initialize warehouse cache service _warehouseCacheService = EquipmentWarehouseCacheService(); // 각 장비의 현재 워런티 날짜로 초기화 for (final equipment in selectedEquipments) { final id = equipment.id; final warrantyDate = equipment.warrantyEndedAt; if (id != null && warrantyDate != null) { _warrantyDates[id] = warrantyDate; } } } set transactionDate(DateTime value) { _transactionDate = value; notifyListeners(); } set selectedCompany(CompanyDto? value) { _selectedCompany = value; notifyListeners(); } Future initialize() async { // 병렬로 회사 정보와 창고 캐시 로드 await Future.wait([ loadCompanies(), _loadWarehouseCache(), ]); } Future loadCompanies() async { print('[EquipmentOutboundController] loadCompanies called'); _isLoadingCompanies = true; _companyError = null; notifyListeners(); try { print('[EquipmentOutboundController] Calling _companyRepository.getCompanies'); final result = await _companyRepository.getCompanies( limit: 1000, // 모든 회사를 가져오기 위해 큰 값 설정 ); result.fold( (failure) { print('[EquipmentOutboundController] Company loading failed: ${failure.message}'); _companyError = failure.message; }, (data) { print('[EquipmentOutboundController] Companies loaded successfully: ${data.items.length} companies'); // Convert Company to CompanyDto - only use required fields _companies = data.items .map((company) => CompanyDto( id: company.id, name: company.name, contactName: '', // Default value for required field contactPhone: '', // Default value for required field contactEmail: '', // Default value for required field address: company.address.toString(), isCustomer: company.isCustomer, )) .where((c) => c.isCustomer == true) .toList(); print('[EquipmentOutboundController] Filtered customer companies: ${_companies.length}'); }, ); } catch (e, stackTrace) { print('[EquipmentOutboundController] Exception in loadCompanies: $e'); print('[EquipmentOutboundController] Stack trace: $stackTrace'); _companyError = '회사 목록을 불러오는 중 오류가 발생했습니다: $e'; } finally { _isLoadingCompanies = false; notifyListeners(); print('[EquipmentOutboundController] loadCompanies completed'); } } /// 창고 캐시 로딩 Future _loadWarehouseCache() async { if (_warehouseCacheService.needsRefresh()) { _isLoadingWarehouseInfo = true; _warehouseError = null; notifyListeners(); try { final success = await _warehouseCacheService.loadCache(); if (!success) { _warehouseError = _warehouseCacheService.lastError ?? '창고 정보 로딩 실패'; } } catch (e) { _warehouseError = '창고 정보 로딩 중 오류: $e'; } finally { _isLoadingWarehouseInfo = false; notifyListeners(); } } } /// 장비의 현재 창고 정보 조회 (Stock Status 기반) /// /// [equipment]: 조회할 장비 /// /// Returns: 창고명 (Stock Status 우선, Fallback으로 Equipment DTO 사용) String getEquipmentCurrentWarehouse(EquipmentDto equipment) { // 디버깅: 실제 Equipment DTO 데이터 출력 print('[EquipmentOutboundController] Equipment ${equipment.id} 창고 정보:'); print(' - warehousesId: ${equipment.warehousesId}'); print(' - warehousesName: ${equipment.warehousesName}'); print(' - serialNumber: ${equipment.serialNumber}'); if (_warehouseError != null) { print('[EquipmentOutboundController] Stock Status API 실패, Equipment DTO 사용'); final fallbackName = equipment.warehousesName ?? '창고 미지정'; print(' - Fallback 결과: $fallbackName'); return fallbackName; } // Primary: Stock Status API 기반 정보 사용 final stockInfo = _warehouseCacheService.getEquipmentStock(equipment.id); print('[EquipmentOutboundController] Stock Status API 결과:'); print(' - stockInfo 존재: ${stockInfo != null}'); if (stockInfo != null) { print(' - stockInfo.warehouseName: ${stockInfo.warehouseName}'); print(' - stockInfo.warehouseId: ${stockInfo.warehouseId}'); } final finalResult = stockInfo?.warehouseName ?? equipment.warehousesName ?? '입출고 이력 없음'; print(' - 최종 결과: $finalResult'); return finalResult; } /// 장비의 현재 창고 ID 조회 int? getEquipmentCurrentWarehouseId(EquipmentDto equipment) { // Primary: Stock Status API 기반 정보 사용 final stockInfo = _warehouseCacheService.getEquipmentStock(equipment.id); return stockInfo?.warehouseId ?? equipment.warehousesId; } /// 장비의 재고 현황 정보 조회 StockStatusDto? getEquipmentStockStatus(EquipmentDto equipment) { return _warehouseCacheService.getEquipmentStock(equipment.id); } /// 출고 후 창고 캐시 갱신 Future _refreshWarehouseCache() async { print('[EquipmentOutboundController] 출고 완료 후 창고 캐시 갱신 시작...'); try { await _warehouseCacheService.refreshCache(); print('[EquipmentOutboundController] 창고 캐시 갱신 완료'); } catch (e) { print('[EquipmentOutboundController] 창고 캐시 갱신 실패: $e'); // 갱신 실패해도 출고 프로세스는 성공으로 간주 } } Future processOutbound() async { print('[EquipmentOutboundController] processOutbound called'); print('[EquipmentOutboundController] canSubmit: $canSubmit'); print('[EquipmentOutboundController] selectedEquipments count: ${selectedEquipments.length}'); print('[EquipmentOutboundController] selectedCompany: ${_selectedCompany?.name} (ID: ${_selectedCompany?.id})'); print('[EquipmentOutboundController] API Base URL: ${_equipmentHistoryRepository.toString()}'); if (!canSubmit) { print('[EquipmentOutboundController] Cannot submit - validation failed'); return false; } _isLoading = true; _errorMessage = null; notifyListeners(); try { print('[EquipmentOutboundController] Starting outbound process for ${selectedEquipments.length} equipments'); // Process each selected equipment for (int i = 0; i < selectedEquipments.length; i++) { final equipment = selectedEquipments[i]; // 개선된 창고 정보 조회 (Stock Status API 우선) final currentWarehouseName = getEquipmentCurrentWarehouse(equipment); final currentWarehouseId = getEquipmentCurrentWarehouseId(equipment); print('[EquipmentOutboundController] Processing equipment ${i+1}/${selectedEquipments.length}'); print('[EquipmentOutboundController] Equipment ID: ${equipment.id}'); print('[EquipmentOutboundController] Equipment Serial: ${equipment.serialNumber}'); print('[EquipmentOutboundController] Current Warehouse (Stock Status): $currentWarehouseName (ID: $currentWarehouseId)'); print('[EquipmentOutboundController] Original Warehouse (DTO): ${equipment.warehousesName} (ID: ${equipment.warehousesId})'); await _equipmentHistoryRepository.createStockOut( equipmentsId: equipment.id, warehousesId: currentWarehouseId ?? equipment.warehousesId, // 개선된 창고 정보 사용 companyIds: _selectedCompany?.id != null ? [_selectedCompany!.id!] : null, quantity: 1, transactedAt: _transactionDate, remark: remarkController.text.isNotEmpty ? remarkController.text : null, ); print('[EquipmentOutboundController] Successfully processed equipment ${equipment.id}'); } print('[EquipmentOutboundController] All equipments processed successfully'); // 출고 완료 후 창고 캐시 갱신 (백그라운드에서 실행) unawaited(_refreshWarehouseCache()); return true; } catch (e, stackTrace) { print('[EquipmentOutboundController] ERROR during outbound process: $e'); print('[EquipmentOutboundController] Stack trace: $stackTrace'); _errorMessage = '출고 처리 중 오류가 발생했습니다: $e'; notifyListeners(); return false; } finally { _isLoading = false; notifyListeners(); print('[EquipmentOutboundController] processOutbound completed'); } } String formatDate(DateTime date) { return DateFormat('yyyy-MM-dd').format(date); } String formatPrice(int? price) { if (price == null) return '-'; final formatter = NumberFormat('#,###'); return '₩${formatter.format(price)}'; } DateTime? getWarrantyDate(int equipmentId) { return _warrantyDates[equipmentId]; } void updateWarrantyDate(int equipmentId, DateTime date) { _warrantyDates[equipmentId] = date; notifyListeners(); } @override void dispose() { remarkController.dispose(); super.dispose(); } }