번호 자동 부여 대응 및 API 공통 처리 보강
This commit is contained in:
@@ -72,8 +72,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
query: {if (includeParts.isNotEmpty) 'include': includeParts.join(',')},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 활성화된 결재 행위 목록을 조회한다.
|
||||
@@ -133,8 +132,9 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
'$_basePath/$id/can-proceed',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalProceedStatusDto.fromJson(data).toEntity();
|
||||
return ApprovalProceedStatusDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 새로운 결재를 생성한다.
|
||||
@@ -145,8 +145,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 기본 정보를 수정한다.
|
||||
@@ -157,8 +156,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재를 삭제(비활성화)한다.
|
||||
@@ -174,8 +172,7 @@ class ApprovalRepositoryRemote implements ApprovalRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalDto.fromJson(data).toEntity();
|
||||
return ApprovalDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계/행위 응답에서 결재 객체 JSON을 추출한다.
|
||||
|
||||
@@ -51,9 +51,8 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
query: {if (includeSteps) 'include': 'steps'},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalTemplateDto.fromJson(
|
||||
data,
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: includeSteps);
|
||||
}
|
||||
|
||||
@@ -68,9 +67,8 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
data: input.toCreatePayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
final created = ApprovalTemplateDto.fromJson(
|
||||
data,
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: false);
|
||||
if (steps.isNotEmpty) {
|
||||
await _postSteps(created.id, steps);
|
||||
@@ -109,8 +107,9 @@ class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ApprovalTemplateDto.fromJson(data).toEntity(includeSteps: false);
|
||||
return ApprovalTemplateDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity(includeSteps: false);
|
||||
}
|
||||
|
||||
/// 템플릿 단계 전체를 신규로 등록한다.
|
||||
|
||||
@@ -204,14 +204,12 @@ extension ApprovalStepActionTypeX on ApprovalStepActionType {
|
||||
class ApprovalCreateInput {
|
||||
ApprovalCreateInput({
|
||||
required this.transactionId,
|
||||
required this.approvalNo,
|
||||
required this.approvalStatusId,
|
||||
required this.requestedById,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final int transactionId;
|
||||
final String approvalNo;
|
||||
final int approvalStatusId;
|
||||
final int requestedById;
|
||||
final String? note;
|
||||
@@ -220,7 +218,6 @@ class ApprovalCreateInput {
|
||||
final trimmedNote = note?.trim();
|
||||
return {
|
||||
'transaction_id': transactionId,
|
||||
'approval_no': approvalNo,
|
||||
'approval_status_id': approvalStatusId,
|
||||
'requested_by_id': requestedById,
|
||||
if (trimmedNote != null && trimmedNote.isNotEmpty) 'note': trimmedNote,
|
||||
|
||||
@@ -116,7 +116,7 @@ class _ApprovalHistoryEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final sortedHistories = _applySorting(histories);
|
||||
|
||||
return AppLayout(
|
||||
|
||||
@@ -135,7 +135,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -378,14 +378,13 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
|
||||
/// 신규 결재 등록 다이얼로그를 열어 UI 단계에서 필요한 필드와 안내를 제공한다.
|
||||
Future<void> _openCreateApprovalDialog() async {
|
||||
final approvalNoController = TextEditingController();
|
||||
final transactionController = TextEditingController();
|
||||
final requesterController = TextEditingController();
|
||||
final noteController = TextEditingController();
|
||||
String? createdApprovalNo;
|
||||
InventoryEmployeeSuggestion? requesterSelection;
|
||||
int? statusId = _controller.defaultApprovalStatusId;
|
||||
String? transactionError;
|
||||
String? approvalNoError;
|
||||
String? statusError;
|
||||
String? requesterError;
|
||||
|
||||
@@ -422,8 +421,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
onPressed: isSubmitting
|
||||
? null
|
||||
: () async {
|
||||
final approvalNo = approvalNoController.text
|
||||
.trim();
|
||||
final transactionText = transactionController.text
|
||||
.trim();
|
||||
final transactionId = int.tryParse(
|
||||
@@ -433,9 +430,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
final hasStatuses = statusOptions.isNotEmpty;
|
||||
|
||||
setState(() {
|
||||
approvalNoError = approvalNo.isEmpty
|
||||
? '결재번호를 입력하세요.'
|
||||
: null;
|
||||
transactionError = transactionText.isEmpty
|
||||
? '트랜잭션 ID를 입력하세요.'
|
||||
: (transactionId == null
|
||||
@@ -449,8 +443,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
: null;
|
||||
});
|
||||
|
||||
if (approvalNoError != null ||
|
||||
transactionError != null ||
|
||||
if (transactionError != null ||
|
||||
statusError != null ||
|
||||
requesterError != null) {
|
||||
return;
|
||||
@@ -458,7 +451,6 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
|
||||
final input = ApprovalCreateInput(
|
||||
transactionId: transactionId!,
|
||||
approvalNo: approvalNo,
|
||||
approvalStatusId: statusId!,
|
||||
requestedById: requesterSelection!.id,
|
||||
note: note.isEmpty ? null : note,
|
||||
@@ -470,6 +462,7 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
return;
|
||||
}
|
||||
if (result != null) {
|
||||
createdApprovalNo = result.approvalNo;
|
||||
Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
@@ -489,28 +482,18 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('결재번호', style: shadTheme.textTheme.small),
|
||||
const SizedBox(height: 8),
|
||||
ShadInput(
|
||||
controller: approvalNoController,
|
||||
enabled: !isSubmitting,
|
||||
placeholder: const Text('예: APP-2025-0001'),
|
||||
onChanged: (_) {
|
||||
if (approvalNoError != null) {
|
||||
setState(() => approvalNoError = null);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (approvalNoError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
approvalNoError!,
|
||||
style: shadTheme.textTheme.small.copyWith(
|
||||
color: materialTheme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: shadTheme.colorScheme.mutedForeground
|
||||
.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'결재번호는 저장 시 자동으로 생성됩니다.',
|
||||
style: shadTheme.textTheme.muted,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('트랜잭션 ID', style: shadTheme.textTheme.small),
|
||||
const SizedBox(height: 8),
|
||||
@@ -689,13 +672,16 @@ class _ApprovalEnabledPageState extends State<_ApprovalEnabledPage> {
|
||||
},
|
||||
);
|
||||
|
||||
approvalNoController.dispose();
|
||||
transactionController.dispose();
|
||||
requesterController.dispose();
|
||||
noteController.dispose();
|
||||
|
||||
if (created == true && mounted) {
|
||||
SuperportToast.success(context, '결재를 생성했습니다.');
|
||||
final number = createdApprovalNo ?? '-';
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'결재를 생성했습니다. ($number)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,10 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'page': page,
|
||||
'page_size': pageSize,
|
||||
if (query != null && query.isNotEmpty) 'q': query,
|
||||
if (statusId != null) ...{
|
||||
'status_id': statusId,
|
||||
'step_status_id': statusId,
|
||||
},
|
||||
if (statusId != null) ...{'status_id': statusId},
|
||||
if (approverId != null) 'approver_id': approverId,
|
||||
if (approvalId != null) 'approval_id': approvalId,
|
||||
'include': 'approval,approver,step_status',
|
||||
'include': 'approval,approver,status',
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
@@ -54,8 +51,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'$_basePath/$id',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? const {};
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 생성한다.
|
||||
@@ -66,11 +64,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 수정한다.
|
||||
@@ -81,11 +77,9 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
|
||||
/// 결재 단계를 비활성화한다.
|
||||
@@ -101,10 +95,8 @@ class ApprovalStepRepositoryRemote implements ApprovalStepRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final raw = response.data;
|
||||
final data =
|
||||
(raw?['data'] as Map<String, dynamic>?) ??
|
||||
(raw is Map<String, dynamic> ? raw : const <String, dynamic>{});
|
||||
return ApprovalStepRecordDto.fromJson(data).toEntity();
|
||||
return ApprovalStepRecordDto.fromJson(
|
||||
_api.unwrapAsMap(response),
|
||||
).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class _ApprovalStepEnabledPageState extends State<_ApprovalStepEnabledPage> {
|
||||
final pageSize = result?.pageSize ?? records.length;
|
||||
final totalPages = pageSize == 0
|
||||
? 1
|
||||
: (totalCount / pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (totalCount / pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -116,7 +116,7 @@ class _ApprovalTemplateEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final showReset =
|
||||
_searchController.text.trim().isNotEmpty ||
|
||||
_controller.statusFilter != ApprovalTemplateStatusFilter.all;
|
||||
|
||||
@@ -26,8 +26,7 @@ class AuthRepositoryRemote implements AuthRepository {
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final json = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return AuthSessionDto.fromJson(json).toEntity();
|
||||
return AuthSessionDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -37,7 +36,6 @@ class AuthRepositoryRemote implements AuthRepository {
|
||||
data: {'refresh_token': refreshToken},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final json = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return AuthSessionDto.fromJson(json).toEntity();
|
||||
return AuthSessionDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ class DashboardRepositoryRemote implements DashboardRepository {
|
||||
_summaryPath,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final json = (response.data?['data'] as Map<String, dynamic>?) ??
|
||||
response.data ??
|
||||
const <String, dynamic>{};
|
||||
return DashboardSummaryDto.fromJson(json).toEntity();
|
||||
return DashboardSummaryDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ class _KpiCard extends StatelessWidget {
|
||||
const SizedBox(height: 6),
|
||||
Text(kpi?.displayValue ?? '--', style: theme.textTheme.h3),
|
||||
const SizedBox(height: 8),
|
||||
Text(kpi?.trendLabel ?? '데이터 동기화 중', style: theme.textTheme.muted),
|
||||
_DeltaTrendRow(kpi: kpi),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -265,6 +265,68 @@ class _KpiCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DeltaTrendRow extends StatelessWidget {
|
||||
const _DeltaTrendRow({this.kpi});
|
||||
|
||||
final DashboardKpi? kpi;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
final delta = kpi?.delta;
|
||||
|
||||
if (delta == null) {
|
||||
return Text(kpi?.trendLabel ?? '데이터 동기화 중', style: theme.textTheme.muted);
|
||||
}
|
||||
|
||||
final absDelta = delta.abs();
|
||||
final percent = (absDelta * 100).toStringAsFixed(1);
|
||||
final trimmedPercent = percent.endsWith('.0')
|
||||
? percent.substring(0, percent.length - 2)
|
||||
: percent;
|
||||
final percentText = delta > 0
|
||||
? '+$trimmedPercent%'
|
||||
: delta < 0
|
||||
? '-$trimmedPercent%'
|
||||
: '0%';
|
||||
|
||||
final icon = delta > 0
|
||||
? lucide.LucideIcons.arrowUpRight
|
||||
: delta < 0
|
||||
? lucide.LucideIcons.arrowDownRight
|
||||
: lucide.LucideIcons.minus;
|
||||
|
||||
final Color color;
|
||||
if (delta > 0) {
|
||||
color = theme.colorScheme.primary;
|
||||
} else if (delta < 0) {
|
||||
color = theme.colorScheme.destructive;
|
||||
} else {
|
||||
color = theme.textTheme.muted.color ?? theme.colorScheme.mutedForeground;
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 16, color: color),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
percentText,
|
||||
style: theme.textTheme.small.copyWith(color: color),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
kpi?.trendLabel ?? '전일 대비',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.muted,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RecentTransactionsCard extends StatelessWidget {
|
||||
const _RecentTransactionsCard({required this.transactions});
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ class InboundRecord {
|
||||
required this.processedAt,
|
||||
required this.warehouse,
|
||||
this.warehouseId,
|
||||
this.warehouseCode,
|
||||
this.warehouseZipcode,
|
||||
this.warehouseAddress,
|
||||
required this.status,
|
||||
this.statusId,
|
||||
required this.writer,
|
||||
@@ -32,6 +35,9 @@ class InboundRecord {
|
||||
processedAt: transaction.transactionDate,
|
||||
warehouse: transaction.warehouse.name,
|
||||
warehouseId: transaction.warehouse.id,
|
||||
warehouseCode: transaction.warehouse.code,
|
||||
warehouseZipcode: transaction.warehouse.zipcode,
|
||||
warehouseAddress: transaction.warehouse.addressLine,
|
||||
status: transaction.status.name,
|
||||
statusId: transaction.status.id,
|
||||
writer: transaction.createdBy.name,
|
||||
@@ -54,6 +60,9 @@ class InboundRecord {
|
||||
final DateTime processedAt;
|
||||
final String warehouse;
|
||||
final int? warehouseId;
|
||||
final String? warehouseCode;
|
||||
final String? warehouseZipcode;
|
||||
final String? warehouseAddress;
|
||||
final String status;
|
||||
final int? statusId;
|
||||
final String writer;
|
||||
|
||||
@@ -75,6 +75,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
String? _errorMessage;
|
||||
Set<int> _processingTransactionIds = {};
|
||||
|
||||
String? _routeSelectedNumber;
|
||||
String? _pendingDetailNumber;
|
||||
bool _suppressNextRouteSelection = false;
|
||||
|
||||
late List<String> _statusOptions;
|
||||
final Map<String, LookupItem> _statusLookup = {};
|
||||
LookupItem? _transactionTypeLookup;
|
||||
@@ -184,6 +188,26 @@ class _InboundPageState extends State<InboundPage> {
|
||||
controller.transactionType ?? _transactionTypeLookup;
|
||||
_refreshSelection();
|
||||
});
|
||||
final detailNumber = _pendingDetailNumber;
|
||||
if (detailNumber != null) {
|
||||
_pendingDetailNumber = null;
|
||||
InboundRecord? matched;
|
||||
for (final record in _records) {
|
||||
if (record.transactionNumber == detailNumber) {
|
||||
matched = record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched != null) {
|
||||
final target = matched;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(target);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -456,10 +480,7 @@ class _InboundPageState extends State<InboundPage> {
|
||||
mobile: (_) => _InboundMobileList(
|
||||
records: visibleRecords,
|
||||
selected: _selectedRecord,
|
||||
onSelect: (record) {
|
||||
setState(() => _selectedRecord = record);
|
||||
_showDetailDialog(record);
|
||||
},
|
||||
onSelect: _handleRecordTap,
|
||||
dateFormatter: _dateFormatter,
|
||||
currencyFormatter: _currencyFormatter,
|
||||
transitionsEnabled: _transitionsEnabled,
|
||||
@@ -663,18 +684,37 @@ class _InboundPageState extends State<InboundPage> {
|
||||
const FixedTableSpanExtent(InboundTableSpec.rowSpanHeight),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = records[rowIndex];
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_showDetailDialog(record);
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleRecordTap(InboundRecord record) {
|
||||
_selectRecord(record, openDetail: true);
|
||||
}
|
||||
|
||||
List<int> _visibleColumnsFor(DeviceBreakpoint breakpoint) {
|
||||
return InboundTableSpec.visibleColumns(breakpoint);
|
||||
}
|
||||
|
||||
void _selectRecord(InboundRecord? record, {bool openDetail = false}) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
final selectedNumber = record?.transactionNumber;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
_updateRoute(selected: selectedNumber);
|
||||
if (openDetail && record != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(record);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<ShadTableCell> _buildTableCells(
|
||||
InboundRecord record,
|
||||
List<int> visibleColumns,
|
||||
@@ -767,18 +807,14 @@ class _InboundPageState extends State<InboundPage> {
|
||||
Future<void> _handleCreate() async {
|
||||
final record = await _showInboundFormDialog();
|
||||
if (record != null) {
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_selectRecord(record, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleEdit(InboundRecord record) async {
|
||||
final updated = await _showInboundFormDialog(initial: record);
|
||||
if (updated != null) {
|
||||
setState(() {
|
||||
_selectedRecord = updated;
|
||||
});
|
||||
_selectRecord(updated, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,6 +1118,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
? pageSizeParam
|
||||
: InboundTableSpec.pageSizeOptions.first;
|
||||
final page = (pageParam != null && pageParam > 0) ? pageParam : 1;
|
||||
final selectedParam = params['selected']?.trim();
|
||||
final selectedNumber = selectedParam == null || selectedParam.isEmpty
|
||||
? null
|
||||
: selectedParam;
|
||||
|
||||
void assign() {
|
||||
_pendingQuery = query;
|
||||
@@ -1106,6 +1146,13 @@ class _InboundPageState extends State<InboundPage> {
|
||||
_sortAscending = resolvedSortAscending;
|
||||
_pageSize = pageSize;
|
||||
_currentPage = page;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
if (_suppressNextRouteSelection) {
|
||||
_pendingDetailNumber = null;
|
||||
} else {
|
||||
_pendingDetailNumber = selectedNumber;
|
||||
}
|
||||
_suppressNextRouteSelection = false;
|
||||
_refreshSelection();
|
||||
}
|
||||
|
||||
@@ -1121,9 +1168,7 @@ class _InboundPageState extends State<InboundPage> {
|
||||
void _goToPage(int page) {
|
||||
final totalItems = _result?.total ?? _filteredRecords.length;
|
||||
final totalPages = _calculateTotalPages(totalItems);
|
||||
final int target = page < 1
|
||||
? 1
|
||||
: (page > totalPages ? totalPages : page);
|
||||
final int target = page < 1 ? 1 : (page > totalPages ? totalPages : page);
|
||||
if (target == _currentPage) {
|
||||
return;
|
||||
}
|
||||
@@ -1141,6 +1186,16 @@ class _InboundPageState extends State<InboundPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final selectedNumber = _routeSelectedNumber;
|
||||
if (selectedNumber != null) {
|
||||
for (final record in filtered) {
|
||||
if (record.transactionNumber == selectedNumber) {
|
||||
_selectedRecord = record;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final current = _selectedRecord;
|
||||
if (current != null) {
|
||||
InboundRecord? matched;
|
||||
@@ -1162,7 +1217,7 @@ class _InboundPageState extends State<InboundPage> {
|
||||
_selectedRecord = filtered.first;
|
||||
}
|
||||
|
||||
void _updateRoute({int? page, int? pageSize}) {
|
||||
void _updateRoute({int? page, int? pageSize, String? selected}) {
|
||||
if (!mounted) return;
|
||||
final targetPage = page ?? _currentPage;
|
||||
final targetPageSize = pageSize ?? _pageSize;
|
||||
@@ -1208,6 +1263,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
if (targetPageSize != InboundTableSpec.pageSizeOptions.first) {
|
||||
params['page_size'] = targetPageSize.toString();
|
||||
}
|
||||
final selectedNumber = selected ?? _selectedRecord?.transactionNumber;
|
||||
if (selectedNumber != null && selectedNumber.isNotEmpty) {
|
||||
params['selected'] = selectedNumber;
|
||||
}
|
||||
|
||||
final uri = Uri(
|
||||
path: widget.routeUri.path,
|
||||
@@ -1221,6 +1280,7 @@ class _InboundPageState extends State<InboundPage> {
|
||||
if (router == null) {
|
||||
return;
|
||||
}
|
||||
_suppressNextRouteSelection = true;
|
||||
router.go(newLocation);
|
||||
}
|
||||
|
||||
@@ -1351,12 +1411,8 @@ class _InboundPageState extends State<InboundPage> {
|
||||
text: writerLabel(writerSelection),
|
||||
);
|
||||
final remarkController = TextEditingController(text: initial?.remark ?? '');
|
||||
final transactionNumberController = TextEditingController(
|
||||
text: initial?.transactionNumber ?? '',
|
||||
);
|
||||
final approvalNumberController = TextEditingController(
|
||||
text: initial?.raw?.approval?.approvalNo ?? '',
|
||||
);
|
||||
final assignedTransactionNo = initial?.transactionNumber;
|
||||
final assignedApprovalNo = initial?.raw?.approval?.approvalNo;
|
||||
final approvalNoteController = TextEditingController();
|
||||
final transactionTypeValue =
|
||||
initial?.transactionType ??
|
||||
@@ -1378,8 +1434,6 @@ class _InboundPageState extends State<InboundPage> {
|
||||
};
|
||||
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
String? headerNotice;
|
||||
@@ -1408,18 +1462,12 @@ class _InboundPageState extends State<InboundPage> {
|
||||
writerController: writerController,
|
||||
writerSelection: writerSelection,
|
||||
requireWriterSelection: initial == null,
|
||||
transactionNumberController: transactionNumberController,
|
||||
transactionNumberRequired: initial == null,
|
||||
approvalNumberController: approvalNumberController,
|
||||
approvalNumberRequired: initial == null,
|
||||
warehouseSelection: warehouseSelection,
|
||||
statusValue: statusValue.value,
|
||||
drafts: drafts,
|
||||
lineErrors: lineErrors,
|
||||
);
|
||||
writerError = validationResult.writerError;
|
||||
transactionNumberError = validationResult.transactionNumberError;
|
||||
approvalNumberError = validationResult.approvalNumberError;
|
||||
warehouseError = validationResult.warehouseError;
|
||||
statusError = validationResult.statusError;
|
||||
headerNotice = validationResult.headerNotice;
|
||||
@@ -1463,8 +1511,6 @@ class _InboundPageState extends State<InboundPage> {
|
||||
|
||||
final remarkText = remarkController.text.trim();
|
||||
final remarkValue = remarkText.isEmpty ? null : remarkText;
|
||||
final transactionNoValue = transactionNumberController.text.trim();
|
||||
final approvalNoValue = approvalNumberController.text.trim();
|
||||
final approvalNoteValue = approvalNoteController.text.trim();
|
||||
|
||||
final transactionId = initial?.id;
|
||||
@@ -1529,7 +1575,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '입고 정보가 수정되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'입고 정보가 수정되었습니다. (${updated.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
return;
|
||||
}
|
||||
@@ -1553,7 +1602,6 @@ class _InboundPageState extends State<InboundPage> {
|
||||
.toList(growable: false);
|
||||
final created = await controller.createTransaction(
|
||||
StockTransactionCreateInput(
|
||||
transactionNo: transactionNoValue,
|
||||
transactionTypeId: transactionTypeLookup.id,
|
||||
transactionStatusId: statusItem.id,
|
||||
warehouseId: warehouseId,
|
||||
@@ -1562,7 +1610,6 @@ class _InboundPageState extends State<InboundPage> {
|
||||
note: remarkValue,
|
||||
lines: createLines,
|
||||
approval: StockTransactionApprovalInput(
|
||||
approvalNo: approvalNoValue,
|
||||
requestedById: createdById,
|
||||
note: approvalNoteValue.isEmpty ? null : approvalNoteValue,
|
||||
),
|
||||
@@ -1573,7 +1620,10 @@ class _InboundPageState extends State<InboundPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '입고가 등록되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'입고가 등록되었습니다. (${created.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
} catch (error) {
|
||||
updateSaving(false);
|
||||
@@ -1719,20 +1769,11 @@ class _InboundPageState extends State<InboundPage> {
|
||||
width: 240,
|
||||
child: SuperportFormField(
|
||||
label: '트랜잭션번호',
|
||||
required: true,
|
||||
errorText: transactionNumberError,
|
||||
child: ShadInput(
|
||||
controller: transactionNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: IN-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (transactionNumberError != null) {
|
||||
setState(() {
|
||||
transactionNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
assignedTransactionNo ?? '저장 시 자동 생성',
|
||||
style: assignedTransactionNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1740,20 +1781,11 @@ class _InboundPageState extends State<InboundPage> {
|
||||
width: 240,
|
||||
child: SuperportFormField(
|
||||
label: '결재번호',
|
||||
required: true,
|
||||
errorText: approvalNumberError,
|
||||
child: ShadInput(
|
||||
controller: approvalNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: APP-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (approvalNumberError != null) {
|
||||
setState(() {
|
||||
approvalNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
assignedApprovalNo ?? '저장 시 자동 생성',
|
||||
style: assignedApprovalNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1926,8 +1958,6 @@ class _InboundPageState extends State<InboundPage> {
|
||||
statusValue.dispose();
|
||||
writerController.dispose();
|
||||
remarkController.dispose();
|
||||
transactionNumberController.dispose();
|
||||
approvalNumberController.dispose();
|
||||
approvalNoteController.dispose();
|
||||
transactionTypeController.dispose();
|
||||
processedAt.dispose();
|
||||
@@ -2271,10 +2301,6 @@ _InboundFormValidation _validateInboundForm({
|
||||
required TextEditingController writerController,
|
||||
required InventoryEmployeeSuggestion? writerSelection,
|
||||
required bool requireWriterSelection,
|
||||
required TextEditingController transactionNumberController,
|
||||
required bool transactionNumberRequired,
|
||||
required TextEditingController approvalNumberController,
|
||||
required bool approvalNumberRequired,
|
||||
required InventoryWarehouseOption? warehouseSelection,
|
||||
required String statusValue,
|
||||
required List<_LineItemDraft> drafts,
|
||||
@@ -2282,8 +2308,6 @@ _InboundFormValidation _validateInboundForm({
|
||||
}) {
|
||||
var isValid = true;
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
String? headerNotice;
|
||||
@@ -2299,18 +2323,6 @@ _InboundFormValidation _validateInboundForm({
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final transactionNumber = transactionNumberController.text.trim();
|
||||
if (transactionNumberRequired && transactionNumber.isEmpty) {
|
||||
transactionNumberError = '거래번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final approvalNumber = approvalNumberController.text.trim();
|
||||
if (approvalNumberRequired && approvalNumber.isEmpty) {
|
||||
approvalNumberError = '결재번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (warehouseSelection == null) {
|
||||
warehouseError = '창고를 선택하세요.';
|
||||
isValid = false;
|
||||
@@ -2369,8 +2381,6 @@ _InboundFormValidation _validateInboundForm({
|
||||
return _InboundFormValidation(
|
||||
isValid: isValid,
|
||||
writerError: writerError,
|
||||
transactionNumberError: transactionNumberError,
|
||||
approvalNumberError: approvalNumberError,
|
||||
warehouseError: warehouseError,
|
||||
statusError: statusError,
|
||||
headerNotice: headerNotice,
|
||||
@@ -2386,8 +2396,6 @@ class _InboundFormValidation {
|
||||
const _InboundFormValidation({
|
||||
required this.isValid,
|
||||
this.writerError,
|
||||
this.transactionNumberError,
|
||||
this.approvalNumberError,
|
||||
this.warehouseError,
|
||||
this.statusError,
|
||||
this.headerNotice,
|
||||
@@ -2395,8 +2403,6 @@ class _InboundFormValidation {
|
||||
|
||||
final bool isValid;
|
||||
final String? writerError;
|
||||
final String? transactionNumberError;
|
||||
final String? approvalNumberError;
|
||||
final String? warehouseError;
|
||||
final String? statusError;
|
||||
final String? headerNotice;
|
||||
|
||||
@@ -43,6 +43,15 @@ class InboundDetailView extends StatelessWidget {
|
||||
value: dateFormatter.format(record.processedAt),
|
||||
),
|
||||
_DetailChip(label: '창고', value: record.warehouse),
|
||||
if (record.warehouseCode != null &&
|
||||
record.warehouseCode!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 코드', value: record.warehouseCode!),
|
||||
if (record.warehouseZipcode != null &&
|
||||
record.warehouseZipcode!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 우편번호', value: record.warehouseZipcode!),
|
||||
if (record.warehouseAddress != null &&
|
||||
record.warehouseAddress!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 주소', value: record.warehouseAddress!),
|
||||
_DetailChip(label: '트랜잭션 유형', value: record.transactionType),
|
||||
_DetailChip(label: '상태', value: record.status),
|
||||
_DetailChip(label: '작성자', value: record.writer),
|
||||
|
||||
@@ -11,6 +11,9 @@ class OutboundRecord {
|
||||
required this.processedAt,
|
||||
required this.warehouse,
|
||||
this.warehouseId,
|
||||
this.warehouseCode,
|
||||
this.warehouseZipcode,
|
||||
this.warehouseAddress,
|
||||
required this.status,
|
||||
this.statusId,
|
||||
required this.writer,
|
||||
@@ -31,6 +34,9 @@ class OutboundRecord {
|
||||
processedAt: transaction.transactionDate,
|
||||
warehouse: transaction.warehouse.name,
|
||||
warehouseId: transaction.warehouse.id,
|
||||
warehouseCode: transaction.warehouse.code,
|
||||
warehouseZipcode: transaction.warehouse.zipcode,
|
||||
warehouseAddress: transaction.warehouse.addressLine,
|
||||
status: transaction.status.name,
|
||||
statusId: transaction.status.id,
|
||||
writer: transaction.createdBy.name,
|
||||
@@ -54,6 +60,9 @@ class OutboundRecord {
|
||||
final DateTime processedAt;
|
||||
final String warehouse;
|
||||
final int? warehouseId;
|
||||
final String? warehouseCode;
|
||||
final String? warehouseZipcode;
|
||||
final String? warehouseAddress;
|
||||
final String status;
|
||||
final int? statusId;
|
||||
final String writer;
|
||||
|
||||
@@ -79,6 +79,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
String? _errorMessage;
|
||||
Set<int> _processingTransactionIds = {};
|
||||
|
||||
String? _routeSelectedNumber;
|
||||
String? _pendingDetailNumber;
|
||||
bool _suppressNextRouteSelection = false;
|
||||
|
||||
late List<String> _statusOptions;
|
||||
final Map<String, LookupItem> _statusLookup = {};
|
||||
LookupItem? _transactionTypeLookup;
|
||||
@@ -256,6 +260,26 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
controller.transactionType ?? _transactionTypeLookup;
|
||||
_refreshSelection();
|
||||
});
|
||||
final detailNumber = _pendingDetailNumber;
|
||||
if (detailNumber != null) {
|
||||
_pendingDetailNumber = null;
|
||||
OutboundRecord? matched;
|
||||
for (final record in _records) {
|
||||
if (record.transactionNumber == detailNumber) {
|
||||
matched = record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched != null) {
|
||||
final target = matched;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(target);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -580,10 +604,7 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_showDetailDialog(record);
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -748,6 +769,24 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
return records;
|
||||
}
|
||||
|
||||
void _selectRecord(OutboundRecord? record, {bool openDetail = false}) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
final selectedNumber = record?.transactionNumber;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
_updateRoute(selected: selectedNumber);
|
||||
if (openDetail && record != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(record);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _buildRecordRow(OutboundRecord record) {
|
||||
final primaryItem = record.items.isNotEmpty ? record.items.first : null;
|
||||
return [
|
||||
@@ -848,18 +887,14 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
Future<void> _handleCreate() async {
|
||||
final record = await _showOutboundFormDialog();
|
||||
if (record != null) {
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_selectRecord(record, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleEdit(OutboundRecord record) async {
|
||||
final updated = await _showOutboundFormDialog(initial: record);
|
||||
if (updated != null) {
|
||||
setState(() {
|
||||
_selectedRecord = updated;
|
||||
});
|
||||
_selectRecord(updated, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1163,6 +1198,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
(params['order'] ?? '').toLowerCase() == 'asc';
|
||||
final pageSizeParam = int.tryParse(params['page_size'] ?? '');
|
||||
final pageParam = int.tryParse(params['page'] ?? '');
|
||||
final selectedParam = params['selected']?.trim();
|
||||
final selectedNumber = selectedParam == null || selectedParam.isEmpty
|
||||
? null
|
||||
: selectedParam;
|
||||
|
||||
final warehouseId = warehouseIdParam;
|
||||
final warehouseLabel = warehouseParam != null && warehouseParam.isNotEmpty
|
||||
@@ -1232,6 +1271,13 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
_sortAscending = resolvedSortAscending;
|
||||
_pageSize = pageSize;
|
||||
_currentPage = page;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
if (_suppressNextRouteSelection) {
|
||||
_pendingDetailNumber = null;
|
||||
} else {
|
||||
_pendingDetailNumber = selectedNumber;
|
||||
}
|
||||
_suppressNextRouteSelection = false;
|
||||
_refreshSelection();
|
||||
}
|
||||
|
||||
@@ -1265,6 +1311,16 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final selectedNumber = _routeSelectedNumber;
|
||||
if (selectedNumber != null) {
|
||||
for (final record in filtered) {
|
||||
if (record.transactionNumber == selectedNumber) {
|
||||
_selectedRecord = record;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final current = _selectedRecord;
|
||||
if (current != null) {
|
||||
OutboundRecord? matched;
|
||||
@@ -1286,7 +1342,7 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
_selectedRecord = filtered.first;
|
||||
}
|
||||
|
||||
void _updateRoute({int? page, int? pageSize}) {
|
||||
void _updateRoute({int? page, int? pageSize, String? selected}) {
|
||||
if (!mounted) return;
|
||||
final targetPage = page ?? _currentPage;
|
||||
final targetPageSize = pageSize ?? _pageSize;
|
||||
@@ -1336,6 +1392,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
if (targetPageSize != OutboundTableSpec.pageSizeOptions.first) {
|
||||
params['page_size'] = targetPageSize.toString();
|
||||
}
|
||||
final selectedNumber = selected ?? _selectedRecord?.transactionNumber;
|
||||
if (selectedNumber != null && selectedNumber.isNotEmpty) {
|
||||
params['selected'] = selectedNumber;
|
||||
}
|
||||
|
||||
final uri = Uri(
|
||||
path: widget.routeUri.path,
|
||||
@@ -1349,6 +1409,7 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
if (router == null) {
|
||||
return;
|
||||
}
|
||||
_suppressNextRouteSelection = true;
|
||||
router.go(newLocation);
|
||||
}
|
||||
|
||||
@@ -1510,12 +1571,8 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
final transactionTypeController = TextEditingController(
|
||||
text: transactionTypeValue,
|
||||
);
|
||||
final transactionNumberController = TextEditingController(
|
||||
text: initial?.transactionNumber ?? '',
|
||||
);
|
||||
final approvalNumberController = TextEditingController(
|
||||
text: initial?.raw?.approval?.approvalNo ?? '',
|
||||
);
|
||||
final assignedTransactionNo = initial?.transactionNumber;
|
||||
final assignedApprovalNo = initial?.raw?.approval?.approvalNo;
|
||||
final approvalNoteController = TextEditingController();
|
||||
|
||||
final drafts =
|
||||
@@ -1530,8 +1587,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
};
|
||||
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? customerError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
@@ -1561,10 +1616,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
writerController: writerController,
|
||||
writerSelection: writerSelection,
|
||||
requireWriterSelection: initial == null,
|
||||
transactionNumberController: transactionNumberController,
|
||||
transactionNumberRequired: initial == null,
|
||||
approvalNumberController: approvalNumberController,
|
||||
approvalNumberRequired: initial == null,
|
||||
warehouseSelection: warehouseSelection,
|
||||
statusValue: statusValue.value,
|
||||
selectedCustomers: customerSelection
|
||||
@@ -1575,8 +1626,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
);
|
||||
|
||||
writerError = validation.writerError;
|
||||
transactionNumberError = validation.transactionNumberError;
|
||||
approvalNumberError = validation.approvalNumberError;
|
||||
customerError = validation.customerError;
|
||||
warehouseError = validation.warehouseError;
|
||||
statusError = validation.statusError;
|
||||
@@ -1621,8 +1670,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
|
||||
final remarkText = remarkController.text.trim();
|
||||
final remarkValue = remarkText.isEmpty ? null : remarkText;
|
||||
final transactionNoValue = transactionNumberController.text.trim();
|
||||
final approvalNoValue = approvalNumberController.text.trim();
|
||||
final approvalNoteValue = approvalNoteController.text.trim();
|
||||
final transactionId = initial?.id;
|
||||
|
||||
@@ -1715,7 +1762,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '출고 정보가 수정되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'출고 정보가 수정되었습니다. (${updated.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
return;
|
||||
}
|
||||
@@ -1749,7 +1799,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
|
||||
final created = await controller.createTransaction(
|
||||
StockTransactionCreateInput(
|
||||
transactionNo: transactionNoValue,
|
||||
transactionTypeId: transactionTypeLookup.id,
|
||||
transactionStatusId: statusItem.id,
|
||||
warehouseId: warehouseId,
|
||||
@@ -1759,7 +1808,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
lines: createLines,
|
||||
customers: createCustomers,
|
||||
approval: StockTransactionApprovalInput(
|
||||
approvalNo: approvalNoValue,
|
||||
requestedById: createdById,
|
||||
note: approvalNoteValue.isEmpty ? null : approvalNoteValue,
|
||||
),
|
||||
@@ -1770,7 +1818,10 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '출고가 등록되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'출고가 등록되었습니다. (${created.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
} catch (error) {
|
||||
updateSaving(false);
|
||||
@@ -1911,20 +1962,11 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
width: 240,
|
||||
child: SuperportFormField(
|
||||
label: '트랜잭션번호',
|
||||
required: true,
|
||||
errorText: transactionNumberError,
|
||||
child: ShadInput(
|
||||
controller: transactionNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: OUT-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (transactionNumberError != null) {
|
||||
setState(() {
|
||||
transactionNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
assignedTransactionNo ?? '저장 시 자동 생성',
|
||||
style: assignedTransactionNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1932,20 +1974,11 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
width: 240,
|
||||
child: SuperportFormField(
|
||||
label: '결재번호',
|
||||
required: true,
|
||||
errorText: approvalNumberError,
|
||||
child: ShadInput(
|
||||
controller: approvalNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: APP-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (approvalNumberError != null) {
|
||||
setState(() {
|
||||
approvalNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
assignedApprovalNo ?? '저장 시 자동 생성',
|
||||
style: assignedApprovalNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -2163,8 +2196,6 @@ class _OutboundPageState extends State<OutboundPage> {
|
||||
writerController.dispose();
|
||||
remarkController.dispose();
|
||||
transactionTypeController.dispose();
|
||||
transactionNumberController.dispose();
|
||||
approvalNumberController.dispose();
|
||||
approvalNoteController.dispose();
|
||||
processedAt.dispose();
|
||||
|
||||
@@ -2418,10 +2449,6 @@ _OutboundFormValidation _validateOutboundForm({
|
||||
required TextEditingController writerController,
|
||||
required InventoryEmployeeSuggestion? writerSelection,
|
||||
required bool requireWriterSelection,
|
||||
required TextEditingController transactionNumberController,
|
||||
required bool transactionNumberRequired,
|
||||
required TextEditingController approvalNumberController,
|
||||
required bool approvalNumberRequired,
|
||||
required InventoryWarehouseOption? warehouseSelection,
|
||||
required String statusValue,
|
||||
required List<InventoryCustomerOption> selectedCustomers,
|
||||
@@ -2430,8 +2457,6 @@ _OutboundFormValidation _validateOutboundForm({
|
||||
}) {
|
||||
var isValid = true;
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? customerError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
@@ -2448,18 +2473,6 @@ _OutboundFormValidation _validateOutboundForm({
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final transactionNumber = transactionNumberController.text.trim();
|
||||
if (transactionNumberRequired && transactionNumber.isEmpty) {
|
||||
transactionNumberError = '거래번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final approvalNumber = approvalNumberController.text.trim();
|
||||
if (approvalNumberRequired && approvalNumber.isEmpty) {
|
||||
approvalNumberError = '결재번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (warehouseSelection == null) {
|
||||
warehouseError = '창고를 선택하세요.';
|
||||
isValid = false;
|
||||
@@ -2523,8 +2536,6 @@ _OutboundFormValidation _validateOutboundForm({
|
||||
return _OutboundFormValidation(
|
||||
isValid: isValid,
|
||||
writerError: writerError,
|
||||
transactionNumberError: transactionNumberError,
|
||||
approvalNumberError: approvalNumberError,
|
||||
customerError: customerError,
|
||||
warehouseError: warehouseError,
|
||||
statusError: statusError,
|
||||
@@ -2536,8 +2547,6 @@ class _OutboundFormValidation {
|
||||
const _OutboundFormValidation({
|
||||
required this.isValid,
|
||||
this.writerError,
|
||||
this.transactionNumberError,
|
||||
this.approvalNumberError,
|
||||
this.customerError,
|
||||
this.warehouseError,
|
||||
this.statusError,
|
||||
@@ -2546,8 +2555,6 @@ class _OutboundFormValidation {
|
||||
|
||||
final bool isValid;
|
||||
final String? writerError;
|
||||
final String? transactionNumberError;
|
||||
final String? approvalNumberError;
|
||||
final String? customerError;
|
||||
final String? warehouseError;
|
||||
final String? statusError;
|
||||
|
||||
@@ -43,6 +43,15 @@ class OutboundDetailView extends StatelessWidget {
|
||||
value: dateFormatter.format(record.processedAt),
|
||||
),
|
||||
_DetailChip(label: '창고', value: record.warehouse),
|
||||
if (record.warehouseCode != null &&
|
||||
record.warehouseCode!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 코드', value: record.warehouseCode!),
|
||||
if (record.warehouseZipcode != null &&
|
||||
record.warehouseZipcode!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 우편번호', value: record.warehouseZipcode!),
|
||||
if (record.warehouseAddress != null &&
|
||||
record.warehouseAddress!.trim().isNotEmpty)
|
||||
_DetailChip(label: '창고 주소', value: record.warehouseAddress!),
|
||||
_DetailChip(label: '트랜잭션 유형', value: record.transactionType),
|
||||
_DetailChip(label: '상태', value: record.status),
|
||||
_DetailChip(label: '작성자', value: record.writer),
|
||||
|
||||
@@ -12,6 +12,9 @@ class RentalRecord {
|
||||
required this.processedAt,
|
||||
required this.warehouse,
|
||||
this.warehouseId,
|
||||
this.warehouseCode,
|
||||
this.warehouseZipcode,
|
||||
this.warehouseAddress,
|
||||
required this.status,
|
||||
this.statusId,
|
||||
required this.writer,
|
||||
@@ -34,6 +37,9 @@ class RentalRecord {
|
||||
processedAt: transaction.transactionDate,
|
||||
warehouse: transaction.warehouse.name,
|
||||
warehouseId: transaction.warehouse.id,
|
||||
warehouseCode: transaction.warehouse.code,
|
||||
warehouseZipcode: transaction.warehouse.zipcode,
|
||||
warehouseAddress: transaction.warehouse.addressLine,
|
||||
status: transaction.status.name,
|
||||
statusId: transaction.status.id,
|
||||
writer: transaction.createdBy.name,
|
||||
@@ -59,6 +65,9 @@ class RentalRecord {
|
||||
final DateTime processedAt;
|
||||
final String warehouse;
|
||||
final int? warehouseId;
|
||||
final String? warehouseCode;
|
||||
final String? warehouseZipcode;
|
||||
final String? warehouseAddress;
|
||||
final String status;
|
||||
final int? statusId;
|
||||
final String writer;
|
||||
|
||||
@@ -80,6 +80,10 @@ class _RentalPageState extends State<RentalPage> {
|
||||
String? _errorMessage;
|
||||
Set<int> _processingTransactionIds = {};
|
||||
|
||||
String? _routeSelectedNumber;
|
||||
String? _pendingDetailNumber;
|
||||
bool _suppressNextRouteSelection = false;
|
||||
|
||||
late List<String> _statusOptions;
|
||||
final Map<String, LookupItem> _statusLookup = {};
|
||||
LookupItem? _rentTransactionType;
|
||||
@@ -193,6 +197,26 @@ class _RentalPageState extends State<RentalPage> {
|
||||
);
|
||||
_refreshSelection();
|
||||
});
|
||||
final detailNumber = _pendingDetailNumber;
|
||||
if (detailNumber != null) {
|
||||
_pendingDetailNumber = null;
|
||||
RentalRecord? matched;
|
||||
for (final record in _records) {
|
||||
if (record.transactionNumber == detailNumber) {
|
||||
matched = record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched != null) {
|
||||
final target = matched;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(target);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -525,10 +549,7 @@ class _RentalPageState extends State<RentalPage> {
|
||||
),
|
||||
onRowTap: (rowIndex) {
|
||||
final record = visibleRecords[rowIndex];
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_showDetailDialog(record);
|
||||
_selectRecord(record, openDetail: true);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -708,6 +729,24 @@ class _RentalPageState extends State<RentalPage> {
|
||||
return records;
|
||||
}
|
||||
|
||||
void _selectRecord(RentalRecord? record, {bool openDetail = false}) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
final selectedNumber = record?.transactionNumber;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
_updateRoute(selected: selectedNumber);
|
||||
if (openDetail && record != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_showDetailDialog(record);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _buildRecordRow(RentalRecord record) {
|
||||
return [
|
||||
record.number.split('-').last,
|
||||
@@ -805,18 +844,14 @@ class _RentalPageState extends State<RentalPage> {
|
||||
Future<void> _handleCreate() async {
|
||||
final record = await _showRentalFormDialog();
|
||||
if (record != null) {
|
||||
setState(() {
|
||||
_selectedRecord = record;
|
||||
});
|
||||
_selectRecord(record, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleEdit(RentalRecord record) async {
|
||||
final updated = await _showRentalFormDialog(initial: record);
|
||||
if (updated != null) {
|
||||
setState(() {
|
||||
_selectedRecord = updated;
|
||||
});
|
||||
_selectRecord(updated, openDetail: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,6 +1148,10 @@ class _RentalPageState extends State<RentalPage> {
|
||||
(params['order'] ?? '').toLowerCase() == 'asc';
|
||||
final pageSizeParam = int.tryParse(params['page_size'] ?? '');
|
||||
final pageParam = int.tryParse(params['page'] ?? '');
|
||||
final selectedParam = params['selected']?.trim();
|
||||
final selectedNumber = selectedParam == null || selectedParam.isEmpty
|
||||
? null
|
||||
: selectedParam;
|
||||
|
||||
final warehouseId = warehouseIdParam;
|
||||
final warehouseLabel = warehouseParam != null && warehouseParam.isNotEmpty
|
||||
@@ -1166,6 +1205,13 @@ class _RentalPageState extends State<RentalPage> {
|
||||
_sortAscending = resolvedSortAscending;
|
||||
_pageSize = pageSize;
|
||||
_currentPage = page;
|
||||
_routeSelectedNumber = selectedNumber;
|
||||
if (_suppressNextRouteSelection) {
|
||||
_pendingDetailNumber = null;
|
||||
} else {
|
||||
_pendingDetailNumber = selectedNumber;
|
||||
}
|
||||
_suppressNextRouteSelection = false;
|
||||
_refreshSelection();
|
||||
}
|
||||
|
||||
@@ -1181,9 +1227,7 @@ class _RentalPageState extends State<RentalPage> {
|
||||
void _goToPage(int page) {
|
||||
final totalItems = _result?.total ?? _filteredRecords.length;
|
||||
final totalPages = _calculateTotalPages(totalItems);
|
||||
final int target = page < 1
|
||||
? 1
|
||||
: (page > totalPages ? totalPages : page);
|
||||
final int target = page < 1 ? 1 : (page > totalPages ? totalPages : page);
|
||||
if (target == _currentPage) {
|
||||
return;
|
||||
}
|
||||
@@ -1201,6 +1245,16 @@ class _RentalPageState extends State<RentalPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
final selectedNumber = _routeSelectedNumber;
|
||||
if (selectedNumber != null) {
|
||||
for (final record in filtered) {
|
||||
if (record.transactionNumber == selectedNumber) {
|
||||
_selectedRecord = record;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final current = _selectedRecord;
|
||||
if (current != null) {
|
||||
RentalRecord? matched;
|
||||
@@ -1222,7 +1276,7 @@ class _RentalPageState extends State<RentalPage> {
|
||||
_selectedRecord = filtered.first;
|
||||
}
|
||||
|
||||
void _updateRoute({int? page, int? pageSize}) {
|
||||
void _updateRoute({int? page, int? pageSize, String? selected}) {
|
||||
if (!mounted) return;
|
||||
final targetPage = page ?? _currentPage;
|
||||
final targetPageSize = pageSize ?? _pageSize;
|
||||
@@ -1276,6 +1330,10 @@ class _RentalPageState extends State<RentalPage> {
|
||||
if (targetPageSize != RentalTableSpec.pageSizeOptions.first) {
|
||||
params['page_size'] = targetPageSize.toString();
|
||||
}
|
||||
final selectedNumber = selected ?? _selectedRecord?.transactionNumber;
|
||||
if (selectedNumber != null && selectedNumber.isNotEmpty) {
|
||||
params['selected'] = selectedNumber;
|
||||
}
|
||||
|
||||
final uri = Uri(
|
||||
path: widget.routeUri.path,
|
||||
@@ -1289,6 +1347,7 @@ class _RentalPageState extends State<RentalPage> {
|
||||
if (router == null) {
|
||||
return;
|
||||
}
|
||||
_suppressNextRouteSelection = true;
|
||||
router.go(newLocation);
|
||||
}
|
||||
|
||||
@@ -1488,12 +1547,8 @@ class _RentalPageState extends State<RentalPage> {
|
||||
final transactionTypeController = TextEditingController(
|
||||
text: _transactionTypeForRental(rentalTypeValue.value),
|
||||
);
|
||||
final transactionNumberController = TextEditingController(
|
||||
text: initial?.transactionNumber ?? '',
|
||||
);
|
||||
final approvalNumberController = TextEditingController(
|
||||
text: initial?.raw?.approval?.approvalNo ?? '',
|
||||
);
|
||||
final assignedTransactionNo = initial?.transactionNumber;
|
||||
final assignedApprovalNo = initial?.raw?.approval?.approvalNo;
|
||||
final approvalNoteController = TextEditingController();
|
||||
|
||||
final drafts =
|
||||
@@ -1505,8 +1560,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
|
||||
RentalRecord? result;
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? customerError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
@@ -1538,10 +1591,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
writerController: writerController,
|
||||
writerSelection: writerSelection,
|
||||
requireWriterSelection: initial == null,
|
||||
transactionNumberController: transactionNumberController,
|
||||
transactionNumberRequired: initial == null,
|
||||
approvalNumberController: approvalNumberController,
|
||||
approvalNumberRequired: initial == null,
|
||||
warehouseSelection: warehouseSelection,
|
||||
statusValue: statusValue.value,
|
||||
selectedCustomers: customerSelection
|
||||
@@ -1552,8 +1601,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
);
|
||||
|
||||
writerError = validation.writerError;
|
||||
transactionNumberError = validation.transactionNumberError;
|
||||
approvalNumberError = validation.approvalNumberError;
|
||||
customerError = validation.customerError;
|
||||
warehouseError = validation.warehouseError;
|
||||
statusError = validation.statusError;
|
||||
@@ -1599,8 +1646,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
|
||||
final remarkText = remarkController.text.trim();
|
||||
final remarkValue = remarkText.isEmpty ? null : remarkText;
|
||||
final transactionNoValue = transactionNumberController.text.trim();
|
||||
final approvalNoValue = approvalNumberController.text.trim();
|
||||
final approvalNoteValue = approvalNoteController.text.trim();
|
||||
final transactionId = initial?.id;
|
||||
final initialRecord = initial;
|
||||
@@ -1693,7 +1738,10 @@ class _RentalPageState extends State<RentalPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '대여 정보가 수정되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'대여 정보가 수정되었습니다. (${updated.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
return;
|
||||
}
|
||||
@@ -1728,7 +1776,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
final transactionTypeId = selectedLookup.id;
|
||||
final created = await controller.createTransaction(
|
||||
StockTransactionCreateInput(
|
||||
transactionNo: transactionNoValue,
|
||||
transactionTypeId: transactionTypeId,
|
||||
transactionStatusId: statusItem.id,
|
||||
warehouseId: warehouseId,
|
||||
@@ -1739,7 +1786,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
lines: createLines,
|
||||
customers: createCustomers,
|
||||
approval: StockTransactionApprovalInput(
|
||||
approvalNo: approvalNoValue,
|
||||
requestedById: createdById,
|
||||
note: approvalNoteValue.isEmpty ? null : approvalNoteValue,
|
||||
),
|
||||
@@ -1750,7 +1796,10 @@ class _RentalPageState extends State<RentalPage> {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
SuperportToast.success(context, '대여가 등록되었습니다.');
|
||||
SuperportToast.success(
|
||||
context,
|
||||
'대여가 등록되었습니다. (${created.transactionNumber})',
|
||||
);
|
||||
navigator.pop();
|
||||
} catch (error) {
|
||||
updateSaving(false);
|
||||
@@ -1918,70 +1967,26 @@ class _RentalPageState extends State<RentalPage> {
|
||||
),
|
||||
SizedBox(
|
||||
width: 240,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_FormFieldLabel(
|
||||
label: '트랜잭션번호',
|
||||
child: ShadInput(
|
||||
controller: transactionNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: RENT-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (transactionNumberError != null) {
|
||||
setState(() {
|
||||
transactionNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
if (transactionNumberError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
transactionNumberError!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: _FormFieldLabel(
|
||||
label: '트랜잭션번호',
|
||||
child: Text(
|
||||
assignedTransactionNo ?? '저장 시 자동 생성',
|
||||
style: assignedTransactionNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 240,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_FormFieldLabel(
|
||||
label: '결재번호',
|
||||
child: ShadInput(
|
||||
controller: approvalNumberController,
|
||||
readOnly: initial != null,
|
||||
enabled: initial == null,
|
||||
placeholder: const Text('예: APP-2024-0001'),
|
||||
onChanged: (_) {
|
||||
if (approvalNumberError != null) {
|
||||
setState(() {
|
||||
approvalNumberError = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
if (approvalNumberError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
approvalNumberError!,
|
||||
style: theme.textTheme.small.copyWith(
|
||||
color: theme.colorScheme.destructive,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: _FormFieldLabel(
|
||||
label: '결재번호',
|
||||
child: Text(
|
||||
assignedApprovalNo ?? '저장 시 자동 생성',
|
||||
style: assignedApprovalNo == null
|
||||
? theme.textTheme.muted
|
||||
: theme.textTheme.p,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
@@ -2247,8 +2252,6 @@ class _RentalPageState extends State<RentalPage> {
|
||||
writerController.dispose();
|
||||
remarkController.dispose();
|
||||
transactionTypeController.dispose();
|
||||
transactionNumberController.dispose();
|
||||
approvalNumberController.dispose();
|
||||
approvalNoteController.dispose();
|
||||
processedAt.dispose();
|
||||
returnDue.dispose();
|
||||
@@ -2523,10 +2526,6 @@ _RentalFormValidation _validateRentalForm({
|
||||
required TextEditingController writerController,
|
||||
required InventoryEmployeeSuggestion? writerSelection,
|
||||
required bool requireWriterSelection,
|
||||
required TextEditingController transactionNumberController,
|
||||
required bool transactionNumberRequired,
|
||||
required TextEditingController approvalNumberController,
|
||||
required bool approvalNumberRequired,
|
||||
required InventoryWarehouseOption? warehouseSelection,
|
||||
required String statusValue,
|
||||
required List<InventoryCustomerOption> selectedCustomers,
|
||||
@@ -2535,8 +2534,6 @@ _RentalFormValidation _validateRentalForm({
|
||||
}) {
|
||||
var isValid = true;
|
||||
String? writerError;
|
||||
String? transactionNumberError;
|
||||
String? approvalNumberError;
|
||||
String? customerError;
|
||||
String? warehouseError;
|
||||
String? statusError;
|
||||
@@ -2553,18 +2550,6 @@ _RentalFormValidation _validateRentalForm({
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final transactionNumber = transactionNumberController.text.trim();
|
||||
if (transactionNumberRequired && transactionNumber.isEmpty) {
|
||||
transactionNumberError = '거래번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
final approvalNumber = approvalNumberController.text.trim();
|
||||
if (approvalNumberRequired && approvalNumber.isEmpty) {
|
||||
approvalNumberError = '결재번호를 입력하세요.';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (warehouseSelection == null) {
|
||||
warehouseError = '창고를 선택하세요.';
|
||||
isValid = false;
|
||||
@@ -2628,8 +2613,6 @@ _RentalFormValidation _validateRentalForm({
|
||||
return _RentalFormValidation(
|
||||
isValid: isValid,
|
||||
writerError: writerError,
|
||||
transactionNumberError: transactionNumberError,
|
||||
approvalNumberError: approvalNumberError,
|
||||
customerError: customerError,
|
||||
warehouseError: warehouseError,
|
||||
statusError: statusError,
|
||||
@@ -2641,8 +2624,6 @@ class _RentalFormValidation {
|
||||
const _RentalFormValidation({
|
||||
required this.isValid,
|
||||
this.writerError,
|
||||
this.transactionNumberError,
|
||||
this.approvalNumberError,
|
||||
this.customerError,
|
||||
this.warehouseError,
|
||||
this.statusError,
|
||||
@@ -2651,8 +2632,6 @@ class _RentalFormValidation {
|
||||
|
||||
final bool isValid;
|
||||
final String? writerError;
|
||||
final String? transactionNumberError;
|
||||
final String? approvalNumberError;
|
||||
final String? customerError;
|
||||
final String? warehouseError;
|
||||
final String? statusError;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/// 재고 트랜잭션 생성 입력 모델.
|
||||
class StockTransactionCreateInput {
|
||||
StockTransactionCreateInput({
|
||||
this.transactionNo,
|
||||
required this.transactionTypeId,
|
||||
required this.transactionStatusId,
|
||||
required this.warehouseId,
|
||||
@@ -14,7 +13,6 @@ class StockTransactionCreateInput {
|
||||
this.approval,
|
||||
});
|
||||
|
||||
final String? transactionNo;
|
||||
final int transactionTypeId;
|
||||
final int transactionStatusId;
|
||||
final int warehouseId;
|
||||
@@ -29,8 +27,6 @@ class StockTransactionCreateInput {
|
||||
Map<String, dynamic> toPayload() {
|
||||
final sanitizedNote = note?.trim();
|
||||
return {
|
||||
if (transactionNo != null && transactionNo!.trim().isNotEmpty)
|
||||
'transaction_no': transactionNo,
|
||||
'transaction_type_id': transactionTypeId,
|
||||
'transaction_status_id': transactionStatusId,
|
||||
'warehouse_id': warehouseId,
|
||||
@@ -213,23 +209,21 @@ class StockTransactionListFilter {
|
||||
/// 재고 트랜잭션 생성 시 결재(Approval) 정보를 담는 입력 모델.
|
||||
class StockTransactionApprovalInput {
|
||||
StockTransactionApprovalInput({
|
||||
required this.approvalNo,
|
||||
required this.requestedById,
|
||||
this.approvalStatusId,
|
||||
this.note,
|
||||
});
|
||||
|
||||
final String approvalNo;
|
||||
final int requestedById;
|
||||
final int? approvalStatusId;
|
||||
final String? note;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final trimmedNote = note?.trim();
|
||||
return {
|
||||
'approval_no': approvalNo,
|
||||
if (approvalStatusId != null) 'approval_status_id': approvalStatusId,
|
||||
'requested_by_id': requestedById,
|
||||
'note': note?.trim(),
|
||||
if (trimmedNote != null && trimmedNote.isNotEmpty) 'note': trimmedNote,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@ class CustomerRepositoryRemote implements CustomerRepository {
|
||||
query: {if (includeZipcode) 'include': 'zipcode'},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return CustomerDto.fromJson(data).toEntity();
|
||||
return CustomerDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 고객을 생성한다.
|
||||
@@ -59,8 +58,7 @@ class CustomerRepositoryRemote implements CustomerRepository {
|
||||
data: customerInputToJson(input),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return CustomerDto.fromJson(data).toEntity();
|
||||
return CustomerDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 고객 정보를 수정한다.
|
||||
@@ -72,8 +70,7 @@ class CustomerRepositoryRemote implements CustomerRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return CustomerDto.fromJson(data).toEntity();
|
||||
return CustomerDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 고객을 삭제한다.
|
||||
@@ -89,7 +86,6 @@ class CustomerRepositoryRemote implements CustomerRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return CustomerDto.fromJson(data).toEntity();
|
||||
return CustomerDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class _CustomerEnabledPageState extends State<_CustomerEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -56,8 +56,7 @@ class GroupRepositoryRemote implements GroupRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupDto.fromJson(data).toEntity();
|
||||
return GroupDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 그룹 정보를 수정한다.
|
||||
@@ -69,8 +68,7 @@ class GroupRepositoryRemote implements GroupRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupDto.fromJson(data).toEntity();
|
||||
return GroupDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 그룹을 삭제한다.
|
||||
@@ -86,7 +84,6 @@ class GroupRepositoryRemote implements GroupRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupDto.fromJson(data).toEntity();
|
||||
return GroupDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class _GroupEnabledPageState extends State<_GroupEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -34,7 +34,7 @@ class GroupPermissionRepositoryRemote implements GroupPermissionRepository {
|
||||
if (groupId != null) 'group_id': groupId,
|
||||
if (menuId != null) 'menu_id': menuId,
|
||||
if (isActive != null) 'active': isActive,
|
||||
if (includeDeleted) 'deleted': true,
|
||||
if (includeDeleted) 'include_deleted': true,
|
||||
'include': 'group,menu',
|
||||
},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
@@ -50,8 +50,7 @@ class GroupPermissionRepositoryRemote implements GroupPermissionRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupPermissionDto.fromJson(data).toEntity();
|
||||
return GroupPermissionDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 그룹 권한을 수정한다.
|
||||
@@ -63,8 +62,7 @@ class GroupPermissionRepositoryRemote implements GroupPermissionRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupPermissionDto.fromJson(data).toEntity();
|
||||
return GroupPermissionDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 그룹 권한을 삭제한다.
|
||||
@@ -80,7 +78,6 @@ class GroupPermissionRepositoryRemote implements GroupPermissionRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return GroupPermissionDto.fromJson(data).toEntity();
|
||||
return GroupPermissionDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ class _GroupPermissionEnabledPageState
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -856,6 +856,7 @@ class _PermissionTable extends StatelessWidget {
|
||||
'ID',
|
||||
'그룹명',
|
||||
'메뉴명',
|
||||
'라우트 경로',
|
||||
'생성',
|
||||
'조회',
|
||||
'수정',
|
||||
@@ -872,6 +873,7 @@ class _PermissionTable extends StatelessWidget {
|
||||
permission.id?.toString() ?? '-',
|
||||
permission.group.groupName,
|
||||
permission.menu.menuName,
|
||||
permission.menu.path ?? '-',
|
||||
permission.canCreate ? 'Y' : '-',
|
||||
permission.canRead ? 'Y' : '-',
|
||||
permission.canUpdate ? 'Y' : '-',
|
||||
|
||||
@@ -49,8 +49,7 @@ class MenuRepositoryRemote implements MenuRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return MenuDto.fromJson(data).toEntity();
|
||||
return MenuDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 메뉴 정보를 수정한다.
|
||||
@@ -62,8 +61,7 @@ class MenuRepositoryRemote implements MenuRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return MenuDto.fromJson(data).toEntity();
|
||||
return MenuDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 메뉴를 삭제한다.
|
||||
@@ -79,7 +77,6 @@ class MenuRepositoryRemote implements MenuRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return MenuDto.fromJson(data).toEntity();
|
||||
return MenuDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ class _MenuEnabledPageState extends State<_MenuEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -49,8 +49,7 @@ class ProductRepositoryRemote implements ProductRepository {
|
||||
data: productInputToJson(input),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ProductDto.fromJson(data).toEntity();
|
||||
return ProductDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 제품 정보를 수정한다.
|
||||
@@ -62,8 +61,7 @@ class ProductRepositoryRemote implements ProductRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ProductDto.fromJson(data).toEntity();
|
||||
return ProductDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 제품을 삭제한다.
|
||||
@@ -79,7 +77,6 @@ class ProductRepositoryRemote implements ProductRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return ProductDto.fromJson(data).toEntity();
|
||||
return ProductDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ class _ProductEnabledPageState extends State<_ProductEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -47,8 +47,7 @@ class UserRepositoryRemote implements UserRepository {
|
||||
data: input.toPayload(),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return UserDto.fromJson(data).toEntity();
|
||||
return UserDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 사용자 정보를 수정한다.
|
||||
@@ -60,8 +59,7 @@ class UserRepositoryRemote implements UserRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return UserDto.fromJson(data).toEntity();
|
||||
return UserDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 사용자를 삭제한다.
|
||||
@@ -77,7 +75,6 @@ class UserRepositoryRemote implements UserRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return UserDto.fromJson(data).toEntity();
|
||||
return UserDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class _UserEnabledPageState extends State<_UserEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
@@ -44,8 +44,7 @@ class VendorRepositoryRemote implements VendorRepository {
|
||||
data: vendorInputToJson(input),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return VendorDto.fromJson(data).toEntity();
|
||||
return VendorDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -56,8 +55,7 @@ class VendorRepositoryRemote implements VendorRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return VendorDto.fromJson(data).toEntity();
|
||||
return VendorDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -71,7 +69,6 @@ class VendorRepositoryRemote implements VendorRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return VendorDto.fromJson(data).toEntity();
|
||||
return VendorDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
@@ -365,7 +365,7 @@ class _VendorEnabledPageState extends State<_VendorEnabledPage> {
|
||||
final result = _controller.result;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
if (page < 1) {
|
||||
page = 1;
|
||||
} else if (page > totalPages) {
|
||||
|
||||
@@ -46,8 +46,7 @@ class WarehouseRepositoryRemote implements WarehouseRepository {
|
||||
data: warehouseInputToJson(input),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return WarehouseDto.fromJson(data).toEntity();
|
||||
return WarehouseDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 창고 정보를 수정한다.
|
||||
@@ -59,8 +58,7 @@ class WarehouseRepositoryRemote implements WarehouseRepository {
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return WarehouseDto.fromJson(data).toEntity();
|
||||
return WarehouseDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
|
||||
/// 창고를 삭제한다.
|
||||
@@ -76,7 +74,6 @@ class WarehouseRepositoryRemote implements WarehouseRepository {
|
||||
'$_basePath/$id/restore',
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final data = (response.data?['data'] as Map<String, dynamic>?) ?? {};
|
||||
return WarehouseDto.fromJson(data).toEntity();
|
||||
return WarehouseDto.fromJson(_api.unwrapAsMap(response)).toEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class _WarehouseEnabledPageState extends State<_WarehouseEnabledPage> {
|
||||
final currentPage = result?.page ?? 1;
|
||||
final totalPages = result == null || result.pageSize == 0
|
||||
? 1
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999) as int;
|
||||
: (result.total / result.pageSize).ceil().clamp(1, 9999);
|
||||
final hasNext = result == null
|
||||
? false
|
||||
: (result.page * result.pageSize) < result.total;
|
||||
|
||||
Reference in New Issue
Block a user