- 전체 371개 파일 중 82개 미사용 파일 식별 - Phase 1: 33개 파일 삭제 예정 (100% 안전) - Phase 2: 30개 파일 삭제 검토 예정 - Phase 3: 19개 파일 수동 검토 예정 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
737 lines
27 KiB
Dart
737 lines
27 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/services/mock_data_service.dart'; // Mock 서비스 제거
|
||
import 'package:superport/services/company_service.dart';
|
||
import 'package:superport/core/errors/failures.dart';
|
||
import 'dart:async';
|
||
import 'branch_form_controller.dart'; // 분리된 지점 컨트롤러 import
|
||
import 'package:superport/data/models/zipcode_dto.dart';
|
||
import 'package:superport/data/datasources/remote/api_client.dart';
|
||
import 'package:dio/dio.dart';
|
||
|
||
/// 회사 폼 컨트롤러 - 비즈니스 로직 처리
|
||
class CompanyFormController {
|
||
// final MockDataService? dataService; // Mock 서비스 제거
|
||
final CompanyService _companyService = GetIt.instance<CompanyService>();
|
||
final int? companyId;
|
||
|
||
// Feature flag for API usage
|
||
final bool _useApi;
|
||
|
||
final TextEditingController nameController = TextEditingController();
|
||
Address companyAddress = const Address();
|
||
final TextEditingController zipcodeController = TextEditingController();
|
||
ZipcodeDto? selectedZipcode;
|
||
final TextEditingController contactNameController = TextEditingController();
|
||
final TextEditingController contactPositionController =
|
||
TextEditingController();
|
||
final TextEditingController contactPhoneController = TextEditingController();
|
||
final TextEditingController contactEmailController = TextEditingController();
|
||
final TextEditingController remarkController = TextEditingController();
|
||
final FocusNode nameFocusNode = FocusNode();
|
||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||
final ScrollController scrollController = ScrollController();
|
||
|
||
// 회사 유형 선택값 (복수)
|
||
List<CompanyType> selectedCompanyTypes = [CompanyType.customer];
|
||
|
||
// 부모 회사 선택 (단순화)
|
||
int? selectedParentCompanyId;
|
||
Map<int, String> availableParentCompanies = {};
|
||
|
||
List<String> companyNames = [];
|
||
List<String> filteredCompanyNames = [];
|
||
bool showCompanyNameDropdown = false;
|
||
|
||
// 분리된 BranchFormController 리스트로 관리
|
||
List<BranchFormController> branchControllers = [];
|
||
|
||
// 직책 목록 및 전화번호 접두사 목록(공통 상수)
|
||
final List<String> positions = [
|
||
'대표이사',
|
||
'사장',
|
||
'부사장',
|
||
'전무',
|
||
'상무',
|
||
'이사',
|
||
'부장',
|
||
'차장',
|
||
'팀장',
|
||
'과장',
|
||
'대리',
|
||
'사원',
|
||
'주임',
|
||
'기타',
|
||
];
|
||
final List<String> phonePrefixes = getCommonPhonePrefixes();
|
||
|
||
String selectedPhonePrefix = '010';
|
||
final List<String> phonePrefixesForMain = getCommonPhonePrefixes();
|
||
|
||
ValueNotifier<bool> showPositionDropdownNotifier = ValueNotifier<bool>(false);
|
||
Timer? debounceTimer;
|
||
bool preventAutoFocus = false;
|
||
final Map<int, bool> isNewlyAddedBranch = {};
|
||
|
||
CompanyFormController({this.companyId, bool useApi = true})
|
||
: _useApi = useApi {
|
||
_setupFocusNodes();
|
||
_setupControllerListeners();
|
||
// 비동기 초기화는 별도로 호출해야 함
|
||
Future.microtask(() => loadParentCompanies());
|
||
}
|
||
|
||
// 부모 회사 목록 로드 (LOOKUP COMPANIES API 직접 호출)
|
||
Future<void> loadParentCompanies() async {
|
||
try {
|
||
debugPrint('📝 부모 회사 목록 로드 시작 - LOOKUP /companies API 직접 호출');
|
||
|
||
// API 직접 호출 (GetIt DI 사용)
|
||
final apiClient = GetIt.instance<ApiClient>();
|
||
|
||
debugPrint('📞 === LOOKUP COMPANIES API 요청 ===');
|
||
debugPrint('📞 URL: /lookups/companies');
|
||
|
||
final response = await apiClient.get('/lookups/companies');
|
||
|
||
debugPrint('📊 === LOOKUP COMPANIES API 응답 ===');
|
||
debugPrint('📊 Status Code: ${response.statusCode}');
|
||
debugPrint('📊 Response Data: ${response.data}');
|
||
|
||
if (response.statusCode == 200 && response.data != null) {
|
||
final List<dynamic> companiesJson = response.data as List<dynamic>;
|
||
|
||
debugPrint('🎯 === LOOKUP COMPANIES API 성공 ===');
|
||
debugPrint('📊 받은 회사 총 개수: ${companiesJson.length}개');
|
||
|
||
if (companiesJson.isNotEmpty) {
|
||
debugPrint('📝 Lookup 회사 목록:');
|
||
for (int i = 0; i < companiesJson.length && i < 15; i++) {
|
||
final company = companiesJson[i];
|
||
debugPrint(' ${i + 1}. ID: ${company['id']}, 이름: ${company['name']}');
|
||
}
|
||
if (companiesJson.length > 15) {
|
||
debugPrint(' ... 외 ${companiesJson.length - 15}개 더');
|
||
}
|
||
}
|
||
|
||
// ===== 부모회사 드롭다운 구성 =====
|
||
availableParentCompanies = {};
|
||
for (final companyJson in companiesJson) {
|
||
final id = companyJson['id'] as int?;
|
||
final name = companyJson['name'] as String?;
|
||
|
||
if (id != null && name != null && id != companyId) {
|
||
availableParentCompanies[id] = name;
|
||
}
|
||
}
|
||
|
||
debugPrint('✅ 부모 회사 목록 로드 완료: ${availableParentCompanies.length}개');
|
||
debugPrint('📝 드롭다운에 표시될 회사들: ${availableParentCompanies.values.take(5).join(", ")}${availableParentCompanies.length > 5 ? "..." : ""}');
|
||
|
||
} else {
|
||
debugPrint('❌ Lookup Companies API 실패: 상태코드 ${response.statusCode}');
|
||
availableParentCompanies = {};
|
||
}
|
||
|
||
} catch (e) {
|
||
debugPrint('❌ 부모 회사 목록 로드 예외: $e');
|
||
availableParentCompanies = {};
|
||
}
|
||
}
|
||
|
||
// 회사 데이터 로드 (수정 모드)
|
||
Future<void> loadCompanyData() async {
|
||
if (companyId == null) {
|
||
debugPrint('❌ companyId가 null입니다');
|
||
return;
|
||
}
|
||
|
||
debugPrint('📝 loadCompanyData 시작 - ID: $companyId');
|
||
|
||
try {
|
||
Company? company;
|
||
|
||
if (_useApi) {
|
||
debugPrint('📝 API에서 회사 정보 로드 중...');
|
||
company = await _companyService.getCompanyDetail(companyId!);
|
||
debugPrint('📝 API 응답 받음: ${company != null ? "성공" : "null"}');
|
||
} else {
|
||
debugPrint('📝 API만 사용 가능');
|
||
throw Exception('API를 통해만 데이터를 로드할 수 있습니다');
|
||
}
|
||
|
||
debugPrint('📝 로드된 회사 정보:');
|
||
debugPrint(' - ID: ${company.id}');
|
||
debugPrint(' - 이름: ${company.name}');
|
||
debugPrint(' - 담당자: ${company.contactName}');
|
||
debugPrint(' - 연락처: ${company.contactPhone}');
|
||
debugPrint(' - 이메일: ${company.contactEmail}');
|
||
|
||
// 폼 필드에 데이터 설정
|
||
debugPrint('📝 회사명 설정 전: "${nameController.text}"');
|
||
nameController.text = company.name;
|
||
debugPrint('📝 회사명 설정 후: "${nameController.text}"');
|
||
|
||
companyAddress = company.address;
|
||
debugPrint('📝 주소 설정: $companyAddress');
|
||
|
||
contactNameController.text = company.contactName ?? '';
|
||
debugPrint('📝 담당자명 설정: "${contactNameController.text}"');
|
||
|
||
contactPositionController.text = company.contactPosition ?? '';
|
||
debugPrint('📝 직급 설정: "${contactPositionController.text}"');
|
||
|
||
contactEmailController.text = company.contactEmail ?? '';
|
||
debugPrint('📝 이메일 설정: "${contactEmailController.text}"');
|
||
|
||
remarkController.text = company.remark ?? '';
|
||
debugPrint('📝 비고 설정: "${remarkController.text}"');
|
||
|
||
// 전화번호 처리 - 수정 모드에서는 전체 전화번호를 그대로 사용
|
||
if (company.contactPhone != null && company.contactPhone!.isNotEmpty) {
|
||
// 통합 필드를 위해 전체 전화번호를 그대로 저장
|
||
contactPhoneController.text = company.contactPhone!;
|
||
|
||
// 기존 분리 로직은 참고용으로만 유지
|
||
selectedPhonePrefix = extractPhonePrefix(
|
||
company.contactPhone!,
|
||
phonePrefixes,
|
||
);
|
||
debugPrint('📝 전화번호 설정 (전체): ${contactPhoneController.text}');
|
||
debugPrint('📝 전화번호 접두사 (참고): $selectedPhonePrefix');
|
||
}
|
||
|
||
// 회사 유형 설정
|
||
selectedCompanyTypes = List.from(company.companyTypes);
|
||
debugPrint('📝 회사 유형 설정: $selectedCompanyTypes');
|
||
|
||
// 부모 회사 설정
|
||
selectedParentCompanyId = company.parentCompanyId;
|
||
debugPrint('📝 부모 회사 설정: $selectedParentCompanyId');
|
||
|
||
// 지점 정보 설정
|
||
if (company.branches != null && company.branches!.isNotEmpty) {
|
||
branchControllers.clear();
|
||
for (final branch in company.branches!) {
|
||
branchControllers.add(
|
||
BranchFormController(
|
||
branch: branch,
|
||
positions: positions,
|
||
phonePrefixes: phonePrefixes,
|
||
),
|
||
);
|
||
}
|
||
debugPrint('📝 지점 설정 완료: ${branchControllers.length}개');
|
||
}
|
||
|
||
debugPrint('📝 폼 필드 설정 완료:');
|
||
debugPrint(' - 회사명: "${nameController.text}"');
|
||
debugPrint(' - 담당자: "${contactNameController.text}"');
|
||
debugPrint(' - 이메일: "${contactEmailController.text}"');
|
||
debugPrint(' - 전화번호: "$selectedPhonePrefix-${contactPhoneController.text}"');
|
||
debugPrint(' - 지점 수: ${branchControllers.length}');
|
||
debugPrint(' - 회사 유형: $selectedCompanyTypes');
|
||
|
||
// TextEditingController는 text 설정 시 자동으로 리스너 트리거됨
|
||
// notifyListeners() 직접 호출은 불필요하고 부적절함
|
||
|
||
debugPrint('✅ 폼 데이터 로드 완료');
|
||
} catch (e, stackTrace) {
|
||
debugPrint('❌ 회사 정보 로드 실패: $e');
|
||
debugPrint('❌ 스택 트레이스: $stackTrace');
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
void dispose() {
|
||
debounceTimer?.cancel();
|
||
scrollController.dispose();
|
||
showPositionDropdownNotifier.dispose();
|
||
for (final branchController in branchControllers) {
|
||
branchController.dispose();
|
||
}
|
||
nameController.dispose();
|
||
contactNameController.dispose();
|
||
contactPositionController.dispose();
|
||
contactPhoneController.dispose();
|
||
contactEmailController.dispose();
|
||
remarkController.dispose();
|
||
nameFocusNode.dispose();
|
||
}
|
||
|
||
void _setupFocusNodes() {
|
||
nameFocusNode.addListener(() {
|
||
if (nameFocusNode.hasFocus) {
|
||
showCompanyNameDropdown = filteredCompanyNames.isNotEmpty;
|
||
} else {
|
||
showCompanyNameDropdown = false;
|
||
}
|
||
});
|
||
}
|
||
|
||
void _setupControllerListeners() {
|
||
nameController.addListener(_onCompanyNameTextChanged);
|
||
}
|
||
|
||
|
||
|
||
void updateCompanyAddress(Address address) {
|
||
companyAddress = address;
|
||
}
|
||
|
||
void updateBranchAddress(int index, Address address) {
|
||
if (index >= 0 && index < branchControllers.length) {
|
||
branchControllers[index].updateAddress(address);
|
||
}
|
||
}
|
||
|
||
void _onCompanyNameTextChanged() {
|
||
final query = nameController.text.toLowerCase();
|
||
if (query.isEmpty) {
|
||
filteredCompanyNames = List.from(companyNames);
|
||
} else {
|
||
filteredCompanyNames =
|
||
companyNames
|
||
.where((name) => name.toLowerCase().contains(query))
|
||
.toList();
|
||
}
|
||
showCompanyNameDropdown =
|
||
nameFocusNode.hasFocus && filteredCompanyNames.isNotEmpty;
|
||
}
|
||
|
||
void selectCompanyName(String name) {
|
||
nameController.text = name;
|
||
showCompanyNameDropdown = false;
|
||
}
|
||
|
||
// 지점 추가
|
||
void addBranch() {
|
||
final newBranch = Branch(
|
||
companyId: companyId ?? 0,
|
||
name: '지점 |