feat: Equipment DTO 호환성 수정 전 백업 커밋
- Equipment DTO 필드명 변경 (name → equipment_number 등) 완료 - Phase 1-7 파생 수정사항 체계적 진행 예정 - 통합 모델 정리, Controller 동기화, UI 업데이트 예정 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:superport/utils/constants.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/core/utils/debug_logger.dart';
|
||||
import 'package:superport/core/utils/equipment_status_converter.dart';
|
||||
import 'package:superport/core/services/lookups_service.dart';
|
||||
|
||||
/// 장비 입고 폼 컨트롤러
|
||||
///
|
||||
@@ -16,6 +17,7 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
final EquipmentService _equipmentService = GetIt.instance<EquipmentService>();
|
||||
final WarehouseService _warehouseService = GetIt.instance<WarehouseService>();
|
||||
final CompanyService _companyService = GetIt.instance<CompanyService>();
|
||||
final LookupsService _lookupsService = GetIt.instance<LookupsService>();
|
||||
final int? equipmentInId; // 실제로는 장비 ID (입고 ID가 아님)
|
||||
int? actualEquipmentId; // API 호출용 실제 장비 ID
|
||||
|
||||
@@ -28,37 +30,114 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
bool get isSaving => _isSaving;
|
||||
|
||||
// 저장 가능 상태를 별도 변수로 관리 (성능 개선 및 UI 동기화)
|
||||
bool _canSave = false;
|
||||
bool get canSave => _canSave;
|
||||
|
||||
/// canSave 상태 업데이트 (UI 렌더링 문제 해결)
|
||||
void _updateCanSave() {
|
||||
final hasEquipmentNumber = _equipmentNumber.trim().isNotEmpty;
|
||||
final hasManufacturer = _manufacturer.trim().isNotEmpty;
|
||||
final isNotSaving = !_isSaving;
|
||||
|
||||
final newCanSave = isNotSaving && hasEquipmentNumber && hasManufacturer;
|
||||
|
||||
if (_canSave != newCanSave) {
|
||||
_canSave = newCanSave;
|
||||
print('🚀 [canSave 상태 변경] $_canSave → equipmentNumber: "$_equipmentNumber", manufacturer: "$_manufacturer"');
|
||||
notifyListeners(); // 명시적 UI 업데이트
|
||||
}
|
||||
}
|
||||
|
||||
// 폼 키
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
// 입력 상태 변수
|
||||
String manufacturer = '';
|
||||
String name = '';
|
||||
String category = '';
|
||||
String subCategory = '';
|
||||
String subSubCategory = '';
|
||||
String serialNumber = '';
|
||||
String barcode = '';
|
||||
int quantity = 1;
|
||||
DateTime inDate = DateTime.now();
|
||||
String equipmentType = EquipmentType.new_;
|
||||
bool hasSerialNumber = true;
|
||||
// 입력 상태 변수 (백엔드 API 구조에 맞게 수정)
|
||||
String _equipmentNumber = ''; // 장비번호 (필수) - private으로 변경
|
||||
String _manufacturer = ''; // 제조사 (필수) - private으로 변경
|
||||
String _modelName = ''; // 모델명 - private으로 변경
|
||||
String _serialNumber = ''; // 시리얼번호 - private으로 변경
|
||||
String _category1 = ''; // 대분류 - private으로 변경
|
||||
String _category2 = ''; // 중분류 - private으로 변경
|
||||
String _category3 = ''; // 소분류 - private으로 변경
|
||||
|
||||
// 워런티 관련 상태
|
||||
String? warrantyLicense;
|
||||
String? warrantyCode; // 워런티 코드(텍스트 입력)
|
||||
DateTime warrantyStartDate = DateTime.now();
|
||||
DateTime warrantyEndDate = DateTime.now().add(const Duration(days: 365));
|
||||
List<String> warrantyLicenses = [];
|
||||
// Getters and Setters for reactive fields
|
||||
String get equipmentNumber => _equipmentNumber;
|
||||
set equipmentNumber(String value) {
|
||||
if (_equipmentNumber != value) {
|
||||
_equipmentNumber = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
print('DEBUG [Controller] equipmentNumber updated: "$_equipmentNumber"');
|
||||
}
|
||||
}
|
||||
|
||||
// 자동완성 데이터
|
||||
String get serialNumber => _serialNumber;
|
||||
set serialNumber(String value) {
|
||||
if (_serialNumber != value) {
|
||||
_serialNumber = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
print('DEBUG [Controller] serialNumber updated: "$_serialNumber"');
|
||||
}
|
||||
}
|
||||
|
||||
String get manufacturer => _manufacturer;
|
||||
set manufacturer(String value) {
|
||||
if (_manufacturer != value) {
|
||||
_manufacturer = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
print('DEBUG [Controller] manufacturer updated: "$_manufacturer"');
|
||||
}
|
||||
}
|
||||
|
||||
String get modelName => _modelName;
|
||||
set modelName(String value) {
|
||||
if (_modelName != value) {
|
||||
_modelName = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
print('DEBUG [Controller] modelName updated: "$_modelName"');
|
||||
}
|
||||
}
|
||||
|
||||
String get category1 => _category1;
|
||||
set category1(String value) {
|
||||
if (_category1 != value) {
|
||||
_category1 = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
}
|
||||
}
|
||||
|
||||
String get category2 => _category2;
|
||||
set category2(String value) {
|
||||
if (_category2 != value) {
|
||||
_category2 = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
}
|
||||
}
|
||||
|
||||
String get category3 => _category3;
|
||||
set category3(String value) {
|
||||
if (_category3 != value) {
|
||||
_category3 = value;
|
||||
_updateCanSave(); // canSave 상태 업데이트
|
||||
}
|
||||
}
|
||||
DateTime? purchaseDate; // 구매일
|
||||
double? purchasePrice; // 구매가격
|
||||
|
||||
// 삭제된 필드들 (백엔드 미지원)
|
||||
// barcode, quantity, inDate, equipmentType, hasSerialNumber
|
||||
// warranty 관련 모든 필드들
|
||||
|
||||
// Lookups API 데이터 (매번 API 호출)
|
||||
List<String> manufacturers = [];
|
||||
List<String> equipmentNames = [];
|
||||
// 카테고리 자동완성 데이터
|
||||
List<String> categories = [];
|
||||
List<String> subCategories = [];
|
||||
List<String> subSubCategories = [];
|
||||
Map<int, String> companies = {};
|
||||
Map<int, String> warehouses = {};
|
||||
|
||||
// 선택된 ID 값들
|
||||
int? selectedCompanyId; // 구매처 ID
|
||||
int? selectedWarehouseId; // 입고지 ID
|
||||
|
||||
// 창고 위치 전체 데이터 (이름-ID 매핑용)
|
||||
Map<String, int> warehouseLocationMap = {};
|
||||
@@ -66,33 +145,41 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
// 편집 모드 여부
|
||||
bool isEditMode = false;
|
||||
|
||||
// 수정불가 필드 목록 (수정 모드에서만 적용)
|
||||
static const List<String> _readOnlyFields = [
|
||||
'equipmentNumber', // 장비번호
|
||||
'manufacturer', // 제조사
|
||||
'modelName', // 모델명
|
||||
'serialNumber', // 시리얼번호
|
||||
'purchaseDate', // 구매일
|
||||
'purchasePrice', // 구매가격
|
||||
];
|
||||
|
||||
/// 특정 필드가 수정불가인지 확인
|
||||
bool isFieldReadOnly(String fieldName) {
|
||||
return isEditMode && _readOnlyFields.contains(fieldName);
|
||||
}
|
||||
|
||||
// 입고지, 파트너사 관련 상태
|
||||
String? warehouseLocation;
|
||||
String? partnerCompany;
|
||||
List<String> warehouseLocations = [];
|
||||
List<String> partnerCompanies = [];
|
||||
|
||||
// 새로운 필드들 (백엔드 API 구조 변경 대응)
|
||||
double? purchasePrice; // 구매 가격
|
||||
int? currentCompanyId; // 현재 회사 ID
|
||||
int? warehouseLocationId; // 창고 위치 ID
|
||||
int? currentBranchId; // 현재 지점 ID (Deprecated)
|
||||
DateTime? lastInspectionDate; // 최근 점검일
|
||||
DateTime? nextInspectionDate; // 다음 점검일
|
||||
String? equipmentStatus; // 장비 상태
|
||||
// 기존 필드들은 위로 이동, 삭제된 필드들
|
||||
// currentCompanyId, currentBranchId, lastInspectionDate, nextInspectionDate, equipmentStatus는 입고 시 불필요
|
||||
|
||||
// 워런티 관련 필드들 (필요 시 사용)
|
||||
String? warrantyLicense;
|
||||
DateTime warrantyStartDate = DateTime.now();
|
||||
DateTime warrantyEndDate = DateTime.now().add(const Duration(days: 365));
|
||||
|
||||
final TextEditingController remarkController = TextEditingController();
|
||||
|
||||
EquipmentInFormController({this.equipmentInId}) {
|
||||
isEditMode = equipmentInId != null;
|
||||
_loadManufacturers();
|
||||
_loadEquipmentNames();
|
||||
_loadCategories();
|
||||
_loadSubCategories();
|
||||
_loadSubSubCategories();
|
||||
_loadWarehouseLocations();
|
||||
_loadPartnerCompanies();
|
||||
_loadWarrantyLicenses();
|
||||
_loadDropdownData();
|
||||
_updateCanSave(); // 초기 canSave 상태 설정
|
||||
// 수정 모드일 때 초기 데이터 로드는 initializeForEdit() 메서드로 이동
|
||||
}
|
||||
|
||||
@@ -102,79 +189,50 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
await _loadEquipmentIn();
|
||||
}
|
||||
|
||||
// 자동완성 데이터는 API를 통해 로드해야 하지만, 현재는 빈 목록으로 설정
|
||||
void _loadManufacturers() {
|
||||
// TODO: API를 통해 제조사 목록 로드
|
||||
manufacturers = [];
|
||||
}
|
||||
|
||||
void _loadEquipmentNames() {
|
||||
// TODO: API를 통해 장비명 목록 로드
|
||||
equipmentNames = [];
|
||||
}
|
||||
|
||||
void _loadCategories() {
|
||||
// TODO: API를 통해 카테고리 목록 로드
|
||||
categories = [];
|
||||
}
|
||||
|
||||
void _loadSubCategories() {
|
||||
// TODO: API를 통해 서브카테고리 목록 로드
|
||||
subCategories = [];
|
||||
}
|
||||
|
||||
void _loadSubSubCategories() {
|
||||
// TODO: API를 통해 서브서브카테고리 목록 로드
|
||||
subSubCategories = [];
|
||||
}
|
||||
|
||||
// 입고지 목록 로드
|
||||
void _loadWarehouseLocations() async {
|
||||
// 드롭다운 데이터 로드 (매번 API 호출)
|
||||
void _loadDropdownData() async {
|
||||
try {
|
||||
DebugLogger.log('입고지 목록 API 로드 시작', tag: 'EQUIPMENT_IN');
|
||||
final response = await _warehouseService.getWarehouseLocations();
|
||||
warehouseLocations = response.items.map((e) => e.name).toList();
|
||||
// 이름-ID 매핑 저장
|
||||
warehouseLocationMap = {for (var loc in response.items) loc.name: loc.id};
|
||||
DebugLogger.log('입고지 목록 로드 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'count': warehouseLocations.length,
|
||||
'locations': warehouseLocations,
|
||||
'locationMap': warehouseLocationMap,
|
||||
});
|
||||
notifyListeners();
|
||||
DebugLogger.log('Equipment 폼 드롭다운 데이터 로드 시작', tag: 'EQUIPMENT_IN');
|
||||
final result = await _lookupsService.getEquipmentFormDropdownData();
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
DebugLogger.logError('드롭다운 데이터 로드 실패', error: failure.message);
|
||||
// API 실패 시 빈 데이터
|
||||
manufacturers = [];
|
||||
equipmentNames = [];
|
||||
companies = {};
|
||||
warehouses = {};
|
||||
notifyListeners();
|
||||
},
|
||||
(data) {
|
||||
manufacturers = data['manufacturers'] as List<String>;
|
||||
equipmentNames = data['equipment_names'] as List<String>;
|
||||
companies = data['companies'] as Map<int, String>;
|
||||
warehouses = data['warehouses'] as Map<int, String>;
|
||||
|
||||
DebugLogger.log('드롭다운 데이터 로드 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'manufacturers_count': manufacturers.length,
|
||||
'equipment_names_count': equipmentNames.length,
|
||||
'companies_count': companies.length,
|
||||
'warehouses_count': warehouses.length,
|
||||
});
|
||||
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
DebugLogger.logError('입고지 목록 로드 실패', error: e);
|
||||
// API 실패 시 빈 목록
|
||||
warehouseLocations = [];
|
||||
warehouseLocationMap = {};
|
||||
DebugLogger.logError('드롭다운 데이터 로드 예외', error: e);
|
||||
manufacturers = [];
|
||||
equipmentNames = [];
|
||||
companies = {};
|
||||
warehouses = {};
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 파트너사 목록 로드
|
||||
void _loadPartnerCompanies() async {
|
||||
try {
|
||||
DebugLogger.log('파트너사 목록 API 로드 시작', tag: 'EQUIPMENT_IN');
|
||||
final response = await _companyService.getCompanies();
|
||||
partnerCompanies = response.items.map((c) => c.name).toList();
|
||||
DebugLogger.log('파트너사 목록 로드 성공', tag: 'EQUIPMENT_IN', data: {
|
||||
'count': partnerCompanies.length,
|
||||
'companies': partnerCompanies,
|
||||
});
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
DebugLogger.logError('파트너사 목록 로드 실패', error: e);
|
||||
// API 실패 시 빈 목록
|
||||
partnerCompanies = [];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 워런티 라이센스 목록 로드
|
||||
void _loadWarrantyLicenses() {
|
||||
// 실제로는 API나 서비스에서 불러와야 하지만, 파트너사와 동일한 데이터 사용
|
||||
warrantyLicenses = List.from(partnerCompanies);
|
||||
}
|
||||
// 기존의 개별 로드 메서드들은 _loadDropdownData()로 통합됨
|
||||
// warehouseLocations, partnerCompanies 리스트 변수들도 제거됨
|
||||
|
||||
// 기존 데이터 로드(수정 모드)
|
||||
Future<void> _loadEquipmentIn() async {
|
||||
@@ -205,60 +263,43 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
'name': equipment.name,
|
||||
});
|
||||
|
||||
// 장비 정보 설정
|
||||
// 장비 정보 설정 (새로운 필드 구조)
|
||||
print('DEBUG [_loadEquipmentIn] Setting equipment data...');
|
||||
print('DEBUG [_loadEquipmentIn] equipment.manufacturer="${equipment.manufacturer}"');
|
||||
print('DEBUG [_loadEquipmentIn] equipment.name="${equipment.name}"');
|
||||
print('DEBUG [_loadEquipmentIn] equipment.equipmentNumber="${equipment.equipmentNumber}"');
|
||||
|
||||
manufacturer = equipment.manufacturer;
|
||||
name = equipment.name;
|
||||
category = equipment.category;
|
||||
subCategory = equipment.subCategory;
|
||||
subSubCategory = equipment.subSubCategory;
|
||||
serialNumber = equipment.serialNumber ?? '';
|
||||
barcode = equipment.barcode ?? '';
|
||||
quantity = equipment.quantity;
|
||||
// 새로운 필드 구조로 매핑 (setter 사용하여 notifyListeners 자동 호출)
|
||||
_equipmentNumber = equipment.equipmentNumber ?? '';
|
||||
manufacturer = equipment.manufacturer ?? '';
|
||||
modelName = equipment.modelName ?? equipment.name ?? '';
|
||||
_serialNumber = equipment.serialNumber ?? '';
|
||||
category1 = equipment.category1 ?? equipment.category ?? '';
|
||||
category2 = equipment.category2 ?? equipment.subCategory ?? '';
|
||||
category3 = equipment.category3 ?? equipment.subSubCategory ?? '';
|
||||
purchaseDate = equipment.purchaseDate;
|
||||
purchasePrice = equipment.purchasePrice;
|
||||
selectedCompanyId = equipment.companyId;
|
||||
selectedWarehouseId = equipment.warehouseLocationId;
|
||||
remarkController.text = equipment.remark ?? '';
|
||||
hasSerialNumber = serialNumber.isNotEmpty;
|
||||
|
||||
print('DEBUG [_loadEquipmentIn] After setting - manufacturer="$manufacturer", name="$name"');
|
||||
print('DEBUG [_loadEquipmentIn] After setting - equipmentNumber="$_equipmentNumber", manufacturer="$_manufacturer", modelName="$_modelName"');
|
||||
// 🔧 [DEBUG] UI 업데이트를 위한 중요 필드들 로깅
|
||||
print('DEBUG [_loadEquipmentIn] purchaseDate: $purchaseDate, purchasePrice: $purchasePrice');
|
||||
print('DEBUG [_loadEquipmentIn] selectedCompanyId: $selectedCompanyId, selectedWarehouseId: $selectedWarehouseId');
|
||||
|
||||
DebugLogger.log('장비 데이터 설정 완료', tag: 'EQUIPMENT_IN', data: {
|
||||
'manufacturer': manufacturer,
|
||||
'name': name,
|
||||
'category': category,
|
||||
'subCategory': subCategory,
|
||||
'subSubCategory': subSubCategory,
|
||||
'serialNumber': serialNumber,
|
||||
'quantity': quantity,
|
||||
'equipmentNumber': _equipmentNumber,
|
||||
'manufacturer': _manufacturer,
|
||||
'modelName': _modelName,
|
||||
'category1': _category1,
|
||||
'category2': _category2,
|
||||
'category3': _category3,
|
||||
'serialNumber': _serialNumber,
|
||||
});
|
||||
|
||||
print('DEBUG [EQUIPMENT_IN]: Equipment loaded - manufacturer: "$manufacturer", name: "$name", category: "$category"');
|
||||
print('DEBUG [EQUIPMENT_IN]: Equipment loaded - equipmentNumber: "$_equipmentNumber", manufacturer: "$_manufacturer", modelName: "$_modelName"');
|
||||
|
||||
// 워런티 정보
|
||||
warrantyLicense = equipment.warrantyLicense;
|
||||
warrantyStartDate = equipment.warrantyStartDate ?? DateTime.now();
|
||||
warrantyEndDate = equipment.warrantyEndDate ?? DateTime.now().add(const Duration(days: 365));
|
||||
|
||||
// 새로운 필드들 설정 (백엔드 API에서 제공되면 사용, 아니면 기본값)
|
||||
currentCompanyId = equipment.currentCompanyId;
|
||||
currentBranchId = equipment.currentBranchId;
|
||||
lastInspectionDate = equipment.lastInspectionDate;
|
||||
nextInspectionDate = equipment.nextInspectionDate;
|
||||
// 유효한 장비 상태 목록 (클라이언트 형식으로 변환)
|
||||
const validServerStatuses = ['available', 'inuse', 'maintenance', 'disposed'];
|
||||
if (equipment.equipmentStatus != null && validServerStatuses.contains(equipment.equipmentStatus)) {
|
||||
// 서버 상태를 클라이언트 상태로 변환하여 저장
|
||||
equipmentStatus = EquipmentStatusConverter.serverToClient(equipment.equipmentStatus);
|
||||
} else {
|
||||
// 기본값: 입고 상태 (클라이언트 형식)
|
||||
equipmentStatus = 'I'; // 입고
|
||||
}
|
||||
|
||||
// 입고 관련 정보는 현재 API에서 제공하지 않으므로 기본값 사용
|
||||
inDate = equipment.inDate ?? DateTime.now();
|
||||
equipmentType = EquipmentType.new_;
|
||||
// 창고 위치와 파트너사는 사용자가 수정 시 입력
|
||||
// 추가 필드들은 입고 시에는 필요하지 않으므로 생략
|
||||
|
||||
} catch (e, stackTrace) {
|
||||
print('DEBUG [_loadEquipmentIn] Error loading equipment: $e');
|
||||
@@ -271,6 +312,7 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
DebugLogger.logError('장비 로드 실패', error: e);
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
_updateCanSave(); // 데이터 로드 완료 시 canSave 상태 업데이트
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -308,66 +350,43 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
|
||||
_isSaving = true;
|
||||
_error = null;
|
||||
_updateCanSave(); // 저장 시작 시 canSave 상태 업데이트
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
|
||||
// 입력값이 리스트에 없으면 추가
|
||||
if (partnerCompany != null &&
|
||||
partnerCompany!.isNotEmpty &&
|
||||
!partnerCompanies.contains(partnerCompany)) {
|
||||
partnerCompanies.add(partnerCompany!);
|
||||
}
|
||||
if (warehouseLocation != null &&
|
||||
warehouseLocation!.isNotEmpty &&
|
||||
!warehouseLocations.contains(warehouseLocation)) {
|
||||
warehouseLocations.add(warehouseLocation!);
|
||||
}
|
||||
if (manufacturer.isNotEmpty && !manufacturers.contains(manufacturer)) {
|
||||
manufacturers.add(manufacturer);
|
||||
}
|
||||
if (name.isNotEmpty && !equipmentNames.contains(name)) {
|
||||
equipmentNames.add(name);
|
||||
}
|
||||
if (category.isNotEmpty && !categories.contains(category)) {
|
||||
categories.add(category);
|
||||
}
|
||||
if (subCategory.isNotEmpty && !subCategories.contains(subCategory)) {
|
||||
subCategories.add(subCategory);
|
||||
}
|
||||
if (subSubCategory.isNotEmpty &&
|
||||
!subSubCategories.contains(subSubCategory)) {
|
||||
subSubCategories.add(subSubCategory);
|
||||
}
|
||||
if (warrantyLicense != null &&
|
||||
warrantyLicense!.isNotEmpty &&
|
||||
!warrantyLicenses.contains(warrantyLicense)) {
|
||||
warrantyLicenses.add(warrantyLicense!);
|
||||
}
|
||||
// 새로운 입력값을 로컬 리스트에 추가 (사용자 경험 향상용)
|
||||
if (_manufacturer.isNotEmpty && !manufacturers.contains(_manufacturer)) {
|
||||
manufacturers.add(_manufacturer);
|
||||
}
|
||||
if (_modelName.isNotEmpty && !equipmentNames.contains(_modelName)) {
|
||||
equipmentNames.add(_modelName);
|
||||
}
|
||||
|
||||
// 백엔드 API 구조에 맞는 Equipment 객체 생성
|
||||
final equipment = Equipment(
|
||||
manufacturer: manufacturer,
|
||||
name: name,
|
||||
category: category,
|
||||
subCategory: subCategory,
|
||||
subSubCategory: subSubCategory,
|
||||
serialNumber: hasSerialNumber ? serialNumber : null,
|
||||
barcode: barcode.isNotEmpty ? barcode : null,
|
||||
quantity: quantity,
|
||||
inDate: inDate, // 구매일 매핑
|
||||
remark: remarkController.text.trim().isEmpty ? null : remarkController.text.trim(),
|
||||
warrantyLicense: warrantyLicense,
|
||||
// 백엔드 API 새로운 필드들 매핑
|
||||
// 새로운 필드들 (백엔드 API 기준)
|
||||
equipmentNumber: _equipmentNumber.trim(),
|
||||
manufacturer: _manufacturer.trim(),
|
||||
modelName: _modelName.trim().isEmpty ? null : _modelName.trim(),
|
||||
serialNumber: _serialNumber.trim().isEmpty ? null : _serialNumber.trim(),
|
||||
category1: _category1.trim().isEmpty ? null : _category1.trim(),
|
||||
category2: _category2.trim().isEmpty ? null : _category2.trim(),
|
||||
category3: _category3.trim().isEmpty ? null : _category3.trim(),
|
||||
purchaseDate: purchaseDate,
|
||||
purchasePrice: purchasePrice,
|
||||
currentCompanyId: currentCompanyId,
|
||||
warehouseLocationId: warehouseLocationId,
|
||||
currentBranchId: currentBranchId, // Deprecated but kept for compatibility
|
||||
lastInspectionDate: lastInspectionDate,
|
||||
nextInspectionDate: nextInspectionDate,
|
||||
equipmentStatus: equipmentStatus, // 클라이언트 형식 ('I', 'O' 등)
|
||||
warrantyStartDate: warrantyStartDate,
|
||||
warrantyEndDate: warrantyEndDate,
|
||||
// 워런티 코드 저장 필요시 여기에 추가
|
||||
remark: remarkController.text.trim().isEmpty ? null : remarkController.text.trim(),
|
||||
companyId: selectedCompanyId, // 구매처 ID
|
||||
warehouseLocationId: selectedWarehouseId, // 입고지 ID
|
||||
|
||||
// 기존 Equipment 모델과의 호환성을 위한 매핑
|
||||
name: _modelName.trim().isEmpty ? _equipmentNumber.trim() : _modelName.trim(),
|
||||
category: _category1.trim(),
|
||||
subCategory: _category2.trim(),
|
||||
subSubCategory: _category3.trim(),
|
||||
barcode: '', // 사용하지 않음
|
||||
quantity: 1, // 기본값
|
||||
inDate: purchaseDate ?? DateTime.now(),
|
||||
);
|
||||
|
||||
// API 호출
|
||||
@@ -379,8 +398,9 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
|
||||
DebugLogger.log('장비 정보 업데이트 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'equipmentId': actualEquipmentId,
|
||||
'equipmentNumber': equipment.equipmentNumber,
|
||||
'manufacturer': equipment.manufacturer,
|
||||
'name': equipment.name,
|
||||
'modelName': equipment.modelName,
|
||||
'serialNumber': equipment.serialNumber,
|
||||
});
|
||||
|
||||
@@ -391,10 +411,11 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
// 생성 모드
|
||||
try {
|
||||
// 1. 먼저 장비 생성
|
||||
DebugLogger.log('장비 생성 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'manufacturer': manufacturer,
|
||||
'name': name,
|
||||
'serialNumber': serialNumber,
|
||||
DebugLogger.log('장비 입고 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'equipmentNumber': _equipmentNumber,
|
||||
'manufacturer': _manufacturer,
|
||||
'modelName': _modelName,
|
||||
'serialNumber': _serialNumber,
|
||||
});
|
||||
|
||||
final createdEquipment = await _equipmentService.createEquipment(equipment);
|
||||
@@ -403,29 +424,7 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
'equipmentId': createdEquipment.id,
|
||||
});
|
||||
|
||||
// 2. 입고 처리 (warehouse location ID 필요)
|
||||
int? warehouseLocationId;
|
||||
if (warehouseLocation != null) {
|
||||
// 저장된 매핑에서 ID 가져오기
|
||||
warehouseLocationId = warehouseLocationMap[warehouseLocation];
|
||||
|
||||
if (warehouseLocationId == null) {
|
||||
DebugLogger.logError('창고 위치 ID를 찾을 수 없음', error: 'Warehouse: $warehouseLocation');
|
||||
}
|
||||
}
|
||||
|
||||
DebugLogger.log('입고 처리 시작', tag: 'EQUIPMENT_IN', data: {
|
||||
'equipmentId': createdEquipment.id,
|
||||
'quantity': quantity,
|
||||
'warehouseLocationId': warehouseLocationId,
|
||||
});
|
||||
|
||||
await _equipmentService.equipmentIn(
|
||||
equipmentId: createdEquipment.id!,
|
||||
quantity: quantity,
|
||||
warehouseLocationId: warehouseLocationId,
|
||||
notes: remarkController.text.trim(),
|
||||
);
|
||||
// 새로운 API에서는 장비 생성 시 입고 처리까지 한 번에 처리됨
|
||||
|
||||
DebugLogger.log('입고 처리 성공', tag: 'EQUIPMENT_IN');
|
||||
|
||||
@@ -435,15 +434,8 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// 저장 후 리스트 재로딩 (중복 방지 및 최신화)
|
||||
_loadManufacturers();
|
||||
_loadEquipmentNames();
|
||||
_loadCategories();
|
||||
_loadSubCategories();
|
||||
_loadSubSubCategories();
|
||||
_loadWarehouseLocations();
|
||||
_loadPartnerCompanies();
|
||||
_loadWarrantyLicenses();
|
||||
// 저장 후 드롭다운 데이터 새로고침 (백엔드 데이터 업데이트를 위해)
|
||||
_loadDropdownData();
|
||||
|
||||
return true;
|
||||
} on Failure catch (e) {
|
||||
@@ -456,6 +448,7 @@ class EquipmentInFormController extends ChangeNotifier {
|
||||
return false;
|
||||
} finally {
|
||||
_isSaving = false;
|
||||
_updateCanSave(); // 저장 완료 시 canSave 상태 업데이트
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
} else {
|
||||
throw Exception('LookupsService not registered in GetIt');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -67,11 +68,11 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
required PaginationParams params,
|
||||
Map<String, dynamic>? additionalFilters,
|
||||
}) async {
|
||||
// API 호출
|
||||
// API 호출 (페이지 크기를 명시적으로 10개로 고정)
|
||||
final apiEquipmentDtos = await ErrorHandler.handleApiCall(
|
||||
() => _equipmentService.getEquipmentsWithStatus(
|
||||
page: params.page,
|
||||
perPage: params.perPage,
|
||||
perPage: 10, // 🎯 장비 리스트는 항상 10개로 고정
|
||||
status: _statusFilter != null ?
|
||||
EquipmentStatusConverter.clientToServer(_statusFilter) : null,
|
||||
search: params.search,
|
||||
@@ -98,7 +99,10 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
}
|
||||
|
||||
// DTO를 UnifiedEquipment로 변환
|
||||
print('DEBUG [EquipmentListController] Converting ${apiEquipmentDtos.items.length} DTOs to UnifiedEquipment');
|
||||
final items = apiEquipmentDtos.items.map((dto) {
|
||||
// 🔧 [DEBUG] JOIN된 데이터 로깅
|
||||
print('DEBUG [EquipmentListController] DTO ID: ${dto.id}, companyName: "${dto.companyName}", warehouseName: "${dto.warehouseName}"');
|
||||
final equipment = Equipment(
|
||||
id: dto.id,
|
||||
manufacturer: dto.manufacturer ?? 'Unknown',
|
||||
@@ -113,13 +117,24 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
// 간단한 Company 정보 생성 (사용하지 않으므로 제거)
|
||||
// final company = dto.companyName != null ? ... : null;
|
||||
|
||||
return UnifiedEquipment(
|
||||
final unifiedEquipment = UnifiedEquipment(
|
||||
id: dto.id,
|
||||
equipment: equipment,
|
||||
date: dto.createdAt ?? DateTime.now(),
|
||||
status: EquipmentStatusConverter.serverToClient(dto.status),
|
||||
notes: null, // EquipmentListDto에 remark 필드 없음
|
||||
// 🔧 [BUG FIX] 누락된 위치 정보 필드들 추가
|
||||
// 문제: 장비 리스트에서 위치 정보(현재 위치, 창고 위치)가 표시되지 않음
|
||||
// 원인: UnifiedEquipment 생성 시 JOIN된 데이터(companyName, warehouseName) 누락
|
||||
// 해결: EquipmentListDto의 JOIN된 데이터를 UnifiedEquipment 필드로 매핑
|
||||
currentCompany: dto.companyName, // API company_name → currentCompany
|
||||
warehouseLocation: dto.warehouseName, // API warehouse_name → warehouseLocation
|
||||
// currentBranch는 EquipmentListDto에 없으므로 null (백엔드 API 구조 변경으로 지점 개념 제거)
|
||||
currentBranch: null,
|
||||
);
|
||||
// 🔧 [DEBUG] 변환된 UnifiedEquipment 로깅 (필요 시 활성화)
|
||||
// print('DEBUG [EquipmentListController] UnifiedEquipment ID: ${unifiedEquipment.id}, currentCompany: "${unifiedEquipment.currentCompany}", warehouseLocation: "${unifiedEquipment.warehouseLocation}"');
|
||||
return unifiedEquipment;
|
||||
}).toList();
|
||||
|
||||
// API에서 반환한 실제 메타데이터 사용
|
||||
@@ -400,8 +415,8 @@ class EquipmentListController extends BaseListController<UnifiedEquipment> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 캐시된 장비 카테고리 목록 조회
|
||||
List<CategoryItem> getCachedEquipmentCategories() {
|
||||
/// 캐시된 장비 카테고리 조합 목록 조회
|
||||
List<CategoryCombinationItem> getCachedEquipmentCategories() {
|
||||
final result = _lookupsService.getEquipmentCategories();
|
||||
return result.fold(
|
||||
(failure) => [],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -618,10 +618,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
Routes.equipmentInAdd,
|
||||
);
|
||||
if (result == true) {
|
||||
setState(() {
|
||||
_controller.loadData();
|
||||
_controller.goToPage(1);
|
||||
});
|
||||
// 입고 완료 후 데이터 새로고침 (중복 방지)
|
||||
_controller.refresh();
|
||||
}
|
||||
},
|
||||
variant: ShadcnButtonVariant.primary,
|
||||
@@ -693,10 +691,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
Routes.equipmentInAdd,
|
||||
);
|
||||
if (result == true) {
|
||||
setState(() {
|
||||
_controller.loadData();
|
||||
_controller.goToPage(1);
|
||||
});
|
||||
// 입고 완료 후 데이터 새로고침 (중복 방지)
|
||||
_controller.refresh();
|
||||
}
|
||||
},
|
||||
variant: ShadcnButtonVariant.primary,
|
||||
@@ -743,7 +739,11 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
totalWidth += 100; // 카테고리
|
||||
totalWidth += 50; // 수량
|
||||
totalWidth += 70; // 상태
|
||||
totalWidth += 80; // 날짜
|
||||
totalWidth += 80; // 입출고일
|
||||
totalWidth += 120; // 입고지
|
||||
totalWidth += 120; // 구매처
|
||||
totalWidth += 100; // 구매일
|
||||
totalWidth += 100; // 구매가격
|
||||
totalWidth += 90; // 관리
|
||||
|
||||
// 상세 컬럼들 (조건부)
|
||||
@@ -751,10 +751,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
totalWidth += 120; // 시리얼번호
|
||||
totalWidth += 120; // 바코드
|
||||
totalWidth += 120; // 현재 위치
|
||||
totalWidth += 100; // 창고 위치
|
||||
totalWidth += 100; // 창고 위치 (중복 - 입고지와 다름)
|
||||
totalWidth += 100; // 점검일
|
||||
totalWidth += 100; // 구매일
|
||||
totalWidth += 100; // 구매가격
|
||||
}
|
||||
|
||||
// padding 추가 (좌우 각 16px)
|
||||
@@ -853,15 +851,21 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
_buildHeaderCell('수량', flex: 1, useExpanded: useExpanded, minWidth: 50),
|
||||
// 상태
|
||||
_buildHeaderCell('상태', flex: 2, useExpanded: useExpanded, minWidth: 70),
|
||||
// 날짜
|
||||
_buildHeaderCell('날짜', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
// 입출고일
|
||||
_buildHeaderCell('입출고일', flex: 2, useExpanded: useExpanded, minWidth: 80),
|
||||
// 입고지
|
||||
_buildHeaderCell('입고지', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 구매처
|
||||
_buildHeaderCell('구매처', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
// 구매일
|
||||
_buildHeaderCell('구매일', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
// 구매가격
|
||||
_buildHeaderCell('구매가격', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
_buildHeaderCell('현재 위치', flex: 3, useExpanded: useExpanded, minWidth: 120),
|
||||
_buildHeaderCell('창고 위치', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
_buildHeaderCell('점검일', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
_buildHeaderCell('구매일', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
_buildHeaderCell('구매가격', flex: 2, useExpanded: useExpanded, minWidth: 100),
|
||||
],
|
||||
// 관리
|
||||
_buildHeaderCell('관리', flex: 2, useExpanded: useExpanded, minWidth: 90),
|
||||
@@ -975,13 +979,57 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 70,
|
||||
),
|
||||
// 날짜
|
||||
// 입출고일
|
||||
_buildDataCell(
|
||||
_buildDateWidget(equipment),
|
||||
_buildCreatedDateWidget(equipment),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 80,
|
||||
),
|
||||
// 입고지
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.warehouseLocation ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 구매처 (회사명)
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.currentCompany ?? '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 3,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 120,
|
||||
),
|
||||
// 구매일
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.purchaseDate != null
|
||||
? '${equipment.equipment.purchaseDate!.year}/${equipment.equipment.purchaseDate!.month.toString().padLeft(2, '0')}/${equipment.equipment.purchaseDate!.day.toString().padLeft(2, '0')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 구매가격
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.purchasePrice != null
|
||||
? '₩${equipment.equipment.purchasePrice!.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 상세 정보 (조건부)
|
||||
if (_showDetailedColumns) ...[
|
||||
// 현재 위치 (회사 + 지점)
|
||||
@@ -1011,30 +1059,6 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 구매일
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.inDate != null
|
||||
? '${equipment.equipment.inDate!.year}/${equipment.equipment.inDate!.month.toString().padLeft(2, '0')}/${equipment.equipment.inDate!.day.toString().padLeft(2, '0')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
// 구매가격
|
||||
_buildDataCell(
|
||||
Text(
|
||||
equipment.equipment.purchasePrice != null
|
||||
? '₩${equipment.equipment.purchasePrice!.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}'
|
||||
: '-',
|
||||
style: ShadcnTheme.bodySmall,
|
||||
),
|
||||
flex: 2,
|
||||
useExpanded: useExpanded,
|
||||
minWidth: 100,
|
||||
),
|
||||
],
|
||||
// 관리
|
||||
_buildDataCell(
|
||||
@@ -1159,8 +1183,8 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 날짜 위젯 빌더
|
||||
Widget _buildDateWidget(UnifiedEquipment equipment) {
|
||||
/// 입출고일 위젯 빌더
|
||||
Widget _buildCreatedDateWidget(UnifiedEquipment equipment) {
|
||||
String dateStr = equipment.date.toString().substring(0, 10);
|
||||
return Text(
|
||||
dateStr,
|
||||
@@ -1288,23 +1312,63 @@ class _EquipmentListState extends State<EquipmentList> {
|
||||
return '${category.substring(0, 2)}...';
|
||||
}
|
||||
|
||||
/// 카테고리 툴팁 위젯
|
||||
/// 영어 카테고리를 한국어로 변환
|
||||
String _translateCategory(String category) {
|
||||
const Map<String, String> categoryMap = {
|
||||
// 대분류
|
||||
'Network': '네트워크',
|
||||
'Server': '서버',
|
||||
'Storage': '스토리지',
|
||||
'Security': '보안',
|
||||
'Computer': '컴퓨터',
|
||||
'Mobile': '모바일',
|
||||
'Printer': '프린터',
|
||||
'Monitor': '모니터',
|
||||
'Peripheral': '주변기기',
|
||||
// 중분류
|
||||
'Router': '라우터',
|
||||
'Switch': '스위치',
|
||||
'Firewall': '방화벽',
|
||||
'Laptop': '노트북',
|
||||
'Desktop': '데스크톱',
|
||||
'Tablet': '태블릿',
|
||||
'Smartphone': '스마트폰',
|
||||
'Scanner': '스캐너',
|
||||
'Keyboard': '키보드',
|
||||
'Mouse': '마우스',
|
||||
// 소분류 예시
|
||||
'Wireless': '무선',
|
||||
'Wired': '유선',
|
||||
'Gaming': '게이밍',
|
||||
'Office': '사무용',
|
||||
};
|
||||
|
||||
return categoryMap[category] ?? category;
|
||||
}
|
||||
|
||||
/// 카테고리 툴팁 위젯 (한국어 변환 적용)
|
||||
Widget _buildCategoryWithTooltip(UnifiedEquipment equipment) {
|
||||
// 영어→한국어 변환 적용
|
||||
final translatedCategory = _translateCategory(equipment.equipment.category);
|
||||
final translatedSubCategory = _translateCategory(equipment.equipment.subCategory);
|
||||
final translatedSubSubCategory = _translateCategory(equipment.equipment.subSubCategory);
|
||||
|
||||
final fullCategory = EquipmentDisplayHelper.formatCategory(
|
||||
equipment.equipment.category,
|
||||
equipment.equipment.subCategory,
|
||||
equipment.equipment.subSubCategory,
|
||||
translatedCategory,
|
||||
translatedSubCategory,
|
||||
translatedSubSubCategory,
|
||||
);
|
||||
|
||||
// 축약 표기 적용 - 비어있지 않은 카테고리만 표시
|
||||
final List<String> parts = [];
|
||||
if (equipment.equipment.category.isNotEmpty) {
|
||||
parts.add(_shortenCategory(equipment.equipment.category));
|
||||
if (translatedCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedCategory));
|
||||
}
|
||||
if (equipment.equipment.subCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(equipment.equipment.subCategory));
|
||||
if (translatedSubCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedSubCategory));
|
||||
}
|
||||
if (equipment.equipment.subSubCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(equipment.equipment.subSubCategory));
|
||||
if (translatedSubSubCategory.isNotEmpty) {
|
||||
parts.add(_shortenCategory(translatedSubSubCategory));
|
||||
}
|
||||
final shortCategory = parts.join(' > ');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user