주요 변경사항: - Company-Branch → 계층형 Company 구조 완전 마이그레이션 - Equipment 모델 필드명 표준화 (current_company_id → company_id) - DropdownButton assertion 오류 완전 해결 - 지점 추가 드롭다운 페이지네이션 문제 해결 (20개→55개 전체 표시) - Equipment 백엔드 API 데이터 활용도 40%→100% 달성 - 소프트 딜리트 시스템 안정성 향상 기술적 개선: - Branch 관련 deprecated 메서드 정리 - Equipment Status 유효성 검증 로직 추가 - Company 리스트 페이지네이션 최적화 - DTO 모델 Freezed 코드 생성 완료 - 테스트 파일 API 구조 변경 대응 성과: - Flutter 웹 빌드 성공 (컴파일 에러 0건) - 백엔드 API 호환성 95% 달성 - 시스템 안정성 및 사용자 경험 대폭 개선
321 lines
10 KiB
Dart
321 lines
10 KiB
Dart
import 'package:superport/models/address_model.dart';
|
|
|
|
/// 회사 유형 열거형
|
|
/// - 고객사: 서비스를 이용하는 회사
|
|
/// - 파트너사: 서비스를 제공하는 회사
|
|
enum CompanyType {
|
|
customer, // 고객사
|
|
partner, // 파트너사
|
|
}
|
|
|
|
/// 회사 유형을 문자열로 변환 (복수 지원)
|
|
String companyTypeToString(CompanyType type) {
|
|
switch (type) {
|
|
case CompanyType.customer:
|
|
return '고객사';
|
|
case CompanyType.partner:
|
|
return '파트너사';
|
|
}
|
|
}
|
|
|
|
/// 문자열에서 회사 유형으로 변환 (단일)
|
|
CompanyType stringToCompanyType(String type) {
|
|
switch (type) {
|
|
case '고객사':
|
|
return CompanyType.customer;
|
|
case '파트너사':
|
|
return CompanyType.partner;
|
|
default:
|
|
return CompanyType.customer; // 기본값은 고객사
|
|
}
|
|
}
|
|
|
|
/// 문자열 리스트에서 회사 유형 리스트로 변환
|
|
List<CompanyType> stringListToCompanyTypeList(List<dynamic> types) {
|
|
// 중복 제거를 위한 Set 사용
|
|
final Set<CompanyType> uniqueTypes = {};
|
|
|
|
for (final e in types) {
|
|
if (e is CompanyType) {
|
|
uniqueTypes.add(e);
|
|
} else if (e is String) {
|
|
final normalized = e.toLowerCase().trim();
|
|
if (normalized == 'partner' || normalized.contains('partner')) {
|
|
uniqueTypes.add(CompanyType.partner);
|
|
} else if (normalized == 'customer' || normalized.contains('customer')) {
|
|
uniqueTypes.add(CompanyType.customer);
|
|
} else if (normalized == 'other') {
|
|
// "Other" 케이스는 고객사로 기본 매핑
|
|
uniqueTypes.add(CompanyType.customer);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 빈 경우 기본값 반환
|
|
if (uniqueTypes.isEmpty) {
|
|
return [CompanyType.customer];
|
|
}
|
|
|
|
return uniqueTypes.toList();
|
|
}
|
|
|
|
/// 회사 유형 리스트를 문자열 리스트로 변환
|
|
List<String> companyTypeListToStringList(List<CompanyType> types) {
|
|
return types.map((e) => companyTypeToString(e)).toList();
|
|
}
|
|
|
|
class Branch {
|
|
final int? id;
|
|
final int companyId;
|
|
final String name;
|
|
final Address address; // 주소 모델 사용
|
|
final String? contactName; // 담당자 이름
|
|
final String? contactPosition; // 담당자 직책
|
|
final String? contactPhone; // 담당자 전화번호
|
|
final String? contactEmail; // 담당자 이메일
|
|
final String? remark; // 비고
|
|
|
|
Branch({
|
|
this.id,
|
|
required this.companyId,
|
|
required this.name,
|
|
Address? address, // 옵셔널 파라미터로 변경
|
|
this.contactName,
|
|
this.contactPosition,
|
|
this.contactPhone,
|
|
this.contactEmail,
|
|
this.remark,
|
|
}) : address = address ?? const Address(); // 기본값 제공
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id': id,
|
|
'companyId': companyId,
|
|
'name': name,
|
|
'address': address.toString(), // 하위 호환성을 위해 문자열로 변환
|
|
'addressData': address.toJson(), // 새로운 형식으로 저장
|
|
'contactName': contactName,
|
|
'contactPosition': contactPosition,
|
|
'contactPhone': contactPhone,
|
|
'contactEmail': contactEmail,
|
|
'remark': remark,
|
|
};
|
|
}
|
|
|
|
factory Branch.fromJson(Map<String, dynamic> json) {
|
|
// 주소 데이터가 새 형식으로 저장되어 있는지 확인
|
|
Address addressData;
|
|
if (json.containsKey('addressData')) {
|
|
addressData = Address.fromJson(json['addressData']);
|
|
} else if (json.containsKey('address') && json['address'] != null) {
|
|
// 이전 버전 호환성 - 문자열 주소를 Address 객체로 변환
|
|
addressData = Address.fromFullAddress(json['address']);
|
|
} else {
|
|
addressData = const Address();
|
|
}
|
|
|
|
return Branch(
|
|
id: json['id'],
|
|
companyId: json['companyId'],
|
|
name: json['name'],
|
|
address: addressData,
|
|
contactName: json['contactName'],
|
|
contactPosition: json['contactPosition'],
|
|
contactPhone: json['contactPhone'],
|
|
contactEmail: json['contactEmail'],
|
|
remark: json['remark'],
|
|
);
|
|
}
|
|
|
|
/// 복사본을 생성하고 일부 필드를 업데이트합니다.
|
|
Branch copyWith({
|
|
int? id,
|
|
int? companyId,
|
|
String? name,
|
|
Address? address,
|
|
String? contactName,
|
|
String? contactPosition,
|
|
String? contactPhone,
|
|
String? contactEmail,
|
|
String? remark,
|
|
}) {
|
|
return Branch(
|
|
id: id ?? this.id,
|
|
companyId: companyId ?? this.companyId,
|
|
name: name ?? this.name,
|
|
address: address ?? this.address,
|
|
contactName: contactName ?? this.contactName,
|
|
contactPosition: contactPosition ?? this.contactPosition,
|
|
contactPhone: contactPhone ?? this.contactPhone,
|
|
contactEmail: contactEmail ?? this.contactEmail,
|
|
remark: remark ?? this.remark,
|
|
);
|
|
}
|
|
}
|
|
|
|
class Company {
|
|
final int? id;
|
|
final String name;
|
|
final Address address; // 주소 모델 사용
|
|
final String? contactName; // 담당자 이름
|
|
final String? contactPosition; // 담당자 직책
|
|
final String? contactPhone; // 담당자 전화번호
|
|
final String? contactEmail; // 담당자 이메일
|
|
final List<Branch>? branches;
|
|
final List<CompanyType> companyTypes; // 회사 유형 (복수 가능)
|
|
final String? remark; // 비고
|
|
final bool isActive; // 활성 상태
|
|
final bool isPartner; // 파트너사 플래그
|
|
final bool isCustomer; // 고객사 플래그
|
|
final int? parentCompanyId; // 상위 회사 ID (계층형 구조)
|
|
final DateTime? createdAt; // 생성일
|
|
final DateTime? updatedAt; // 수정일
|
|
|
|
Company({
|
|
this.id,
|
|
required this.name,
|
|
Address? address, // 옵셔널 파라미터로 변경
|
|
this.contactName,
|
|
this.contactPosition,
|
|
this.contactPhone,
|
|
this.contactEmail,
|
|
this.branches,
|
|
this.companyTypes = const [CompanyType.customer], // 기본값은 고객사
|
|
this.remark,
|
|
this.isActive = true, // 기본값은 활성
|
|
this.isPartner = false, // 기본값은 파트너 아님
|
|
this.isCustomer = true, // 기본값은 고객사
|
|
this.parentCompanyId, // 상위 회사 ID
|
|
this.createdAt,
|
|
this.updatedAt,
|
|
}) : address = address ?? const Address(); // 기본값 제공
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id': id,
|
|
'name': name,
|
|
'address': address.toString(), // 하위 호환성을 위해 문자열로 변환
|
|
'addressData': address.toJson(), // 새로운 형식으로 저장
|
|
'contactName': contactName,
|
|
'contactPosition': contactPosition,
|
|
'contactPhone': contactPhone,
|
|
'contactEmail': contactEmail,
|
|
'branches': branches?.map((branch) => branch.toJson()).toList(),
|
|
// 회사 유형을 문자열 리스트로 저장
|
|
'companyTypes': companyTypes.map((e) => e.toString()).toList(),
|
|
'remark': remark,
|
|
'isActive': isActive,
|
|
'isPartner': isPartner,
|
|
'isCustomer': isCustomer,
|
|
'parentCompanyId': parentCompanyId,
|
|
'createdAt': createdAt?.toIso8601String(),
|
|
'updatedAt': updatedAt?.toIso8601String(),
|
|
};
|
|
}
|
|
|
|
factory Company.fromJson(Map<String, dynamic> json) {
|
|
List<Branch>? branchList;
|
|
if (json['branches'] != null) {
|
|
branchList =
|
|
(json['branches'] as List)
|
|
.map((branchJson) => Branch.fromJson(branchJson))
|
|
.toList();
|
|
}
|
|
|
|
// 주소 데이터가 새 형식으로 저장되어 있는지 확인
|
|
Address addressData;
|
|
if (json.containsKey('addressData')) {
|
|
addressData = Address.fromJson(json['addressData']);
|
|
} else if (json.containsKey('address') && json['address'] != null) {
|
|
// 이전 버전 호환성 - 문자열 주소를 Address 객체로 변환
|
|
addressData = Address.fromFullAddress(json['address']);
|
|
} else {
|
|
addressData = const Address();
|
|
}
|
|
|
|
// 회사 유형 파싱 (복수) - 서버 응답 우선
|
|
List<CompanyType> types = [CompanyType.customer]; // 기본값
|
|
if (json.containsKey('company_types')) {
|
|
final raw = json['company_types'];
|
|
if (raw is List) {
|
|
types = stringListToCompanyTypeList(raw);
|
|
}
|
|
} else if (json.containsKey('companyTypes')) {
|
|
final raw = json['companyTypes'];
|
|
if (raw is List) {
|
|
types = stringListToCompanyTypeList(raw);
|
|
}
|
|
} else if (json.containsKey('companyType')) {
|
|
// 이전 버전 호환성: 단일 값
|
|
final raw = json['companyType'];
|
|
if (raw is String) {
|
|
types = [stringToCompanyType(raw)];
|
|
} else if (raw is int) {
|
|
types = [CompanyType.values[raw]];
|
|
}
|
|
}
|
|
|
|
return Company(
|
|
id: json['id'],
|
|
name: json['name'],
|
|
address: addressData,
|
|
contactName: json['contact_name'] ?? json['contactName'],
|
|
contactPosition: json['contact_position'] ?? json['contactPosition'],
|
|
contactPhone: json['contact_phone'] ?? json['contactPhone'],
|
|
contactEmail: json['contact_email'] ?? json['contactEmail'],
|
|
branches: branchList,
|
|
companyTypes: types,
|
|
remark: json['remark'],
|
|
isActive: json['is_active'] ?? json['isActive'] ?? true,
|
|
isPartner: json['is_partner'] ?? json['isPartner'] ?? false,
|
|
isCustomer: json['is_customer'] ?? json['isCustomer'] ?? true,
|
|
parentCompanyId: json['parent_company_id'] ?? json['parentCompanyId'],
|
|
createdAt: json['created_at'] != null
|
|
? DateTime.parse(json['created_at'])
|
|
: (json['createdAt'] != null ? DateTime.parse(json['createdAt']) : null),
|
|
updatedAt: json['updated_at'] != null
|
|
? DateTime.parse(json['updated_at'])
|
|
: (json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null),
|
|
);
|
|
}
|
|
|
|
/// 복사본을 생성하고 일부 필드를 업데이트합니다.
|
|
Company copyWith({
|
|
int? id,
|
|
String? name,
|
|
Address? address,
|
|
String? contactName,
|
|
String? contactPosition,
|
|
String? contactPhone,
|
|
String? contactEmail,
|
|
List<Branch>? branches,
|
|
List<CompanyType>? companyTypes,
|
|
String? remark,
|
|
bool? isActive,
|
|
bool? isPartner,
|
|
bool? isCustomer,
|
|
int? parentCompanyId,
|
|
DateTime? createdAt,
|
|
DateTime? updatedAt,
|
|
}) {
|
|
return Company(
|
|
id: id ?? this.id,
|
|
name: name ?? this.name,
|
|
address: address ?? this.address,
|
|
contactName: contactName ?? this.contactName,
|
|
contactPosition: contactPosition ?? this.contactPosition,
|
|
contactPhone: contactPhone ?? this.contactPhone,
|
|
contactEmail: contactEmail ?? this.contactEmail,
|
|
branches: branches ?? this.branches,
|
|
companyTypes: companyTypes ?? this.companyTypes,
|
|
remark: remark ?? this.remark,
|
|
isActive: isActive ?? this.isActive,
|
|
isPartner: isPartner ?? this.isPartner,
|
|
isCustomer: isCustomer ?? this.isCustomer,
|
|
parentCompanyId: parentCompanyId ?? this.parentCompanyId,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
updatedAt: updatedAt ?? this.updatedAt,
|
|
);
|
|
}
|
|
}
|