372 lines
13 KiB
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();
|
|
}
|
|
} |