Files
superport/lib/screens/company/controllers/branch_controller.dart

372 lines
13 KiB
Dart

/// 지점 관리 컨트롤러 (입력/수정 통합)
///
/// 지점 생성 및 수정 화면의 비즈니스 로직을 담당하는 통합 컨트롤러 클래스
/// 주요 기능:
/// - 본사 목록 조회 및 관리
/// - 지점 정보 입력/수정 관리
/// - 지점 생성/수정 요청
/// - 폼 유효성 검증
/// - 수정 모드에서 기존 데이터 로드
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<CompanyService>();
// 수정 모드 관련
final int? branchId; // 수정할 지점 ID (null이면 생성 모드)
final String? parentCompanyName; // 본사명 (수정 모드에서 표시용)
bool get isEditMode => branchId != null;
// 원본 데이터 (변경 감지용)
Company? _originalBranch;
Company? get originalBranch => _originalBranch;
// 본사 목록 관련
List<CompanyItem> _headquartersList = [];
List<CompanyItem> 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<FormState> formKey = GlobalKey<FormState>();
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<String> _phonePrefixes = PhoneUtils.getCommonPhonePrefixes();
List<String> get phonePrefixes => _phonePrefixes;
BranchController({
this.branchId, // 수정 모드: 지점 ID, 생성 모드: null
this.parentCompanyName, // 수정 모드: 본사명 (표시용)
}) {
if (!isEditMode) {
// 생성 모드: 본사 목록 로드
_loadHeadquarters();
} else {
// 수정 모드: 지점 데이터 로드 및 본사 목록도 로드
_loadHeadquarters();
_loadBranchData();
}
}
/// 지점 데이터 로드 (수정 모드에서만 사용)
Future<void> _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<void> _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<bool> 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<void> 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<String, String> _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();
}
}