// ignore_for_file: invalid_use_of_protected_member part of '../../registration_page.dart'; extension _RegistrationSubmitPart on _RegistrationPageState { Future _submit() async { if (!_canSubmit) return; final baseUrl = await _baseUrl(); if (baseUrl == null) { _feedbackService.trigger(FeedbackEvent.submitFailure); _showStatusOverride( '未配置 API 地址,请前往设置', StatusDotColor.red, const Duration(seconds: 2), ); return; } setState(() => _isSubmitting = true); // For transit target: block if any scanned item is already transferred // (on_shelf items are allowed — transit registration acts as off-shelf) if (_isTransitTarget) { final transferredItems = _findTransferredItemsInScanned(); if (transferredItems.isNotEmpty) { setState(() => _isSubmitting = false); _feedbackService.trigger(FeedbackEvent.submitFailure); _showLocationConflictDialog( onShelfItems: [], transferredItems: transferredItems, ); return; } } // For temp storage target: block if any scanned item is transferred or temp_stored // (on_shelf items are allowed — temp storage acts as shelf change) if (_isTempStorageTarget) { final transferredItems = _findTransferredItemsInScanned(); final tempStoredItems = _findTempStoredItemsInScanned(); if (transferredItems.isNotEmpty || tempStoredItems.isNotEmpty) { setState(() => _isSubmitting = false); _feedbackService.trigger(FeedbackEvent.submitFailure); _showLocationConflictDialog( onShelfItems: [], transferredItems: transferredItems, tempStoredItems: tempStoredItems, ); return; } } if (_mode == RegistrationMode.multiCode && _zongpaiNos.length > 1) { if (_isTransitTarget && !await _ensureBatchSamePaicha(baseUrl)) { if (!mounted) return; setState(() => _isSubmitting = false); _feedbackService.trigger(FeedbackEvent.submitFailure); _showStatusOverride( batchPaichaMismatchMessage, StatusDotColor.red, const Duration(seconds: 2), ); return; } // Pre-check: block entire batch if any item already has a location binding if (!_isTransitTarget && !_isTempStorageTarget) { final onShelfItems = _findOnShelfItemsInScanned(); final transferredItems = _findTransferredItemsInScanned(); if (onShelfItems.isNotEmpty || transferredItems.isNotEmpty) { setState(() => _isSubmitting = false); _feedbackService.trigger(FeedbackEvent.duplicateError); _showLocationConflictDialog( onShelfItems: onShelfItems, transferredItems: transferredItems, ); return; } } await _submitBatch(baseUrl); } else { await _submitOne(baseUrl, _zongpaiNos.first); } } Future _ensureBatchSamePaicha(String baseUrl) async { final paichaByCode = {}; final cachedOverview = _overview; if (cachedOverview?.success == true && cachedOverview!.paichaNo != null) { for (final item in cachedOverview.items) { paichaByCode[item.zongpaiNo] = cachedOverview.paichaNo!; } } for (final zongpaiNo in _zongpaiNos) { if (paichaByCode.containsKey(zongpaiNo)) continue; final result = await _apiService.fetchPaichaOverview( baseUrl: baseUrl, zongpaiNo: zongpaiNo, ); if (!result.success || result.paichaNo == null) { return false; } for (final item in result.items) { paichaByCode[item.zongpaiNo] = result.paichaNo!; } } return allSamePaicha(_zongpaiNos.map((code) => paichaByCode[code])); } Future _submitOne(String baseUrl, String zongpaiNo) async { final result = await _apiService.registerLocation( baseUrl: baseUrl, zongpaiNo: zongpaiNo, locationCode: _locationCode!, ); if (!mounted) return; setState(() => _isSubmitting = false); if (result.success) { _feedbackService.trigger(FeedbackEvent.submitSuccess); if (_isTransitTarget) { setState(() => _isSubmitting = false); await _navigateToBoxing( mode: BoxingMode.singleCode, codes: [zongpaiNo], codesToClear: [zongpaiNo], clearLocation: _mode == RegistrationMode.singleCode, ); return; } setState(() { _zongpaiNos.remove(zongpaiNo); if (_mode == RegistrationMode.singleCode && !_isTempStorageTarget) { _locationCode = null; _locationType = null; } }); final msg = result.isOffShelfSuccess ? '下架成功' : _isTempStorageTarget ? '变更货架成功' : (_mode == RegistrationMode.multiCode ? '多码上架模式,请扫描下一张执行卡' : '上架成功'); _showStatusOverride( msg, StatusDotColor.green, const Duration(milliseconds: 1500), ); await _refreshCurrentOverview(); } else if (result.isDuplicate) { _feedbackService.trigger(FeedbackEvent.duplicateError); _showDuplicateDialog(zongpaiNo, result.duplicateInfo); } else if (result.isAlreadyOffShelf) { _feedbackService.trigger(FeedbackEvent.submitFailure); _showStatusOverride( result.errorMessage ?? '该总排号已下架至转运区域,不可重新上架', StatusDotColor.red, const Duration(seconds: 2), ); } else { final isNetwork = result.errorMessage == '网络异常,请检查网络连接'; _feedbackService.trigger( isNetwork ? FeedbackEvent.networkError : FeedbackEvent.submitFailure, ); _showStatusOverride( result.errorMessage ?? '提交失败', isNetwork ? StatusDotColor.yellow : StatusDotColor.red, const Duration(seconds: 2), ); } } Future _submitBatch(String baseUrl) async { int successCount = 0; int offShelfCount = 0; final failed = []; final toRemove = []; final transitCodes = []; for (final zongpaiNo in List.of(_zongpaiNos)) { final result = await _apiService.registerLocation( baseUrl: baseUrl, zongpaiNo: zongpaiNo, locationCode: _locationCode!, ); if (result.success) { successCount++; if (result.isOffShelfSuccess) { offShelfCount++; } toRemove.add(zongpaiNo); if (_isTransitTarget) { transitCodes.add(zongpaiNo); } } else { failed.add(zongpaiNo); if (result.isDuplicate) { if (_isTransitTarget && transitCodes.isNotEmpty) { setState(() { _zongpaiNos.removeWhere((item) => toRemove.contains(item)); _isSubmitting = false; }); await _navigateToBoxing( mode: BoxingMode.multiCode, codes: transitCodes, codesToClear: toRemove, clearLocation: false, returnMessage: '成功 $successCount 条,失败 ${failed.length} 条', ); return; } setState(() { _zongpaiNos.removeWhere((item) => toRemove.contains(item)); _isSubmitting = false; }); _feedbackService.trigger(FeedbackEvent.duplicateError); _showDuplicateDialog(zongpaiNo, result.duplicateInfo); return; } if (result.isAlreadyOffShelf) { if (_isTransitTarget && transitCodes.isNotEmpty) { setState(() { _zongpaiNos.removeWhere((item) => toRemove.contains(item)); _isSubmitting = false; }); await _navigateToBoxing( mode: BoxingMode.multiCode, codes: transitCodes, codesToClear: toRemove, clearLocation: false, returnMessage: '成功 $successCount 条,失败 ${failed.length} 条', ); return; } setState(() { _zongpaiNos.removeWhere((item) => toRemove.contains(item)); _isSubmitting = false; }); _feedbackService.trigger(FeedbackEvent.submitFailure); _showStatusOverride( result.errorMessage ?? '该总排号已下架至转运区域,不可重新上架', StatusDotColor.red, const Duration(seconds: 2), ); return; } } } if (!mounted) return; setState(() { _zongpaiNos.removeWhere((item) => toRemove.contains(item)); _isSubmitting = false; }); if (_isTransitTarget && transitCodes.isNotEmpty) { await _navigateToBoxing( mode: BoxingMode.multiCode, codes: transitCodes, codesToClear: toRemove, clearLocation: failed.isEmpty, returnMessage: failed.isEmpty ? null : '成功 $successCount 条,失败 ${failed.length} 条', ); return; } if (failed.isEmpty) { _feedbackService.trigger(FeedbackEvent.submitSuccess); final msg = offShelfCount == successCount ? '批量下架成功($successCount 条)' : '批量上架成功($successCount 条)'; _showStatusOverride( msg, StatusDotColor.green, const Duration(milliseconds: 1500), ); await _refreshCurrentOverview(); } else { _feedbackService.trigger(FeedbackEvent.submitFailure); _showStatusOverride( '成功 $successCount 条,失败 ${failed.length} 条', StatusDotColor.red, const Duration(seconds: 2), ); } } Future _navigateToBoxing({ required BoxingMode mode, required List codes, required List codesToClear, required bool clearLocation, String? returnMessage, }) async { await _refreshCurrentOverview(); if (!mounted) return; final navigator = Navigator.of(context); await _stopScanListening(); await navigator.push( MaterialPageRoute( builder: (_) => BoxingPage( arguments: BoxingPageArguments( initialMode: mode, autoScanCodes: codes, ), ), ), ); if (!mounted) return; await Future.delayed(const Duration(milliseconds: 120)); if (!mounted) return; _restartScanListening(); setState(() { _zongpaiNos.removeWhere((item) => codesToClear.contains(item)); if (clearLocation) { _locationCode = null; _locationType = null; } }); await _refreshCurrentOverview(); if (!mounted) return; if (returnMessage != null) { _showStatusOverride( returnMessage, StatusDotColor.red, const Duration(seconds: 2), ); } } }