feat(android): add attachment boxing FJ- scan and submit
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,11 @@ extension _BoxingScanPart on _BoxingPageState {
|
||||
|
||||
final parsed = CodeParser.parse(result.barcode);
|
||||
|
||||
if (parsed.type == CodeType.attachmentCode) {
|
||||
this._handleAttachmentBoxScan(parsed.value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed.type != CodeType.zongpaiNo) {
|
||||
_feedbackService.trigger(FeedbackEvent.scanInvalid);
|
||||
this._showStatusOverride(
|
||||
@@ -92,6 +97,119 @@ extension _BoxingScanPart on _BoxingPageState {
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _handleAttachmentBoxScan(String zongpai) async {
|
||||
if (_isAutoProcessing || _crossPaichaPending) return;
|
||||
|
||||
// 1. Fetch categories
|
||||
final catAction = await _apiActions.fetchAttachmentCategories();
|
||||
if (!mounted) return;
|
||||
if (!catAction.success) {
|
||||
_feedbackService.trigger(FeedbackEvent.scanInvalid);
|
||||
_showStatusOverride(
|
||||
catAction.errorMessage ?? '加载附件类型失败',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Pick category
|
||||
final picked = await showBoxingAttachmentCategoryPicker(
|
||||
context: context,
|
||||
categories: catAction.data!.categories,
|
||||
);
|
||||
if (picked == null || !mounted) return;
|
||||
|
||||
setState(() {
|
||||
_attachmentCategoryId = picked.id;
|
||||
_attachmentCategoryName = picked.name;
|
||||
});
|
||||
|
||||
// 3. Fetch box info (server-extended: includes attachment_summary)
|
||||
final action = await _apiActions.fetchBoxInfo(zongpai);
|
||||
if (!mounted) return;
|
||||
|
||||
if (!action.success) {
|
||||
_feedbackService.trigger(switch (action.errorKind) {
|
||||
BoxingActionErrorKind.network => FeedbackEvent.networkError,
|
||||
BoxingActionErrorKind.missingApiUrl => FeedbackEvent.submitFailure,
|
||||
_ => FeedbackEvent.scanInvalid,
|
||||
});
|
||||
_showStatusOverride(
|
||||
action.errorMessage ?? '查询失败',
|
||||
_dotForActionError(action.errorKind),
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final result = action.data!;
|
||||
final totalQuantity = result.quantity ?? 0;
|
||||
|
||||
// 4. Derive attachment qty context from the box-info attachment_summary
|
||||
// or from a separate status call
|
||||
final baseUrl = await _baseUrl();
|
||||
int expectedQty = totalQuantity;
|
||||
int boxedQty = 0;
|
||||
|
||||
if (baseUrl != null) {
|
||||
final statusResult = await _apiService.fetchAttachmentStatus(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpai,
|
||||
);
|
||||
if (statusResult.success) {
|
||||
for (final item in statusResult.items) {
|
||||
if (item.categoryId == _attachmentCategoryId) {
|
||||
expectedQty = item.expectedQty;
|
||||
boxedQty = item.boxedQty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final remaining = expectedQty - boxedQty;
|
||||
final alreadyCompleted = remaining <= 0;
|
||||
|
||||
_feedbackService.trigger(
|
||||
alreadyCompleted
|
||||
? FeedbackEvent.alreadyCompleted
|
||||
: FeedbackEvent.scanValid,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_zongpaiNo = zongpai;
|
||||
_paichanNo = result.paichanNo;
|
||||
_workOrderNo = result.workOrderNo;
|
||||
_erpQuantity = result.quantity;
|
||||
_currentZongpaiBoxes = result.currentZongpaiBoxes;
|
||||
_existingBoxes = result.existingBoxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
_attachmentExpectedQty = expectedQty;
|
||||
_attachmentBoxedQty = boxedQty;
|
||||
_phase = BoxingPhase.scanned;
|
||||
_isDuplicateBoxNo = false;
|
||||
_editingBox = null;
|
||||
_completedJumpBoxNo = null;
|
||||
_statusOverrideText = null;
|
||||
});
|
||||
|
||||
// Auto-fill: box_no = maxBoxNo+1, quantity = remaining
|
||||
if (alreadyCompleted) {
|
||||
_boxNoController.clear();
|
||||
_quantityController.clear();
|
||||
_statusOverrideText = '该类型附件已全部装箱完毕';
|
||||
_statusOverrideDot = StatusDotColor.red;
|
||||
return;
|
||||
}
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
_quantityController.text = remaining.toString();
|
||||
_quantityController.selection = TextSelection(
|
||||
baseOffset: 0,
|
||||
extentOffset: _quantityController.text.length,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _processAutoScanCodes(List<String> codes) async {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
|
||||
@@ -6,6 +6,53 @@ extension _BoxingSubmitPart on _BoxingPageState {
|
||||
Future<bool> _submit() async {
|
||||
if (!_canSubmit) return false;
|
||||
|
||||
final attachCatId = _attachmentCategoryId;
|
||||
if (attachCatId != null) {
|
||||
// Attachment boxing submit
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.saveAttachmentBox(
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
categoryId: attachCatId,
|
||||
boxNo: int.parse(_boxNoController.text),
|
||||
quantity: int.parse(_quantityController.text),
|
||||
);
|
||||
if (!mounted) return false;
|
||||
setState(() => _isSubmitting = false);
|
||||
|
||||
if (action.success) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
// Reset to waiting for next scan
|
||||
setState(() {
|
||||
_zongpaiNo = null;
|
||||
_phase = BoxingPhase.waiting;
|
||||
_attachmentCategoryId = null;
|
||||
_attachmentCategoryName = null;
|
||||
});
|
||||
_showStatusOverride(
|
||||
'附件装箱成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
_requestFocus();
|
||||
return true;
|
||||
} else if (action.errorKind == BoxingActionErrorKind.duplicate) {
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateBoxNo);
|
||||
_showStatusOverride(
|
||||
action.errorMessage ?? '该箱号已存在同类型附件',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
} else {
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
action.errorMessage ?? '提交失败',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final boxNo = int.parse(_boxNoController.text);
|
||||
final quantity = int.parse(_quantityController.text);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user