import 'package:flutter/services.dart'; /// 한국 사업자 번호 자동 포맷팅 (000-00-00000) class BusinessNumberFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { // 숫자만 추출 final digitsOnly = newValue.text.replaceAll(RegExp(r'[^\d]'), ''); // 최대 10자리 제한 (000-00-00000 = 10자리) final truncated = digitsOnly.length > 10 ? digitsOnly.substring(0, 10) : digitsOnly; // 포맷팅 String formatted = ''; for (int i = 0; i < truncated.length; i++) { if ((i == 3 || i == 5) && i < truncated.length) { formatted += '-'; } formatted += truncated[i]; } // 커서 위치 계산 int cursorPosition = formatted.length; // 백스페이스 처리: 하이픈 앞에서 백스페이스를 누르면 하이픈도 함께 삭제 if (oldValue.text.length > newValue.text.length) { if (newValue.selection.baseOffset == 4 || newValue.selection.baseOffset == 7) { formatted = formatted.substring(0, formatted.length - 1); cursorPosition = formatted.length; } } // 입력 중 커서 위치 조정 if (newValue.selection.baseOffset < newValue.text.length) { final beforeCursor = newValue.text.substring(0, newValue.selection.baseOffset); final digitsBeforeCursor = beforeCursor.replaceAll(RegExp(r'[^\d]'), '').length; cursorPosition = 0; int digitCount = 0; for (int i = 0; i < formatted.length; i++) { if (formatted[i] != '-') { digitCount++; } cursorPosition++; if (digitCount == digitsBeforeCursor) { break; } } } return TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: cursorPosition), ); } } /// 사업자 번호 유효성 검증 class BusinessNumberValidator { /// 사업자 번호 체크섬 검증 /// 대한민국 사업자등록번호 검증 알고리즘 사용 static bool isValid(String businessNumber) { // 하이픈 제거 final digitsOnly = businessNumber.replaceAll(RegExp(r'[^\d]'), ''); // 10자리가 아니면 무효 if (digitsOnly.length != 10) { return false; } // 체크섬 계산을 위한 가중치 const weights = [1, 3, 7, 1, 3, 7, 1, 3, 5]; int sum = 0; for (int i = 0; i < 9; i++) { sum += int.parse(digitsOnly[i]) * weights[i]; } // 9번째 자리(5)에 대한 추가 계산 sum += (int.parse(digitsOnly[8]) * 5) ~/ 10; // 체크섬 계산 final checksum = (10 - (sum % 10)) % 10; // 마지막 자리와 체크섬 비교 return checksum == int.parse(digitsOnly[9]); } /// 사업자 번호 유효성 검증 (폼 필드용) static String? validate(String? value) { if (value == null || value.isEmpty) { return '사업자 번호를 입력하세요'; } final digitsOnly = value.replaceAll(RegExp(r'[^\d]'), ''); if (digitsOnly.length != 10) { return '사업자 번호는 10자리여야 합니다'; } if (!isValid(value)) { return '유효하지 않은 사업자 번호입니다'; } return null; } /// 사업자 번호 타입 추출 static String getBusinessType(String businessNumber) { final digitsOnly = businessNumber.replaceAll(RegExp(r'[^\d]'), ''); if (digitsOnly.length < 5) return '알 수 없음'; final typeCode = digitsOnly.substring(3, 5); final typeNum = int.parse(typeCode); // 사업자 유형 분류 (국세청 기준) if (typeNum >= 1 && typeNum <= 79) { return '개인사업자'; } else if (typeNum >= 80 && typeNum <= 89) { return '법인사업자'; } else if (typeNum >= 90 && typeNum <= 99) { return '기타'; } return '알 수 없음'; } /// 지역 코드 추출 (첫 3자리 기준) static String? getRegionFromBusinessNumber(String businessNumber) { final digitsOnly = businessNumber.replaceAll(RegExp(r'[^\d]'), ''); if (digitsOnly.length < 3) return null; final regionCode = digitsOnly.substring(0, 3); final code = int.parse(regionCode); // 국세청 지역 코드 매핑 (주요 지역만) if (code >= 101 && code <= 115) return '서울 중부'; if (code >= 116 && code <= 123) return '서울 동부'; if (code >= 124 && code <= 133) return '서울 서부'; if (code >= 134 && code <= 139) return '서울 남부'; if (code >= 140 && code <= 149) return '서울 북부'; if (code >= 201 && code <= 209) return '부산'; if (code >= 210 && code <= 219) return '인천'; if (code >= 220 && code <= 229) return '경기 북부'; if (code >= 230 && code <= 239) return '경기 남부'; if (code >= 240 && code <= 249) return '강원'; if (code >= 301 && code <= 309) return '대전'; if (code >= 310 && code <= 319) return '충남'; if (code >= 320 && code <= 329) return '충북'; if (code >= 401 && code <= 409) return '광주'; if (code >= 410 && code <= 419) return '전남'; if (code >= 420 && code <= 429) return '전북'; if (code >= 501 && code <= 509) return '대구'; if (code >= 510 && code <= 519) return '경북'; if (code >= 601 && code <= 609) return '울산'; if (code >= 610 && code <= 619) return '경남'; if (code >= 701 && code <= 709) return '제주'; return null; } }