/// 지점 관리 컨트롤러 (입력/수정 통합) /// /// 지점 생성 및 수정 화면의 비즈니스 로직을 담당하는 통합 컨트롤러 클래스 /// 주요 기능: /// - 본사 목록 조회 및 관리 /// - 지점 정보 입력/수정 관리 /// - 지점 생성/수정 요청 /// - 폼 유효성 검증 /// - 수정 모드에서 기존 데이터 로드 library; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:superport/models/address_model.dart'; import 'package:superport/models/company_model.dart'; import 'package:superport/models/company_item_model.dart'; import 'package:superport/services/company_service.dart'; import 'package:superport/core/errors/failures.dart'; import 'package:superport/utils/phone_utils.dart'; import 'dart:async'; /// 지점 컨트롤러 - 본사 선택 및 지점 정보 입력/수정 관리 class BranchController extends ChangeNotifier { final CompanyService _companyService = GetIt.instance(); // 수정 모드 관련 final int? branchId; // 수정할 지점 ID (null이면 생성 모드) final String? parentCompanyName; // 본사명 (수정 모드에서 표시용) bool get isEditMode => branchId != null; // 원본 데이터 (변경 감지용) Company? _originalBranch; Company? get originalBranch => _originalBranch; // 본사 목록 관련 List _headquartersList = []; List get headquartersList => _headquartersList; int? _selectedHeadquarterId; int? get selectedHeadquarterId => _selectedHeadquarterId; String? _selectedHeadquarterName; String? get selectedHeadquarterName => _selectedHeadquarterName; // 로딩 상태 bool _isLoading = false; bool get isLoading => _isLoading; bool _isLoadingHeadquarters = false; bool get isLoadingHeadquarters => _isLoadingHeadquarters; bool _isSaving = false; bool get isSaving => _isSaving; // 에러 메시지 String? _errorMessage; String? get errorMessage => _errorMessage; // 폼 컨트롤러들 final GlobalKey formKey = GlobalKey(); final TextEditingController nameController = TextEditingController(); final TextEditingController contactNameController = TextEditingController(); final TextEditingController contactPositionController = TextEditingController(); final TextEditingController contactPhoneController = TextEditingController(); final TextEditingController contactEmailController = TextEditingController(); final TextEditingController remarkController = TextEditingController(); // 주소 관련 Address branchAddress = const Address(); final TextEditingController addressController = TextEditingController(); // 전화번호 관련 String _selectedPhonePrefix = '010'; String get selectedPhonePrefix => _selectedPhonePrefix; final TextEditingController phoneNumberController = TextEditingController(); final List _phonePrefixes = PhoneUtils.getCommonPhonePrefixes(); List get phonePrefixes => _phonePrefixes; BranchController({ this.branchId, // 수정 모드: 지점 ID, 생성 모드: null this.parentCompanyName, // 수정 모드: 본사명 (표시용) }) { if (!isEditMode) { // 생성 모드: 본사 목록 로드 _loadHeadquarters(); } else { // 수정 모드: 지점 데이터 로드 및 본사 목록도 로드 _loadHeadquarters(); _loadBranchData(); } } /// 지점 데이터 로드 (수정 모드에서만 사용) Future _loadBranchData() async { if (!isEditMode || branchId == null) return; _isLoading = true; _errorMessage = null; notifyListeners(); try { final company = await _companyService.getCompanyDetail(branchId!); _originalBranch = company; _populateFormWithBranchData(company); } catch (e) { _errorMessage = '지점 정보를 불러오는 중 오류가 발생했습니다: $e'; } finally { _isLoading = false; notifyListeners(); } } /// 본사 목록 로드 (전체 본사 목록 로드 - 55개 전체) Future _loadHeadquarters() async { _isLoadingHeadquarters = true; _errorMessage = null; notifyListeners(); try { final result = await _companyService.getAllHeadquarters(); result.fold( (failure) { _errorMessage = _getFailureMessage(failure); }, (headquarters) { _headquartersList = headquarters; }, ); } catch (e) { _errorMessage = '본사 목록을 불러오는 중 오류가 발생했습니다: $e'; } finally { _isLoadingHeadquarters = false; notifyListeners(); } } /// 폼에 지점 데이터 설정 (수정 모드에서만 사용) void _populateFormWithBranchData(Company company) { nameController.text = company.name; addressController.text = company.address.detailAddress ?? ''; contactNameController.text = company.contactName ?? ''; contactPositionController.text = company.contactPosition ?? ''; contactEmailController.text = company.contactEmail ?? ''; remarkController.text = company.remark ?? ''; // 전화번호 파싱 (간단한 로직으로 구현) final phoneNumber = company.contactPhone ?? ''; if (phoneNumber.isNotEmpty) { final parts = _parsePhoneNumber(phoneNumber); _selectedPhonePrefix = parts['prefix'] ?? '010'; phoneNumberController.text = parts['number'] ?? ''; } // 본사 ID 설정 if (company.parentCompanyId != null) { _selectedHeadquarterId = company.parentCompanyId; // 본사명 찾기 final headquarters = _headquartersList .where((h) => h.id == company.parentCompanyId) .firstOrNull; _selectedHeadquarterName = headquarters?.name ?? parentCompanyName; } notifyListeners(); } /// 본사 선택 void selectHeadquarters(int headquarterId, String headquarterName) { _selectedHeadquarterId = headquarterId; _selectedHeadquarterName = headquarterName; notifyListeners(); } /// 전화번호 접두사 선택 void selectPhonePrefix(String prefix) { _selectedPhonePrefix = prefix; notifyListeners(); } /// 주소 업데이트 void updateBranchAddress(Address address) { branchAddress = address; notifyListeners(); } /// 지점 저장 (생성 또는 수정) Future saveBranch() async { if (!formKey.currentState!.validate()) { return false; } if (_selectedHeadquarterId == null) { _errorMessage = '본사를 선택해주세요'; notifyListeners(); return false; } _isSaving = true; _errorMessage = null; notifyListeners(); try { // 전화번호 합치기 final fullPhoneNumber = PhoneUtils.getFullPhoneNumber( _selectedPhonePrefix, phoneNumberController.text ); contactPhoneController.text = fullPhoneNumber; // 주소 업데이트 updateBranchAddress(Address.fromFullAddress(addressController.text)); if (isEditMode && _originalBranch != null) { // 수정 모드: 기존 지점 정보 업데이트 final updatedBranch = _originalBranch!.copyWith( name: nameController.text.trim(), address: branchAddress, contactName: contactNameController.text.trim(), contactPosition: contactPositionController.text.trim(), contactPhone: fullPhoneNumber, contactEmail: contactEmailController.text.trim(), remark: remarkController.text.trim(), parentCompanyId: _selectedHeadquarterId, updatedAt: DateTime.now(), ); final updatedCompany = await _companyService.updateCompany(branchId!, updatedBranch); _originalBranch = updatedCompany; } else { // 생성 모드: 새 지점 생성 final branchCompany = Company( id: 0, // 새 지점이므로 0 name: nameController.text.trim(), address: branchAddress, contactName: contactNameController.text.trim(), contactPosition: contactPositionController.text.trim(), contactPhone: fullPhoneNumber, contactEmail: contactEmailController.text.trim(), remark: remarkController.text.trim(), parentCompanyId: _selectedHeadquarterId, // 본사 ID 설정 companyTypes: [CompanyType.customer], // 기본값 isPartner: false, isCustomer: true, createdAt: DateTime.now(), updatedAt: DateTime.now(), isActive: true, ); await _companyService.createCompany(branchCompany); } return true; } catch (e) { _errorMessage = '지점 저장 중 오류가 발생했습니다: $e'; return false; } finally { _isSaving = false; notifyListeners(); } } /// 본사 목록 새로고침 Future refreshHeadquarters() async { await _loadHeadquarters(); } /// 변경사항 확인 (수정 모드에서만 사용) bool hasChanges() { if (!isEditMode || _originalBranch == null) return false; final currentAddress = Address.fromFullAddress(addressController.text); final currentFullPhone = PhoneUtils.getFullPhoneNumber( _selectedPhonePrefix, phoneNumberController.text ); return nameController.text.trim() != _originalBranch!.name || currentAddress.detailAddress != (_originalBranch!.address.detailAddress ?? '') || contactNameController.text.trim() != (_originalBranch!.contactName ?? '') || contactPositionController.text.trim() != (_originalBranch!.contactPosition ?? '') || currentFullPhone != (_originalBranch!.contactPhone ?? '') || contactEmailController.text.trim() != (_originalBranch!.contactEmail ?? '') || remarkController.text.trim() != (_originalBranch!.remark ?? '') || _selectedHeadquarterId != _originalBranch!.parentCompanyId; } /// 폼 초기화 void resetForm() { nameController.clear(); contactNameController.clear(); contactPositionController.clear(); phoneNumberController.clear(); contactEmailController.clear(); remarkController.clear(); addressController.clear(); _selectedHeadquarterId = null; _selectedHeadquarterName = null; _selectedPhonePrefix = '010'; branchAddress = const Address(); _errorMessage = null; notifyListeners(); } /// 전화번호 파싱 헬퍼 메서드 Map _parsePhoneNumber(String phoneNumber) { final cleaned = phoneNumber.replaceAll(RegExp(r'[^\d]'), ''); // 휴대폰 번호 (010, 011, 016, 017, 018, 019, 070) if (cleaned.startsWith('010') || cleaned.startsWith('011') || cleaned.startsWith('016') || cleaned.startsWith('017') || cleaned.startsWith('018') || cleaned.startsWith('019') || cleaned.startsWith('070')) { final prefix = cleaned.substring(0, 3); final number = cleaned.length > 3 ? cleaned.substring(3) : ''; final formatted = number.length > 4 ? '${number.substring(0, number.length - 4)}-${number.substring(number.length - 4)}' : number; return {'prefix': prefix, 'number': formatted}; } // 서울 지역번호 (02) if (cleaned.startsWith('02')) { final prefix = '02'; final number = cleaned.length > 2 ? cleaned.substring(2) : ''; final formatted = number.length > 4 ? '${number.substring(0, number.length - 4)}-${number.substring(number.length - 4)}' : number; return {'prefix': prefix, 'number': formatted}; } // 기타 지역번호 (031, 032, 033 등) if (cleaned.length >= 3 && cleaned.startsWith('0')) { final prefix = cleaned.substring(0, 3); final number = cleaned.length > 3 ? cleaned.substring(3) : ''; final formatted = number.length > 4 ? '${number.substring(0, number.length - 4)}-${number.substring(number.length - 4)}' : number; return {'prefix': prefix, 'number': formatted}; } // 파싱 실패 시 기본값 return {'prefix': '010', 'number': phoneNumber}; } /// Failure 메시지 변환 String _getFailureMessage(Failure failure) { switch (failure.runtimeType) { case ServerFailure: return '서버 오류가 발생했습니다'; case NetworkFailure: return '네트워크 연결을 확인해주세요'; case CacheFailure: return '데이터 저장 중 오류가 발생했습니다'; default: return failure.message ?? '알 수 없는 오류가 발생했습니다'; } } @override void dispose() { nameController.dispose(); contactNameController.dispose(); contactPositionController.dispose(); contactPhoneController.dispose(); contactEmailController.dispose(); remarkController.dispose(); addressController.dispose(); phoneNumberController.dispose(); super.dispose(); } }