feat: 회사 관리 API 연동 구현

- CompanyService 및 RemoteDataSource 구현
- Company, Branch DTO 모델 생성 (Freezed)
- 의존성 주입 컨테이너 업데이트
- 회사 등록/수정 폼에 API 연동 로직 적용
- API 통합 계획 문서 업데이트
This commit is contained in:
JiWoong Sul
2025-07-24 17:56:06 +09:00
parent 47bfa3a26a
commit 6b31631cfb
16 changed files with 4171 additions and 85 deletions

View File

@@ -187,9 +187,9 @@ class EquipmentController extends ChangeNotifier {
- [x] 401: 잘못된 인증 정보 - [x] 401: 잘못된 인증 정보
- [ ] 429: 너무 많은 시도 - [ ] 429: 너무 많은 시도
- [x] 500: 서버 오류 - [x] 500: 서버 오류
- [ ] 자동 로그인 구현 - [x] 자동 로그인 구현
- [ ] 토큰 유효성 검사 - [x] 토큰 유효성 검사
- [ ] 토큰 자동 갱신 - [x] 토큰 자동 갱신
- [x] 로그아웃 기능 구현 - [x] 로그아웃 기능 구현
### 4.2 대시보드 ### 4.2 대시보드
@@ -201,21 +201,22 @@ class EquipmentController extends ChangeNotifier {
- GET /api/v1/licenses/expiring-soon - GET /api/v1/licenses/expiring-soon
**작업 Task**: **작업 Task**:
- [ ] DashboardService 생성 - [x] DashboardService 생성
- [ ] 통계 데이터 모델 생성 - [x] 통계 데이터 모델 생성
- [ ] OverviewStats DTO - [x] OverviewStats DTO
- [ ] RecentActivity DTO - [x] RecentActivity DTO
- [ ] StatusDistribution DTO - [x] StatusDistribution DTO (EquipmentStatusDistribution)
- [ ] DashboardController 비동기화 - [x] ExpiringLicense DTO
- [ ] 동시 다중 API 호출 구현 - [x] DashboardController 비동기화
- [ ] 부분 로딩 상태 관리 - [x] 동시 다중 API 호출 구현
- [x] 부분 로딩 상태 관리
- [ ] 실시간 업데이트 구현 - [ ] 실시간 업데이트 구현
- [ ] WebSocket 연결 설정 - [ ] WebSocket 연결 설정
- [ ] 실시간 이벤트 수신 - [ ] 실시간 이벤트 수신
- [ ] 캐싱 전략 구현 - [ ] 캐싱 전략 구현
- [ ] 5분 캐시 TTL - [ ] 5분 캐시 TTL
- [ ] Pull-to-refresh 구현 - [ ] Pull-to-refresh 구현
- [ ] 에러 시 부분 렌더링 - [x] 에러 시 부분 렌더링
### 4.3 장비 목록 ### 4.3 장비 목록
@@ -225,22 +226,22 @@ class EquipmentController extends ChangeNotifier {
- GET /api/v1/companies/names - GET /api/v1/companies/names
**작업 Task**: **작업 Task**:
- [ ] EquipmentService 생성 - [x] EquipmentService 생성
- [ ] 페이지네이션 구현 - [x] 페이지네이션 구현
- [ ] 무한 스크롤 구현 - [x] 무한 스크롤 구현
- [ ] 페이지 상태 관리 - [x] 페이지 상태 관리
- [ ] 로딩 인디케이터 - [x] 로딩 인디케이터
- [ ] 필터링 기능 - [x] 필터링 기능
- [ ] 카테고리별 필터 - [x] 카테고리별 필터
- [ ] 상태별 필터 - [x] 상태별 필터
- [ ] 회사별 필터 - [x] 회사별 필터
- [ ] 날짜 범위 필터 - [x] 날짜 범위 필터
- [ ] 정렬 기능 - [x] 정렬 기능
- [ ] 생성일 정렬 - [x] 생성일 정렬
- [ ] 이름 정렬 - [x] 이름 정렬
- [ ] 상태 정렬 - [x] 상태 정렬
- [ ] 검색 기능 - [x] 검색 기능
- [ ] 디바운싱 구현 - [x] 디바운싱 구현
- [ ] 검색 결과 하이라이트 - [ ] 검색 결과 하이라이트
- [ ] 일괄 작업 - [ ] 일괄 작업
- [ ] 다중 선택 UI - [ ] 다중 선택 UI
@@ -256,22 +257,22 @@ class EquipmentController extends ChangeNotifier {
- POST /api/v1/files/upload - POST /api/v1/files/upload
**작업 Task**: **작업 Task**:
- [ ] 상세 정보 로딩 - [x] 상세 정보 로딩
- [ ] 기본 정보 표시 - [x] 기본 정보 표시
- [ ] 이력 정보 로딩 - [x] 이력 정보 로딩
- [ ] 관련 문서 표시 - [ ] 관련 문서 표시
- [ ] 편집 모드 구현 - [x] 편집 모드 구현
- [ ] 폼 데이터 바인딩 - [x] 폼 데이터 바인딩
- [ ] 실시간 검증 - [x] 실시간 검증
- [ ] 변경사항 추적 - [x] 변경사항 추적
- [ ] 이미지 업로드 - [ ] 이미지 업로드
- [ ] 파일 선택 UI - [ ] 파일 선택 UI
- [ ] 업로드 진행률 - [ ] 업로드 진행률
- [ ] 썸네일 생성 - [ ] 썸네일 생성
- [ ] 히스토리 표시 - [x] 히스토리 표시
- [ ] 타임라인 UI - [x] 타임라인 UI
- [ ] 상태 변경 이력 - [x] 상태 변경 이력
- [ ] 담당자 정보 - [x] 담당자 정보
### 4.5 장비 입고 ### 4.5 장비 입고
@@ -281,10 +282,10 @@ class EquipmentController extends ChangeNotifier {
- GET /api/v1/equipment/serial-check/{serial} - GET /api/v1/equipment/serial-check/{serial}
**작업 Task**: **작업 Task**:
- [ ] 입고 폼 구현 - [x] 입고 폼 구현
- [ ] 장비 정보 입력 - [x] 장비 정보 입력
- [ ] 시리얼 번호 중복 검사 - [x] 시리얼 번호 중복 검사
- [ ] 창고 위치 선택 - [x] 창고 위치 선택
- [ ] 바코드 스캔 통합 - [ ] 바코드 스캔 통합
- [ ] 카메라 권한 요청 - [ ] 카메라 권한 요청
- [ ] 바코드 디코딩 - [ ] 바코드 디코딩
@@ -305,10 +306,10 @@ class EquipmentController extends ChangeNotifier {
- GET /api/v1/customers - GET /api/v1/customers
**작업 Task**: **작업 Task**:
- [ ] 출고 폼 구현 - [x] 출고 폼 구현
- [ ] 가용 장비 조회 - [x] 가용 장비 조회
- [ ] 수량 검증 - [x] 수량 검증
- [ ] 고객 정보 입력 - [x] 고객 정보 입력
- [ ] 출고 승인 프로세스 - [ ] 출고 승인 프로세스
- [ ] 승인 요청 - [ ] 승인 요청
- [ ] 승인자 알림 - [ ] 승인자 알림
@@ -328,11 +329,22 @@ class EquipmentController extends ChangeNotifier {
- POST /api/v1/companies/{id}/branches - POST /api/v1/companies/{id}/branches
**작업 Task**: **작업 Task**:
- [x] CompanyService 생성
- [x] DTO 모델 생성
- [x] CompanyDto (생성/수정/응답)
- [x] CompanyListDto (목록 조회)
- [x] BranchDto (지점 관련)
- [x] CompanyRemoteDataSource 구현
- [x] 모든 CRUD 메서드 구현
- [x] 지점 관련 API 메서드 구현
- [x] DI 등록 (CompanyRemoteDataSource, CompanyService)
- [ ] 회사 목록 구현 - [ ] 회사 목록 구현
- [ ] Controller API 연동
- [ ] 본사/지점 트리 구조 - [ ] 본사/지점 트리 구조
- [ ] 확장/축소 UI - [ ] 확장/축소 UI
- [ ] 검색 필터 - [ ] 검색 필터
- [ ] 회사 등록 - [ ] 회사 등록
- [ ] Controller API 연동
- [ ] 사업자번호 검증 - [ ] 사업자번호 검증
- [ ] 주소 검색 API 연동 - [ ] 주소 검색 API 연동
- [ ] 중복 확인 - [ ] 중복 확인
@@ -463,14 +475,14 @@ class EquipmentController extends ChangeNotifier {
- 세션 타임아웃 처리 - 세션 타임아웃 처리
**작업 Task**: **작업 Task**:
- [ ] AuthService 구현 - [x] AuthService 구현
- [ ] 로그인/로그아웃 - [x] 로그인/로그아웃
- [ ] 토큰 저장/조회 - [x] 토큰 저장/조회
- [ ] 토큰 갱신 로직 - [x] 토큰 갱신 로직
- [ ] AuthInterceptor 구현 - [x] AuthInterceptor 구현
- [ ] 요청 헤더 토큰 추가 - [x] 요청 헤더 토큰 추가
- [ ] 401 에러 처리 - [x] 401 에러 처리
- [ ] 토큰 갱신 재시도 - [x] 토큰 갱신 재시도
- [ ] RouteGuard 구현 - [ ] RouteGuard 구현
- [ ] 인증 확인 - [ ] 인증 확인
- [ ] 권한 확인 - [ ] 권한 확인
@@ -488,7 +500,7 @@ class EquipmentController extends ChangeNotifier {
- [x] ApiClient 싱글톤 구현 - [x] ApiClient 싱글톤 구현
- [ ] BaseApiService 추상 클래스 - [ ] BaseApiService 추상 클래스
- [x] 환경별 설정 관리 - [x] 환경별 설정 관리
- [ ] 에러 핸들링 유틸 - [x] 에러 핸들링 유틸 (exceptions.dart, failures.dart 구현됨)
- [ ] 네트워크 연결 확인 - [ ] 네트워크 연결 확인
### 5.3 상태 관리 ### 5.3 상태 관리
@@ -616,11 +628,17 @@ Future<void> setupDependencies() async {
// API 클라이언트 ✅ // API 클라이언트 ✅
getIt.registerLazySingleton(() => ApiClient()); getIt.registerLazySingleton(() => ApiClient());
// 데이터소스 // 데이터소스
// TODO: Remote datasources will be registered here getIt.registerLazySingleton(() => AuthRemoteDataSource());
getIt.registerLazySingleton(() => DashboardRemoteDataSource());
getIt.registerLazySingleton(() => EquipmentRemoteDataSource());
getIt.registerLazySingleton(() => CompanyRemoteDataSource());
// 리포지토리 // 서비스 ✅
// TODO: Repositories will be registered here getIt.registerLazySingleton(() => AuthService());
getIt.registerLazySingleton(() => DashboardService());
getIt.registerLazySingleton(() => EquipmentService());
getIt.registerLazySingleton(() => CompanyService());
// 컨트롤러 // 컨트롤러
// TODO: Controllers will be registered here // TODO: Controllers will be registered here
@@ -699,13 +717,13 @@ class ErrorHandler {
- [x] AuthService 구현 - [x] AuthService 구현
- [x] 토큰 관리 로직 - [x] 토큰 관리 로직
- [x] 로그인/로그아웃 화면 연동 - [x] 로그인/로그아웃 화면 연동
- [ ] 자동 토큰 갱신 - [x] 자동 토큰 갱신
**3주차: 기본 데이터 레이어** **3주차: 기본 데이터 레이어**
- [ ] Repository 패턴 구현 - [ ] Repository 패턴 구현
- [ ] 기본 모델 변환 - [x] 기본 모델 변환
- [ ] 첫 화면(대시보드) API 연동 - [x] 첫 화면(대시보드) API 연동
- [ ] 로딩/에러 상태 관리 - [x] 로딩/에러 상태 관리
### 7.2 Phase 2: 핵심 기능 (4주) ### 7.2 Phase 2: 핵심 기능 (4주)
@@ -716,7 +734,7 @@ class ErrorHandler {
- [ ] 이미지 업로드 - [ ] 이미지 업로드
**6-7주차: 회사/사용자 관리** **6-7주차: 회사/사용자 관리**
- [ ] 회사 CRUD 구현 - [x] 회사 CRUD 구현 (Service/DataSource 완료, Controller 연동 필요)
- [ ] 지점 관리 기능 - [ ] 지점 관리 기능
- [ ] 사용자 관리 및 권한 - [ ] 사용자 관리 및 권한
- [ ] 프로필 관리 - [ ] 프로필 관리
@@ -763,17 +781,17 @@ class ErrorHandler {
#### 🔴 Critical (필수) #### 🔴 Critical (필수)
1. [x] API 클라이언트 설정 1. [x] API 클라이언트 설정
2. [ ] 인증 시스템 구현 2. [x] 인증 시스템 구현
3. [ ] 기본 CRUD 기능 3. [x] 기본 CRUD 기능 (장비, 회사 완료)
4. [ ] 에러 처리 4. [x] 에러 처리
5. [ ] 로딩 상태 관리 5. [x] 로딩 상태 관리
#### 🟡 High (중요) #### 🟡 High (중요)
6. [ ] 페이지네이션 6. [x] 페이지네이션
7. [ ] 검색/필터 7. [x] 검색/필터 (장비 완료)
8. [ ] 파일 업로드 8. [ ] 파일 업로드
9. [ ] 권한 관리 9. [ ] 권한 관리
10. [ ] 데이터 검증 10. [x] 데이터 검증
#### 🟢 Medium (개선) #### 🟢 Medium (개선)
11. [ ] 캐싱 11. [ ] 캐싱
@@ -816,8 +834,8 @@ class ErrorHandler {
- [ ] CI/CD 파이프라인 - [ ] CI/CD 파이프라인
#### 보안 #### 보안
- [ ] 토큰 안전 저장 - [x] 토큰 안전 저장 (SecureStorage 사용)
- [ ] API 키 관리 - [x] API 키 관리 (환경 변수 사용)
- [ ] 민감 정보 마스킹 - [ ] 민감 정보 마스킹
- [ ] 보안 감사 - [ ] 보안 감사
@@ -981,11 +999,12 @@ class ErrorHandler {
- ScrollController 리스너를 통한 페이지네이션 - ScrollController 리스너를 통한 페이지네이션
### 📈 진행률 ### 📈 진행률
- **전체 API 통합**: 70% 완료 - **전체 API 통합**: 75% 완료
- **인증 시스템**: 100% 완료 - **인증 시스템**: 100% 완료
- **대시보드**: 100% 완료 - **대시보드**: 100% 완료
- **장비 관리**: 100% 완료 (목록, 입고, 출고, 수정, 삭제, 이력 조회 모두 완료) - **장비 관리**: 100% 완료 (목록, 입고, 출고, 수정, 삭제, 이력 조회 모두 완료)
- **회사/사용자 관리**: 0% (대기 중) - **회사 관리**: 70% 완료 (Service/DataSource/DTO 완료, Controller 연동 필요)
- **사용자 관리**: 0% (대기 중)
### 📋 주요 특징 ### 📋 주요 특징
- **한글 입력**: 모든 API 요청/응답에서 UTF-8 인코딩 적용 - **한글 입력**: 모든 API 요청/응답에서 UTF-8 인코딩 적용
@@ -994,6 +1013,15 @@ class ErrorHandler {
- **무한 스크롤**: 대용량 데이터 처리를 위한 페이지네이션 - **무한 스크롤**: 대용량 데이터 처리를 위한 페이지네이션
- **로딩/에러 상태**: 사용자 친화적인 UI 피드백 - **로딩/에러 상태**: 사용자 친화적인 UI 피드백
#### 4차 작업 (2025-07-24 밤)
13. **회사 관리 API 연동 (진행중)**
- **DTO 모델 생성**: CompanyDto, CompanyListDto, BranchDto 모델 생성 및 Freezed 코드 생성 완료
- **CompanyRemoteDataSource 구현**: 회사 CRUD 및 지점 관련 API 엔드포인트 메서드 구현
- **CompanyService 구현**: 비즈니스 로직 및 DTO-Model 변환 처리
- **DI 등록**: CompanyRemoteDataSource, CompanyService 등록 완료
- **Controller 준비**: CompanyFormController에 API 사용을 위한 준비 완료 (실제 구현 대기)
- **미완료**: Controller에서 실제 API 호출 구현, 로딩/에러 상태 관리
--- ---
_마지막 업데이트: 2025-07-24 밤_ (장비 출고, 수정, 삭제, 이력 조회 API 연동 완료. Provider 패턴 적용, 에러 처리 강화) _마지막 업데이트: 2025-07-24 밤_ (회사 관리 API 인프라 구축 완료. Service/DataSource/DTO 구현 완료, Controller 연동 진행 필요)

View File

@@ -0,0 +1,253 @@
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/core/constants/api_endpoints.dart';
import 'package:superport/core/errors/exceptions.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/models/company/company_dto.dart';
import 'package:superport/data/models/company/company_list_dto.dart';
import 'package:superport/data/models/company/branch_dto.dart';
abstract class CompanyRemoteDataSource {
Future<List<CompanyListDto>> getCompanies({
int page = 1,
int perPage = 20,
String? search,
bool? isActive,
});
Future<CompanyResponse> createCompany(CreateCompanyRequest request);
Future<CompanyResponse> getCompanyDetail(int id);
Future<CompanyWithBranches> getCompanyWithBranches(int id);
Future<CompanyResponse> updateCompany(int id, UpdateCompanyRequest request);
Future<void> deleteCompany(int id);
Future<List<CompanyNameDto>> getCompanyNames();
// Branch related methods
Future<BranchResponse> createBranch(int companyId, CreateBranchRequest request);
Future<BranchResponse> getBranchDetail(int companyId, int branchId);
Future<BranchResponse> updateBranch(int companyId, int branchId, UpdateBranchRequest request);
Future<void> deleteBranch(int companyId, int branchId);
Future<List<BranchListDto>> getCompanyBranches(int companyId);
}
class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
final ApiClient _apiClient = GetIt.instance<ApiClient>();
@override
Future<List<CompanyListDto>> getCompanies({
int page = 1,
int perPage = 20,
String? search,
bool? isActive,
}) async {
try {
final queryParams = {
'page': page,
'per_page': perPage,
if (search != null) 'search': search,
if (isActive != null) 'is_active': isActive,
};
final response = await _apiClient.dio.get(
ApiEndpoints.companies,
queryParameters: queryParams,
);
final List<dynamic> data = response.data['data'];
return data.map((json) => CompanyListDto.fromJson(json)).toList();
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch companies',
code: e.response?.statusCode,
);
}
}
@override
Future<CompanyResponse> createCompany(CreateCompanyRequest request) async {
try {
final response = await _apiClient.dio.post(
ApiEndpoints.companies,
data: request.toJson(),
);
return CompanyResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to create company',
code: e.response?.statusCode,
);
}
}
@override
Future<CompanyResponse> getCompanyDetail(int id) async {
try {
final response = await _apiClient.dio.get(
'${ApiEndpoints.companies}/$id',
);
return CompanyResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch company detail',
code: e.response?.statusCode,
);
}
}
@override
Future<CompanyWithBranches> getCompanyWithBranches(int id) async {
try {
final response = await _apiClient.dio.get(
'${ApiEndpoints.companies}/$id/with-branches',
);
return CompanyWithBranches.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch company with branches',
code: e.response?.statusCode,
);
}
}
@override
Future<CompanyResponse> updateCompany(int id, UpdateCompanyRequest request) async {
try {
final response = await _apiClient.dio.put(
'${ApiEndpoints.companies}/$id',
data: request.toJson(),
);
return CompanyResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to update company',
code: e.response?.statusCode,
);
}
}
@override
Future<void> deleteCompany(int id) async {
try {
await _apiClient.dio.delete(
'${ApiEndpoints.companies}/$id',
);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to delete company',
code: e.response?.statusCode,
);
}
}
@override
Future<List<CompanyNameDto>> getCompanyNames() async {
try {
final response = await _apiClient.dio.get(
'${ApiEndpoints.companies}/names',
);
final List<dynamic> data = response.data['data'];
return data.map((json) => CompanyNameDto.fromJson(json)).toList();
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch company names',
code: e.response?.statusCode,
);
}
}
// Branch methods
@override
Future<BranchResponse> createBranch(int companyId, CreateBranchRequest request) async {
try {
final response = await _apiClient.dio.post(
'${ApiEndpoints.companies}/$companyId/branches',
data: request.toJson(),
);
return BranchResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to create branch',
code: e.response?.statusCode,
);
}
}
@override
Future<BranchResponse> getBranchDetail(int companyId, int branchId) async {
try {
final response = await _apiClient.dio.get(
'${ApiEndpoints.companies}/$companyId/branches/$branchId',
);
return BranchResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch branch detail',
code: e.response?.statusCode,
);
}
}
@override
Future<BranchResponse> updateBranch(int companyId, int branchId, UpdateBranchRequest request) async {
try {
final response = await _apiClient.dio.put(
'${ApiEndpoints.companies}/$companyId/branches/$branchId',
data: request.toJson(),
);
return BranchResponse.fromJson(response.data['data']);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to update branch',
code: e.response?.statusCode,
);
}
}
@override
Future<void> deleteBranch(int companyId, int branchId) async {
try {
await _apiClient.dio.delete(
'${ApiEndpoints.companies}/$companyId/branches/$branchId',
);
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to delete branch',
code: e.response?.statusCode,
);
}
}
@override
Future<List<BranchListDto>> getCompanyBranches(int companyId) async {
try {
final response = await _apiClient.dio.get(
'${ApiEndpoints.companies}/$companyId/branches',
);
final List<dynamic> data = response.data['data'];
return data.map((json) => BranchListDto.fromJson(json)).toList();
} on DioException catch (e) {
throw ServerException(
message: e.response?.data['message'] ?? 'Failed to fetch company branches',
code: e.response?.statusCode,
);
}
}
}

View File

@@ -0,0 +1,69 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'branch_dto.freezed.dart';
part 'branch_dto.g.dart';
@freezed
class CreateBranchRequest with _$CreateBranchRequest {
const factory CreateBranchRequest({
@JsonKey(name: 'branch_name') required String branchName,
required String address,
required String phone,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
String? remark,
}) = _CreateBranchRequest;
factory CreateBranchRequest.fromJson(Map<String, dynamic> json) =>
_$CreateBranchRequestFromJson(json);
}
@freezed
class UpdateBranchRequest with _$UpdateBranchRequest {
const factory UpdateBranchRequest({
@JsonKey(name: 'branch_name') String? branchName,
String? address,
String? phone,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
String? remark,
}) = _UpdateBranchRequest;
factory UpdateBranchRequest.fromJson(Map<String, dynamic> json) =>
_$UpdateBranchRequestFromJson(json);
}
@freezed
class BranchResponse with _$BranchResponse {
const factory BranchResponse({
required int id,
@JsonKey(name: 'company_id') required int companyId,
@JsonKey(name: 'branch_name') required String branchName,
required String address,
required String phone,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
String? remark,
@JsonKey(name: 'created_at') required DateTime createdAt,
@JsonKey(name: 'updated_at') required DateTime updatedAt,
@JsonKey(name: 'address_id') int? addressId,
}) = _BranchResponse;
factory BranchResponse.fromJson(Map<String, dynamic> json) =>
_$BranchResponseFromJson(json);
}
@freezed
class BranchListDto with _$BranchListDto {
const factory BranchListDto({
required int id,
@JsonKey(name: 'company_id') required int companyId,
@JsonKey(name: 'branch_name') required String branchName,
required String address,
required String phone,
@JsonKey(name: 'manager_name') String? managerName,
}) = _BranchListDto;
factory BranchListDto.fromJson(Map<String, dynamic> json) =>
_$BranchListDtoFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'branch_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$CreateBranchRequestImpl _$$CreateBranchRequestImplFromJson(
Map<String, dynamic> json) =>
_$CreateBranchRequestImpl(
branchName: json['branch_name'] as String,
address: json['address'] as String,
phone: json['phone'] as String,
managerName: json['manager_name'] as String?,
managerPhone: json['manager_phone'] as String?,
remark: json['remark'] as String?,
);
Map<String, dynamic> _$$CreateBranchRequestImplToJson(
_$CreateBranchRequestImpl instance) =>
<String, dynamic>{
'branch_name': instance.branchName,
'address': instance.address,
'phone': instance.phone,
'manager_name': instance.managerName,
'manager_phone': instance.managerPhone,
'remark': instance.remark,
};
_$UpdateBranchRequestImpl _$$UpdateBranchRequestImplFromJson(
Map<String, dynamic> json) =>
_$UpdateBranchRequestImpl(
branchName: json['branch_name'] as String?,
address: json['address'] as String?,
phone: json['phone'] as String?,
managerName: json['manager_name'] as String?,
managerPhone: json['manager_phone'] as String?,
remark: json['remark'] as String?,
);
Map<String, dynamic> _$$UpdateBranchRequestImplToJson(
_$UpdateBranchRequestImpl instance) =>
<String, dynamic>{
'branch_name': instance.branchName,
'address': instance.address,
'phone': instance.phone,
'manager_name': instance.managerName,
'manager_phone': instance.managerPhone,
'remark': instance.remark,
};
_$BranchResponseImpl _$$BranchResponseImplFromJson(Map<String, dynamic> json) =>
_$BranchResponseImpl(
id: (json['id'] as num).toInt(),
companyId: (json['company_id'] as num).toInt(),
branchName: json['branch_name'] as String,
address: json['address'] as String,
phone: json['phone'] as String,
managerName: json['manager_name'] as String?,
managerPhone: json['manager_phone'] as String?,
remark: json['remark'] as String?,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
addressId: (json['address_id'] as num?)?.toInt(),
);
Map<String, dynamic> _$$BranchResponseImplToJson(
_$BranchResponseImpl instance) =>
<String, dynamic>{
'id': instance.id,
'company_id': instance.companyId,
'branch_name': instance.branchName,
'address': instance.address,
'phone': instance.phone,
'manager_name': instance.managerName,
'manager_phone': instance.managerPhone,
'remark': instance.remark,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'address_id': instance.addressId,
};
_$BranchListDtoImpl _$$BranchListDtoImplFromJson(Map<String, dynamic> json) =>
_$BranchListDtoImpl(
id: (json['id'] as num).toInt(),
companyId: (json['company_id'] as num).toInt(),
branchName: json['branch_name'] as String,
address: json['address'] as String,
phone: json['phone'] as String,
managerName: json['manager_name'] as String?,
);
Map<String, dynamic> _$$BranchListDtoImplToJson(_$BranchListDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'company_id': instance.companyId,
'branch_name': instance.branchName,
'address': instance.address,
'phone': instance.phone,
'manager_name': instance.managerName,
};

View File

@@ -0,0 +1,72 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'company_dto.freezed.dart';
part 'company_dto.g.dart';
@freezed
class CreateCompanyRequest with _$CreateCompanyRequest {
const factory CreateCompanyRequest({
required String name,
required String address,
@JsonKey(name: 'contact_name') required String contactName,
@JsonKey(name: 'contact_position') required String contactPosition,
@JsonKey(name: 'contact_phone') required String contactPhone,
@JsonKey(name: 'contact_email') required String contactEmail,
@JsonKey(name: 'company_types') @Default([]) List<String> companyTypes,
String? remark,
}) = _CreateCompanyRequest;
factory CreateCompanyRequest.fromJson(Map<String, dynamic> json) =>
_$CreateCompanyRequestFromJson(json);
}
@freezed
class UpdateCompanyRequest with _$UpdateCompanyRequest {
const factory UpdateCompanyRequest({
String? name,
String? address,
@JsonKey(name: 'contact_name') String? contactName,
@JsonKey(name: 'contact_position') String? contactPosition,
@JsonKey(name: 'contact_phone') String? contactPhone,
@JsonKey(name: 'contact_email') String? contactEmail,
@JsonKey(name: 'company_types') List<String>? companyTypes,
String? remark,
@JsonKey(name: 'is_active') bool? isActive,
}) = _UpdateCompanyRequest;
factory UpdateCompanyRequest.fromJson(Map<String, dynamic> json) =>
_$UpdateCompanyRequestFromJson(json);
}
@freezed
class CompanyResponse with _$CompanyResponse {
const factory CompanyResponse({
required int id,
required String name,
required String address,
@JsonKey(name: 'contact_name') required String contactName,
@JsonKey(name: 'contact_position') required String contactPosition,
@JsonKey(name: 'contact_phone') required String contactPhone,
@JsonKey(name: 'contact_email') required String contactEmail,
@JsonKey(name: 'company_types') @Default([]) List<String> companyTypes,
String? remark,
@JsonKey(name: 'is_active') required bool isActive,
@JsonKey(name: 'created_at') required DateTime createdAt,
@JsonKey(name: 'updated_at') required DateTime updatedAt,
@JsonKey(name: 'address_id') int? addressId,
}) = _CompanyResponse;
factory CompanyResponse.fromJson(Map<String, dynamic> json) =>
_$CompanyResponseFromJson(json);
}
@freezed
class CompanyNameDto with _$CompanyNameDto {
const factory CompanyNameDto({
required int id,
required String name,
}) = _CompanyNameDto;
factory CompanyNameDto.fromJson(Map<String, dynamic> json) =>
_$CompanyNameDtoFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'company_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$CreateCompanyRequestImpl _$$CreateCompanyRequestImplFromJson(
Map<String, dynamic> json) =>
_$CreateCompanyRequestImpl(
name: json['name'] as String,
address: json['address'] as String,
contactName: json['contact_name'] as String,
contactPosition: json['contact_position'] as String,
contactPhone: json['contact_phone'] as String,
contactEmail: json['contact_email'] as String,
companyTypes: (json['company_types'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
remark: json['remark'] as String?,
);
Map<String, dynamic> _$$CreateCompanyRequestImplToJson(
_$CreateCompanyRequestImpl instance) =>
<String, dynamic>{
'name': instance.name,
'address': instance.address,
'contact_name': instance.contactName,
'contact_position': instance.contactPosition,
'contact_phone': instance.contactPhone,
'contact_email': instance.contactEmail,
'company_types': instance.companyTypes,
'remark': instance.remark,
};
_$UpdateCompanyRequestImpl _$$UpdateCompanyRequestImplFromJson(
Map<String, dynamic> json) =>
_$UpdateCompanyRequestImpl(
name: json['name'] as String?,
address: json['address'] as String?,
contactName: json['contact_name'] as String?,
contactPosition: json['contact_position'] as String?,
contactPhone: json['contact_phone'] as String?,
contactEmail: json['contact_email'] as String?,
companyTypes: (json['company_types'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
remark: json['remark'] as String?,
isActive: json['is_active'] as bool?,
);
Map<String, dynamic> _$$UpdateCompanyRequestImplToJson(
_$UpdateCompanyRequestImpl instance) =>
<String, dynamic>{
'name': instance.name,
'address': instance.address,
'contact_name': instance.contactName,
'contact_position': instance.contactPosition,
'contact_phone': instance.contactPhone,
'contact_email': instance.contactEmail,
'company_types': instance.companyTypes,
'remark': instance.remark,
'is_active': instance.isActive,
};
_$CompanyResponseImpl _$$CompanyResponseImplFromJson(
Map<String, dynamic> json) =>
_$CompanyResponseImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
address: json['address'] as String,
contactName: json['contact_name'] as String,
contactPosition: json['contact_position'] as String,
contactPhone: json['contact_phone'] as String,
contactEmail: json['contact_email'] as String,
companyTypes: (json['company_types'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
remark: json['remark'] as String?,
isActive: json['is_active'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
addressId: (json['address_id'] as num?)?.toInt(),
);
Map<String, dynamic> _$$CompanyResponseImplToJson(
_$CompanyResponseImpl instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'address': instance.address,
'contact_name': instance.contactName,
'contact_position': instance.contactPosition,
'contact_phone': instance.contactPhone,
'contact_email': instance.contactEmail,
'company_types': instance.companyTypes,
'remark': instance.remark,
'is_active': instance.isActive,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'address_id': instance.addressId,
};
_$CompanyNameDtoImpl _$$CompanyNameDtoImplFromJson(Map<String, dynamic> json) =>
_$CompanyNameDtoImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
);
Map<String, dynamic> _$$CompanyNameDtoImplToJson(
_$CompanyNameDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
};

View File

@@ -0,0 +1,33 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'company_dto.dart';
import 'branch_dto.dart';
part 'company_list_dto.freezed.dart';
part 'company_list_dto.g.dart';
@freezed
class CompanyListDto with _$CompanyListDto {
const factory CompanyListDto({
required int id,
required String name,
required String address,
@JsonKey(name: 'contact_name') required String contactName,
@JsonKey(name: 'contact_phone') required String contactPhone,
@JsonKey(name: 'is_active') required bool isActive,
@JsonKey(name: 'branch_count') @Default(0) int branchCount,
}) = _CompanyListDto;
factory CompanyListDto.fromJson(Map<String, dynamic> json) =>
_$CompanyListDtoFromJson(json);
}
@freezed
class CompanyWithBranches with _$CompanyWithBranches {
const factory CompanyWithBranches({
required CompanyResponse company,
@Default([]) List<BranchListDto> branches,
}) = _CompanyWithBranches;
factory CompanyWithBranches.fromJson(Map<String, dynamic> json) =>
_$CompanyWithBranchesFromJson(json);
}

View File

@@ -0,0 +1,499 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'company_list_dto.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
CompanyListDto _$CompanyListDtoFromJson(Map<String, dynamic> json) {
return _CompanyListDto.fromJson(json);
}
/// @nodoc
mixin _$CompanyListDto {
int get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get address => throw _privateConstructorUsedError;
@JsonKey(name: 'contact_name')
String get contactName => throw _privateConstructorUsedError;
@JsonKey(name: 'contact_phone')
String get contactPhone => throw _privateConstructorUsedError;
@JsonKey(name: 'is_active')
bool get isActive => throw _privateConstructorUsedError;
@JsonKey(name: 'branch_count')
int get branchCount => throw _privateConstructorUsedError;
/// Serializes this CompanyListDto to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of CompanyListDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CompanyListDtoCopyWith<CompanyListDto> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CompanyListDtoCopyWith<$Res> {
factory $CompanyListDtoCopyWith(
CompanyListDto value, $Res Function(CompanyListDto) then) =
_$CompanyListDtoCopyWithImpl<$Res, CompanyListDto>;
@useResult
$Res call(
{int id,
String name,
String address,
@JsonKey(name: 'contact_name') String contactName,
@JsonKey(name: 'contact_phone') String contactPhone,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'branch_count') int branchCount});
}
/// @nodoc
class _$CompanyListDtoCopyWithImpl<$Res, $Val extends CompanyListDto>
implements $CompanyListDtoCopyWith<$Res> {
_$CompanyListDtoCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CompanyListDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? name = null,
Object? address = null,
Object? contactName = null,
Object? contactPhone = null,
Object? isActive = null,
Object? branchCount = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
address: null == address
? _value.address
: address // ignore: cast_nullable_to_non_nullable
as String,
contactName: null == contactName
? _value.contactName
: contactName // ignore: cast_nullable_to_non_nullable
as String,
contactPhone: null == contactPhone
? _value.contactPhone
: contactPhone // ignore: cast_nullable_to_non_nullable
as String,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
branchCount: null == branchCount
? _value.branchCount
: branchCount // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$CompanyListDtoImplCopyWith<$Res>
implements $CompanyListDtoCopyWith<$Res> {
factory _$$CompanyListDtoImplCopyWith(_$CompanyListDtoImpl value,
$Res Function(_$CompanyListDtoImpl) then) =
__$$CompanyListDtoImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int id,
String name,
String address,
@JsonKey(name: 'contact_name') String contactName,
@JsonKey(name: 'contact_phone') String contactPhone,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'branch_count') int branchCount});
}
/// @nodoc
class __$$CompanyListDtoImplCopyWithImpl<$Res>
extends _$CompanyListDtoCopyWithImpl<$Res, _$CompanyListDtoImpl>
implements _$$CompanyListDtoImplCopyWith<$Res> {
__$$CompanyListDtoImplCopyWithImpl(
_$CompanyListDtoImpl _value, $Res Function(_$CompanyListDtoImpl) _then)
: super(_value, _then);
/// Create a copy of CompanyListDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? name = null,
Object? address = null,
Object? contactName = null,
Object? contactPhone = null,
Object? isActive = null,
Object? branchCount = null,
}) {
return _then(_$CompanyListDtoImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
address: null == address
? _value.address
: address // ignore: cast_nullable_to_non_nullable
as String,
contactName: null == contactName
? _value.contactName
: contactName // ignore: cast_nullable_to_non_nullable
as String,
contactPhone: null == contactPhone
? _value.contactPhone
: contactPhone // ignore: cast_nullable_to_non_nullable
as String,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
branchCount: null == branchCount
? _value.branchCount
: branchCount // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$CompanyListDtoImpl implements _CompanyListDto {
const _$CompanyListDtoImpl(
{required this.id,
required this.name,
required this.address,
@JsonKey(name: 'contact_name') required this.contactName,
@JsonKey(name: 'contact_phone') required this.contactPhone,
@JsonKey(name: 'is_active') required this.isActive,
@JsonKey(name: 'branch_count') this.branchCount = 0});
factory _$CompanyListDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$CompanyListDtoImplFromJson(json);
@override
final int id;
@override
final String name;
@override
final String address;
@override
@JsonKey(name: 'contact_name')
final String contactName;
@override
@JsonKey(name: 'contact_phone')
final String contactPhone;
@override
@JsonKey(name: 'is_active')
final bool isActive;
@override
@JsonKey(name: 'branch_count')
final int branchCount;
@override
String toString() {
return 'CompanyListDto(id: $id, name: $name, address: $address, contactName: $contactName, contactPhone: $contactPhone, isActive: $isActive, branchCount: $branchCount)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CompanyListDtoImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.address, address) || other.address == address) &&
(identical(other.contactName, contactName) ||
other.contactName == contactName) &&
(identical(other.contactPhone, contactPhone) ||
other.contactPhone == contactPhone) &&
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
(identical(other.branchCount, branchCount) ||
other.branchCount == branchCount));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, name, address, contactName,
contactPhone, isActive, branchCount);
/// Create a copy of CompanyListDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CompanyListDtoImplCopyWith<_$CompanyListDtoImpl> get copyWith =>
__$$CompanyListDtoImplCopyWithImpl<_$CompanyListDtoImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$CompanyListDtoImplToJson(
this,
);
}
}
abstract class _CompanyListDto implements CompanyListDto {
const factory _CompanyListDto(
{required final int id,
required final String name,
required final String address,
@JsonKey(name: 'contact_name') required final String contactName,
@JsonKey(name: 'contact_phone') required final String contactPhone,
@JsonKey(name: 'is_active') required final bool isActive,
@JsonKey(name: 'branch_count') final int branchCount}) =
_$CompanyListDtoImpl;
factory _CompanyListDto.fromJson(Map<String, dynamic> json) =
_$CompanyListDtoImpl.fromJson;
@override
int get id;
@override
String get name;
@override
String get address;
@override
@JsonKey(name: 'contact_name')
String get contactName;
@override
@JsonKey(name: 'contact_phone')
String get contactPhone;
@override
@JsonKey(name: 'is_active')
bool get isActive;
@override
@JsonKey(name: 'branch_count')
int get branchCount;
/// Create a copy of CompanyListDto
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CompanyListDtoImplCopyWith<_$CompanyListDtoImpl> get copyWith =>
throw _privateConstructorUsedError;
}
CompanyWithBranches _$CompanyWithBranchesFromJson(Map<String, dynamic> json) {
return _CompanyWithBranches.fromJson(json);
}
/// @nodoc
mixin _$CompanyWithBranches {
CompanyResponse get company => throw _privateConstructorUsedError;
List<BranchListDto> get branches => throw _privateConstructorUsedError;
/// Serializes this CompanyWithBranches to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CompanyWithBranchesCopyWith<CompanyWithBranches> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CompanyWithBranchesCopyWith<$Res> {
factory $CompanyWithBranchesCopyWith(
CompanyWithBranches value, $Res Function(CompanyWithBranches) then) =
_$CompanyWithBranchesCopyWithImpl<$Res, CompanyWithBranches>;
@useResult
$Res call({CompanyResponse company, List<BranchListDto> branches});
$CompanyResponseCopyWith<$Res> get company;
}
/// @nodoc
class _$CompanyWithBranchesCopyWithImpl<$Res, $Val extends CompanyWithBranches>
implements $CompanyWithBranchesCopyWith<$Res> {
_$CompanyWithBranchesCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? company = null,
Object? branches = null,
}) {
return _then(_value.copyWith(
company: null == company
? _value.company
: company // ignore: cast_nullable_to_non_nullable
as CompanyResponse,
branches: null == branches
? _value.branches
: branches // ignore: cast_nullable_to_non_nullable
as List<BranchListDto>,
) as $Val);
}
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$CompanyResponseCopyWith<$Res> get company {
return $CompanyResponseCopyWith<$Res>(_value.company, (value) {
return _then(_value.copyWith(company: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$CompanyWithBranchesImplCopyWith<$Res>
implements $CompanyWithBranchesCopyWith<$Res> {
factory _$$CompanyWithBranchesImplCopyWith(_$CompanyWithBranchesImpl value,
$Res Function(_$CompanyWithBranchesImpl) then) =
__$$CompanyWithBranchesImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({CompanyResponse company, List<BranchListDto> branches});
@override
$CompanyResponseCopyWith<$Res> get company;
}
/// @nodoc
class __$$CompanyWithBranchesImplCopyWithImpl<$Res>
extends _$CompanyWithBranchesCopyWithImpl<$Res, _$CompanyWithBranchesImpl>
implements _$$CompanyWithBranchesImplCopyWith<$Res> {
__$$CompanyWithBranchesImplCopyWithImpl(_$CompanyWithBranchesImpl _value,
$Res Function(_$CompanyWithBranchesImpl) _then)
: super(_value, _then);
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? company = null,
Object? branches = null,
}) {
return _then(_$CompanyWithBranchesImpl(
company: null == company
? _value.company
: company // ignore: cast_nullable_to_non_nullable
as CompanyResponse,
branches: null == branches
? _value._branches
: branches // ignore: cast_nullable_to_non_nullable
as List<BranchListDto>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$CompanyWithBranchesImpl implements _CompanyWithBranches {
const _$CompanyWithBranchesImpl(
{required this.company, final List<BranchListDto> branches = const []})
: _branches = branches;
factory _$CompanyWithBranchesImpl.fromJson(Map<String, dynamic> json) =>
_$$CompanyWithBranchesImplFromJson(json);
@override
final CompanyResponse company;
final List<BranchListDto> _branches;
@override
@JsonKey()
List<BranchListDto> get branches {
if (_branches is EqualUnmodifiableListView) return _branches;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_branches);
}
@override
String toString() {
return 'CompanyWithBranches(company: $company, branches: $branches)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CompanyWithBranchesImpl &&
(identical(other.company, company) || other.company == company) &&
const DeepCollectionEquality().equals(other._branches, _branches));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, company, const DeepCollectionEquality().hash(_branches));
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CompanyWithBranchesImplCopyWith<_$CompanyWithBranchesImpl> get copyWith =>
__$$CompanyWithBranchesImplCopyWithImpl<_$CompanyWithBranchesImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$CompanyWithBranchesImplToJson(
this,
);
}
}
abstract class _CompanyWithBranches implements CompanyWithBranches {
const factory _CompanyWithBranches(
{required final CompanyResponse company,
final List<BranchListDto> branches}) = _$CompanyWithBranchesImpl;
factory _CompanyWithBranches.fromJson(Map<String, dynamic> json) =
_$CompanyWithBranchesImpl.fromJson;
@override
CompanyResponse get company;
@override
List<BranchListDto> get branches;
/// Create a copy of CompanyWithBranches
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CompanyWithBranchesImplCopyWith<_$CompanyWithBranchesImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,48 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'company_list_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$CompanyListDtoImpl _$$CompanyListDtoImplFromJson(Map<String, dynamic> json) =>
_$CompanyListDtoImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
address: json['address'] as String,
contactName: json['contact_name'] as String,
contactPhone: json['contact_phone'] as String,
isActive: json['is_active'] as bool,
branchCount: (json['branch_count'] as num?)?.toInt() ?? 0,
);
Map<String, dynamic> _$$CompanyListDtoImplToJson(
_$CompanyListDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'address': instance.address,
'contact_name': instance.contactName,
'contact_phone': instance.contactPhone,
'is_active': instance.isActive,
'branch_count': instance.branchCount,
};
_$CompanyWithBranchesImpl _$$CompanyWithBranchesImplFromJson(
Map<String, dynamic> json) =>
_$CompanyWithBranchesImpl(
company:
CompanyResponse.fromJson(json['company'] as Map<String, dynamic>),
branches: (json['branches'] as List<dynamic>?)
?.map((e) => BranchListDto.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$CompanyWithBranchesImplToJson(
_$CompanyWithBranchesImpl instance) =>
<String, dynamic>{
'company': instance.company,
'branches': instance.branches,
};

View File

@@ -6,9 +6,11 @@ import '../data/datasources/remote/api_client.dart';
import '../data/datasources/remote/auth_remote_datasource.dart'; import '../data/datasources/remote/auth_remote_datasource.dart';
import '../data/datasources/remote/dashboard_remote_datasource.dart'; import '../data/datasources/remote/dashboard_remote_datasource.dart';
import '../data/datasources/remote/equipment_remote_datasource.dart'; import '../data/datasources/remote/equipment_remote_datasource.dart';
import '../data/datasources/remote/company_remote_datasource.dart';
import '../services/auth_service.dart'; import '../services/auth_service.dart';
import '../services/dashboard_service.dart'; import '../services/dashboard_service.dart';
import '../services/equipment_service.dart'; import '../services/equipment_service.dart';
import '../services/company_service.dart';
/// GetIt 인스턴스 /// GetIt 인스턴스
final getIt = GetIt.instance; final getIt = GetIt.instance;
@@ -35,6 +37,9 @@ Future<void> setupDependencies() async {
getIt.registerLazySingleton<EquipmentRemoteDataSource>( getIt.registerLazySingleton<EquipmentRemoteDataSource>(
() => EquipmentRemoteDataSourceImpl(), () => EquipmentRemoteDataSourceImpl(),
); );
getIt.registerLazySingleton<CompanyRemoteDataSource>(
() => CompanyRemoteDataSourceImpl(),
);
// 서비스 // 서비스
getIt.registerLazySingleton<AuthService>( getIt.registerLazySingleton<AuthService>(
@@ -46,6 +51,9 @@ Future<void> setupDependencies() async {
getIt.registerLazySingleton<EquipmentService>( getIt.registerLazySingleton<EquipmentService>(
() => EquipmentService(), () => EquipmentService(),
); );
getIt.registerLazySingleton<CompanyService>(
() => CompanyService(),
);
// 리포지토리 // 리포지토리
// TODO: Repositories will be registered here // TODO: Repositories will be registered here

View File

@@ -200,14 +200,41 @@ class _CompanyFormScreenState extends State<CompanyFormScreen> {
} }
// 회사 저장 // 회사 저장
void _saveCompany() { Future<void> _saveCompany() async {
final duplicateCompany = _controller.checkDuplicateCompany(); final duplicateCompany = _controller.checkDuplicateCompany();
if (duplicateCompany != null) { if (duplicateCompany != null) {
DuplicateCompanyDialog.show(context, duplicateCompany); DuplicateCompanyDialog.show(context, duplicateCompany);
return; return;
} }
if (_controller.saveCompany()) {
Navigator.pop(context, true); // 로딩 표시
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(
child: CircularProgressIndicator(),
),
);
try {
final success = await _controller.saveCompany();
if (mounted) {
Navigator.pop(context); // 로딩 다이얼로그 닫기
if (success) {
Navigator.pop(context, true);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('회사 저장에 실패했습니다.')),
);
}
}
} catch (e) {
if (mounted) {
Navigator.pop(context); // 로딩 다이얼로그 닫기
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('오류가 발생했습니다: $e')),
);
}
} }
} }

View File

@@ -533,9 +533,8 @@ class EquipmentOutFormController extends ChangeNotifier {
} else { } else {
onError('출고 정보를 찾을 수 없습니다'); onError('출고 정보를 찾을 수 없습니다');
} }
} } else {
} else { if (selectedEquipments != null && selectedEquipments!.isNotEmpty) {
if (selectedEquipments != null && selectedEquipments!.isNotEmpty) {
// 여러 회사에 각각 출고 처리 // 여러 회사에 각각 출고 처리
List<String> successCompanies = []; List<String> successCompanies = [];
@@ -591,7 +590,7 @@ class EquipmentOutFormController extends ChangeNotifier {
} else { } else {
onSuccess('${successCompanies.join(", ")} 회사로 다중 장비 출고 처리 완료'); onSuccess('${successCompanies.join(", ")} 회사로 다중 장비 출고 처리 완료');
} }
} else if (selectedEquipmentInId != null) { } else if (selectedEquipmentInId != null) {
final equipment = Equipment( final equipment = Equipment(
manufacturer: manufacturer, manufacturer: manufacturer,
name: name, name: name,
@@ -722,7 +721,6 @@ class EquipmentOutFormController extends ChangeNotifier {
onSuccess('${successCompanies.join(", ")} 회사로 새 출고 장비 추가 완료'); onSuccess('${successCompanies.join(", ")} 회사로 새 출고 장비 추가 완료');
} }
} }
}
} on Failure catch (e) { } on Failure catch (e) {
_error = e.message; _error = e.message;
onError(e.message); onError(e.message);

View File

@@ -0,0 +1,291 @@
import 'package:get_it/get_it.dart';
import 'package:superport/core/errors/exceptions.dart';
import 'package:superport/core/errors/failures.dart';
import 'package:superport/data/datasources/remote/company_remote_datasource.dart';
import 'package:superport/data/models/company/company_dto.dart';
import 'package:superport/data/models/company/company_list_dto.dart';
import 'package:superport/data/models/company/branch_dto.dart';
import 'package:superport/models/company_model.dart';
import 'package:superport/models/address_model.dart';
class CompanyService {
final CompanyRemoteDataSource _remoteDataSource = GetIt.instance<CompanyRemoteDataSource>();
// 회사 목록 조회
Future<List<Company>> getCompanies({
int page = 1,
int perPage = 20,
String? search,
bool? isActive,
}) async {
try {
final dtoList = await _remoteDataSource.getCompanies(
page: page,
perPage: perPage,
search: search,
isActive: isActive,
);
return dtoList.map((dto) => _convertListDtoToCompany(dto)).toList();
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch company list: $e');
}
}
// 회사 생성
Future<Company> createCompany(Company company) async {
try {
final request = CreateCompanyRequest(
name: company.name,
address: company.address.toString(),
contactName: company.contactName ?? '',
contactPosition: company.contactPosition ?? '',
contactPhone: company.contactPhone ?? '',
contactEmail: company.contactEmail ?? '',
companyTypes: company.companyTypes.map((e) => e.toString().split('.').last).toList(),
remark: company.remark,
);
final response = await _remoteDataSource.createCompany(request);
return _convertResponseToCompany(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to create company: $e');
}
}
// 회사 상세 조회
Future<Company> getCompanyDetail(int id) async {
try {
final response = await _remoteDataSource.getCompanyDetail(id);
return _convertResponseToCompany(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch company detail: $e');
}
}
// 회사와 지점 정보 함께 조회
Future<Company> getCompanyWithBranches(int id) async {
try {
final response = await _remoteDataSource.getCompanyWithBranches(id);
final company = _convertResponseToCompany(response.company);
final branches = response.branches.map((dto) => _convertBranchDtoToBranch(dto)).toList();
return company.copyWith(branches: branches);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch company with branches: $e');
}
}
// 회사 수정
Future<Company> updateCompany(int id, Company company) async {
try {
final request = UpdateCompanyRequest(
name: company.name,
address: company.address.toString(),
contactName: company.contactName,
contactPosition: company.contactPosition,
contactPhone: company.contactPhone,
contactEmail: company.contactEmail,
companyTypes: company.companyTypes.map((e) => e.toString().split('.').last).toList(),
remark: company.remark,
);
final response = await _remoteDataSource.updateCompany(id, request);
return _convertResponseToCompany(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to update company: $e');
}
}
// 회사 삭제
Future<void> deleteCompany(int id) async {
try {
await _remoteDataSource.deleteCompany(id);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to delete company: $e');
}
}
// 회사명 목록 조회 (드롭다운용)
Future<List<Map<String, dynamic>>> getCompanyNames() async {
try {
final dtoList = await _remoteDataSource.getCompanyNames();
return dtoList.map((dto) => {
'id': dto.id,
'name': dto.name,
}).toList();
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch company names: $e');
}
}
// 지점 관련 메서드들
Future<Branch> createBranch(int companyId, Branch branch) async {
try {
final request = CreateBranchRequest(
branchName: branch.name,
address: branch.address.toString(),
phone: branch.contactPhone ?? '',
managerName: branch.contactName,
managerPhone: branch.contactPhone,
remark: branch.remark,
);
final response = await _remoteDataSource.createBranch(companyId, request);
return _convertBranchResponseToBranch(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to create branch: $e');
}
}
Future<Branch> getBranchDetail(int companyId, int branchId) async {
try {
final response = await _remoteDataSource.getBranchDetail(companyId, branchId);
return _convertBranchResponseToBranch(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch branch detail: $e');
}
}
Future<Branch> updateBranch(int companyId, int branchId, Branch branch) async {
try {
final request = UpdateBranchRequest(
branchName: branch.name,
address: branch.address.toString(),
phone: branch.contactPhone,
managerName: branch.contactName,
managerPhone: branch.contactPhone,
remark: branch.remark,
);
final response = await _remoteDataSource.updateBranch(companyId, branchId, request);
return _convertBranchResponseToBranch(response);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to update branch: $e');
}
}
Future<void> deleteBranch(int companyId, int branchId) async {
try {
await _remoteDataSource.deleteBranch(companyId, branchId);
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to delete branch: $e');
}
}
Future<List<Branch>> getCompanyBranches(int companyId) async {
try {
final dtoList = await _remoteDataSource.getCompanyBranches(companyId);
return dtoList.map((dto) => _convertBranchDtoToBranch(dto)).toList();
} on ServerException catch (e) {
throw Failure(message: e.message);
} catch (e) {
throw Failure(message: 'Failed to fetch company branches: $e');
}
}
// 변환 헬퍼 메서드들
Company _convertListDtoToCompany(CompanyListDto dto) {
return Company(
id: dto.id,
name: dto.name,
address: Address.fromFullAddress(dto.address),
contactName: dto.contactName,
contactPhone: dto.contactPhone,
companyTypes: [CompanyType.customer], // 기본값, 실제로는 API에서 받아와야 함
);
}
Company _convertResponseToCompany(CompanyResponse dto) {
final companyTypes = dto.companyTypes.map((typeStr) {
if (typeStr.contains('partner')) return CompanyType.partner;
return CompanyType.customer;
}).toList();
return Company(
id: dto.id,
name: dto.name,
address: Address.fromFullAddress(dto.address),
contactName: dto.contactName,
contactPosition: dto.contactPosition,
contactPhone: dto.contactPhone,
contactEmail: dto.contactEmail,
companyTypes: companyTypes.isEmpty ? [CompanyType.customer] : companyTypes,
remark: dto.remark,
);
}
Branch _convertBranchDtoToBranch(BranchListDto dto) {
return Branch(
id: dto.id,
companyId: dto.companyId,
name: dto.branchName,
address: Address.fromFullAddress(dto.address),
contactName: dto.managerName,
contactPhone: dto.phone,
);
}
Branch _convertBranchResponseToBranch(BranchResponse dto) {
return Branch(
id: dto.id,
companyId: dto.companyId,
name: dto.branchName,
address: Address.fromFullAddress(dto.address),
contactName: dto.managerName,
contactPhone: dto.phone,
remark: dto.remark,
);
}
}
// Company 모델에 copyWith 메서드가 없다면 extension으로 추가
extension CompanyExtension on Company {
Company copyWith({
int? id,
String? name,
Address? address,
String? contactName,
String? contactPosition,
String? contactPhone,
String? contactEmail,
List<Branch>? branches,
List<CompanyType>? companyTypes,
String? remark,
}) {
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,
);
}
}