318 lines
10 KiB
Dart
318 lines
10 KiB
Dart
// ignore_for_file: invalid_use_of_protected_member
|
|
|
|
part of '../../registration_page.dart';
|
|
|
|
extension _RegistrationSubmitPart on _RegistrationPageState {
|
|
Future<void> _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 already has a location
|
|
if (_isTransitTarget) {
|
|
if (_hasAnyLocatedInScanned()) {
|
|
final onShelfItems = _findOnShelfItemsInScanned();
|
|
final transferredItems = _findTransferredItemsInScanned();
|
|
setState(() => _isSubmitting = false);
|
|
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
|
_showLocationConflictDialog(
|
|
onShelfItems: onShelfItems,
|
|
transferredItems: transferredItems,
|
|
);
|
|
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) {
|
|
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<bool> _ensureBatchSamePaicha(String baseUrl) async {
|
|
final paichaByCode = <String, String>{};
|
|
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<void> _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) {
|
|
_locationCode = null;
|
|
_locationType = null;
|
|
}
|
|
});
|
|
final msg = result.isOffShelfSuccess
|
|
? '下架成功'
|
|
: (_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<void> _submitBatch(String baseUrl) async {
|
|
int successCount = 0;
|
|
int offShelfCount = 0;
|
|
final failed = <String>[];
|
|
final toRemove = <String>[];
|
|
final transitCodes = <String>[];
|
|
|
|
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<void> _navigateToBoxing({
|
|
required BoxingMode mode,
|
|
required List<String> codes,
|
|
required List<String> 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<void>.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),
|
|
);
|
|
}
|
|
}
|
|
}
|