- boxing_models.dart: add BoxingMode, BoxingPhase, BoxingPageArguments - boxing_box_mutations.dart: box item add/replace/remove operations - boxing_calculations.dart: pure calculation helpers (quantities, validation) - boxing_status_presenter.dart: status bar text/dot determination - dialogs/cross_paichan_dialog.dart: cross-paichan confirmation dialog - widgets/boxing_notice_banners.dart: notice banner widgets - Add unit tests for mutations, calculations, and status presenter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
3.0 KiB
Dart
102 lines
3.0 KiB
Dart
import 'package:pad_scanner/pages/boxing/boxing_models.dart';
|
|
import 'package:pad_scanner/services/api_service.dart';
|
|
|
|
int packedQuantity(List<CurrentZongpaiBoxData> items) {
|
|
return items.fold<int>(0, (sum, item) => sum + item.quantity);
|
|
}
|
|
|
|
int remainingQuantity({
|
|
required int? totalQuantity,
|
|
required List<CurrentZongpaiBoxData> currentBoxes,
|
|
}) {
|
|
return (totalQuantity ?? 0) - packedQuantity(currentBoxes);
|
|
}
|
|
|
|
bool quantityTooHigh({
|
|
required int remainingQuantity,
|
|
required String quantityText,
|
|
}) {
|
|
if (remainingQuantity <= 0) return false;
|
|
final quantity = int.tryParse(quantityText);
|
|
return quantity != null && quantity > remainingQuantity;
|
|
}
|
|
|
|
bool canSubmitBoxing({
|
|
required bool isSubmitting,
|
|
required BoxingPhase phase,
|
|
required String? zongpaiNo,
|
|
required String boxNoText,
|
|
required String quantityText,
|
|
required int remainingQuantity,
|
|
required bool quantityTooHigh,
|
|
required bool isDuplicateBoxNo,
|
|
}) {
|
|
if (isSubmitting || phase != BoxingPhase.scanned) return false;
|
|
if (zongpaiNo == null) return false;
|
|
final boxNo = int.tryParse(boxNoText);
|
|
final quantity = int.tryParse(quantityText);
|
|
if (boxNo == null || boxNo <= 0 || quantity == null || quantity <= 0) {
|
|
return false;
|
|
}
|
|
if (remainingQuantity <= 0) return false;
|
|
if (quantityTooHigh) return false;
|
|
if (isDuplicateBoxNo) return false;
|
|
return true;
|
|
}
|
|
|
|
int? currentManyToOneBoxNo(String boxNoText) {
|
|
final boxNo = int.tryParse(boxNoText.trim());
|
|
if (boxNo == null || boxNo <= 0) return null;
|
|
return boxNo;
|
|
}
|
|
|
|
bool singleCodeBoxNoIsDuplicate({
|
|
required String boxNoText,
|
|
required List<CurrentZongpaiBoxData> currentBoxes,
|
|
required int? editingBoxItemId,
|
|
}) {
|
|
final boxNo = int.tryParse(boxNoText);
|
|
if (boxNo == null) return false;
|
|
return currentBoxes.any((box) {
|
|
if (box.boxNo != boxNo) return false;
|
|
return editingBoxItemId == null || box.boxItemId != editingBoxItemId;
|
|
});
|
|
}
|
|
|
|
List<ManyToOnePackedItem> visibleManyToOnePackedItems({
|
|
required int? boxNo,
|
|
required List<BoxDetailData> existingBoxes,
|
|
required List<ManyToOnePackedItem> packedItems,
|
|
required String? paichanNo,
|
|
}) {
|
|
if (boxNo == null) return const [];
|
|
final byItemId = <int, ManyToOnePackedItem>{};
|
|
for (final box in existingBoxes.where((box) => box.boxNo == boxNo)) {
|
|
for (final item in box.items) {
|
|
final boxItemId = item.boxItemId;
|
|
if (boxItemId == null) continue;
|
|
byItemId[boxItemId] = ManyToOnePackedItem(
|
|
boxItemId: boxItemId,
|
|
zongpaiNo: item.zongpaiNo,
|
|
paichanNo: paichanNo,
|
|
workOrderNo: item.workOrderNo,
|
|
boxNo: boxNo,
|
|
quantity: item.quantity,
|
|
totalQuantity: item.totalQuantity,
|
|
);
|
|
}
|
|
}
|
|
for (final item in packedItems.where((item) => item.boxNo == boxNo)) {
|
|
byItemId[item.boxItemId] = item;
|
|
}
|
|
return byItemId.values.toList(growable: false);
|
|
}
|
|
|
|
int maxAssignedQuantity({
|
|
required int? totalQuantity,
|
|
required int packedQuantity,
|
|
required int itemQuantity,
|
|
}) {
|
|
return (totalQuantity ?? 0) - (packedQuantity - itemQuantity);
|
|
}
|