Files
pad_scanner/lib/pages/boxing/parts/boxing_submit_part.dart
Misaka 9dc714dbeb feat: extend boxing page for accessory boxing support
Add pending accessories section to both single-code and multi-code boxing
modes. Accessories display with orange styling and a "加入本箱" button that
calls saveBoxRecord with item_type=accessory. Extend API service with
PendingAccessory model, itemType fields on data classes, and optional
accessory_id parameter on saveBoxRecord. Show accessories with visual
indicator (wrench icon + orange background) in boxing detail page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-24 18:01:01 +08:00

205 lines
6.0 KiB
Dart

// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
part of '../../boxing_page.dart';
extension _BoxingSubmitPart on _BoxingPageState {
Future<bool> _submit() async {
if (!_canSubmit) return false;
final boxNo = int.parse(_boxNoController.text);
final quantity = int.parse(_quantityController.text);
setState(() => _isSubmitting = true);
final editing = _editingBox;
final action = editing == null
? await _apiActions.saveBoxRecord(
zongpaiNo: _zongpaiNo!,
boxNo: boxNo,
quantity: quantity,
)
: await _apiActions.updateBoxRecord(
boxItemId: editing.boxItemId,
boxNo: boxNo,
quantity: quantity,
);
if (!mounted) return false;
setState(() => _isSubmitting = false);
if (action.success) {
if (editing == null) {
this._onSubmitSuccess(boxNo, quantity, action.data!.boxItemId);
} else {
this._onUpdateSuccess(editing, boxNo, quantity);
}
return true;
} else if (action.errorKind == BoxingActionErrorKind.duplicate) {
_feedbackService.trigger(FeedbackEvent.duplicateBoxNo);
this._showStatusOverride(
'该总排号在箱号 ${action.boxNo ?? ""} 已存在,请重新输入',
StatusDotColor.amber,
const Duration(seconds: 2),
);
return false;
} else {
this._showActionError(action, fallback: '提交失败');
return false;
}
}
void _onSubmitSuccess(int boxNo, int quantity, int? boxItemId) {
_feedbackService.trigger(FeedbackEvent.submitSuccess);
setState(() {
if (_mode == BoxingMode.singleCode && boxItemId != null) {
_currentZongpaiBoxes = List.from(_currentZongpaiBoxes)
..add(
CurrentZongpaiBoxData(
boxItemId: boxItemId,
boxNo: boxNo,
quantity: quantity,
),
);
}
this._addExistingBoxItem(boxNo, quantity, boxItemId);
_maxBoxNo = _maxBoxNo > boxNo ? _maxBoxNo : boxNo;
});
switch (_mode) {
case BoxingMode.singleCode:
final remaining = _remainingQuantity;
if (remaining <= 0) {
this._showStatusOverride(
'装箱成功',
StatusDotColor.green,
const Duration(milliseconds: 1500),
);
} else {
setState(() {
_phase = BoxingPhase.scanned;
_boxNoController.text = (_maxBoxNo + 1).toString();
_quantityController.text = remaining.toString();
_quantityController.selection = TextSelection(
baseOffset: 0,
extentOffset: _quantityController.text.length,
);
_isDuplicateBoxNo = false;
});
_focusQuantityInput();
this._showStatusOverride(
'请继续完成剩余数量装箱',
StatusDotColor.green,
const Duration(milliseconds: 1500),
);
}
case BoxingMode.multiCode:
_lastScannedPaichanNo = _paichanNo;
setState(() {
if (boxItemId != null) {
_manyToOnePackedItems.add(
ManyToOnePackedItem(
boxItemId: boxItemId,
zongpaiNo: _zongpaiNo!,
paichanNo: _paichanNo,
workOrderNo: _workOrderNo,
boxNo: boxNo,
quantity: quantity,
totalQuantity: _erpQuantity,
),
);
}
_phase = BoxingPhase.waiting;
_zongpaiNo = null;
_editingPackedItemId = null;
_editingPackedQuantity = '';
_deletingPackedItemId = null;
});
this._showStatusOverride(
'装箱成功,请继续扫码',
StatusDotColor.green,
const Duration(milliseconds: 1500),
);
}
}
void _onUpdateSuccess(
CurrentZongpaiBoxData editing,
int boxNo,
int quantity,
) {
_feedbackService.trigger(FeedbackEvent.submitSuccess);
setState(() {
_currentZongpaiBoxes = _currentZongpaiBoxes.map((item) {
if (item.boxItemId != editing.boxItemId) return item;
return CurrentZongpaiBoxData(
boxItemId: item.boxItemId,
boxNo: boxNo,
quantity: quantity,
);
}).toList();
this._replaceExistingBoxItem(editing, boxNo, quantity);
_editingBox = null;
_boxNoController.text = (_maxBoxNo + 1).toString();
final remaining = _remainingQuantity;
if (remaining > 0) {
_quantityController.text = remaining.toString();
} else {
_quantityController.clear();
}
_isDuplicateBoxNo = false;
});
this._showStatusOverride(
'修改成功',
StatusDotColor.green,
const Duration(milliseconds: 1500),
);
}
Future<void> _submitAccessory(PendingAccessory accessory) async {
if (_zongpaiNo == null || _isSubmitting) return;
final boxNo = int.tryParse(_boxNoController.text);
if (boxNo == null || boxNo <= 0) {
this._showStatusOverride(
'请先输入有效箱号',
StatusDotColor.red,
const Duration(seconds: 2),
);
return;
}
setState(() => _isSubmitting = true);
final action = await _apiActions.saveBoxRecord(
zongpaiNo: _zongpaiNo!,
boxNo: boxNo,
quantity: accessory.quantity,
itemType: 'accessory',
accessoryId: accessory.accessoryId,
);
if (!mounted) return;
setState(() => _isSubmitting = false);
if (action.success) {
_feedbackService.trigger(FeedbackEvent.submitSuccess);
// Refresh box info to update pending accessories
await this._queryBoxInfo(_zongpaiNo!);
if (mounted) {
this._showStatusOverride(
'${accessory.accessoryType} 已加入箱号 $boxNo',
StatusDotColor.green,
const Duration(milliseconds: 1500),
);
}
} else {
this._showActionError(action, fallback: '附件装箱失败');
}
}
}