refactor: extract boxing logic into dedicated modules with tests
- 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>
This commit is contained in:
135
lib/pages/boxing/boxing_box_mutations.dart
Normal file
135
lib/pages/boxing/boxing_box_mutations.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'package:pad_scanner/pages/boxing/boxing_models.dart';
|
||||
import 'package:pad_scanner/services/api_service.dart';
|
||||
|
||||
class BoxMutationResult {
|
||||
final List<BoxDetailData> boxes;
|
||||
final int maxBoxNo;
|
||||
|
||||
const BoxMutationResult({required this.boxes, required this.maxBoxNo});
|
||||
}
|
||||
|
||||
int maxBoxNoFor(List<BoxDetailData> boxes) {
|
||||
return boxes.fold<int>(0, (max, box) => box.boxNo > max ? box.boxNo : max);
|
||||
}
|
||||
|
||||
BoxMutationResult addExistingBoxItem({
|
||||
required List<BoxDetailData> existingBoxes,
|
||||
required int boxNo,
|
||||
required int quantity,
|
||||
required int? boxItemId,
|
||||
required String zongpaiNo,
|
||||
required String? workOrderNo,
|
||||
required int? totalQuantity,
|
||||
}) {
|
||||
final item = BoxItemData(
|
||||
boxItemId: boxItemId,
|
||||
zongpaiNo: zongpaiNo,
|
||||
workOrderNo: workOrderNo,
|
||||
quantity: quantity,
|
||||
totalQuantity: totalQuantity,
|
||||
);
|
||||
final index = existingBoxes.indexWhere((box) => box.boxNo == boxNo);
|
||||
if (index < 0) {
|
||||
final boxes = [
|
||||
...existingBoxes,
|
||||
BoxDetailData(boxNo: boxNo, items: [item]),
|
||||
]..sort((a, b) => a.boxNo.compareTo(b.boxNo));
|
||||
return BoxMutationResult(boxes: boxes, maxBoxNo: maxBoxNoFor(boxes));
|
||||
}
|
||||
|
||||
final boxes = List<BoxDetailData>.from(existingBoxes);
|
||||
final box = boxes[index];
|
||||
boxes[index] = BoxDetailData(boxNo: box.boxNo, items: [...box.items, item]);
|
||||
return BoxMutationResult(boxes: boxes, maxBoxNo: maxBoxNoFor(boxes));
|
||||
}
|
||||
|
||||
BoxMutationResult replaceExistingBoxItem({
|
||||
required List<BoxDetailData> existingBoxes,
|
||||
required CurrentZongpaiBoxData editing,
|
||||
required int boxNo,
|
||||
required int quantity,
|
||||
required String zongpaiNo,
|
||||
required String? workOrderNo,
|
||||
required int? totalQuantity,
|
||||
}) {
|
||||
final updatedItem = BoxItemData(
|
||||
boxItemId: editing.boxItemId,
|
||||
zongpaiNo: zongpaiNo,
|
||||
workOrderNo: workOrderNo,
|
||||
quantity: quantity,
|
||||
totalQuantity: totalQuantity,
|
||||
);
|
||||
return _replaceItemById(
|
||||
existingBoxes: existingBoxes,
|
||||
boxItemId: editing.boxItemId,
|
||||
targetBoxNo: boxNo,
|
||||
updatedItem: updatedItem,
|
||||
);
|
||||
}
|
||||
|
||||
BoxMutationResult replaceExistingPackedItem({
|
||||
required List<BoxDetailData> existingBoxes,
|
||||
required ManyToOnePackedItem item,
|
||||
required int boxNo,
|
||||
required int quantity,
|
||||
}) {
|
||||
final updatedItem = BoxItemData(
|
||||
boxItemId: item.boxItemId,
|
||||
zongpaiNo: item.zongpaiNo,
|
||||
workOrderNo: item.workOrderNo,
|
||||
quantity: quantity,
|
||||
totalQuantity: item.totalQuantity,
|
||||
);
|
||||
return _replaceItemById(
|
||||
existingBoxes: existingBoxes,
|
||||
boxItemId: item.boxItemId,
|
||||
targetBoxNo: boxNo,
|
||||
updatedItem: updatedItem,
|
||||
);
|
||||
}
|
||||
|
||||
BoxMutationResult removeExistingBoxItem({
|
||||
required List<BoxDetailData> existingBoxes,
|
||||
required int boxItemId,
|
||||
}) {
|
||||
final boxes = <BoxDetailData>[];
|
||||
for (final box in existingBoxes) {
|
||||
final items = box.items
|
||||
.where((item) => item.boxItemId != boxItemId)
|
||||
.toList();
|
||||
if (items.isNotEmpty) {
|
||||
boxes.add(BoxDetailData(boxNo: box.boxNo, items: items));
|
||||
}
|
||||
}
|
||||
return BoxMutationResult(boxes: boxes, maxBoxNo: maxBoxNoFor(boxes));
|
||||
}
|
||||
|
||||
BoxMutationResult _replaceItemById({
|
||||
required List<BoxDetailData> existingBoxes,
|
||||
required int boxItemId,
|
||||
required int targetBoxNo,
|
||||
required BoxItemData updatedItem,
|
||||
}) {
|
||||
final boxes = <BoxDetailData>[];
|
||||
for (final box in existingBoxes) {
|
||||
final items = box.items.where((item) {
|
||||
return item.boxItemId != boxItemId;
|
||||
}).toList();
|
||||
if (items.isNotEmpty) {
|
||||
boxes.add(BoxDetailData(boxNo: box.boxNo, items: items));
|
||||
}
|
||||
}
|
||||
|
||||
final targetIndex = boxes.indexWhere((box) => box.boxNo == targetBoxNo);
|
||||
if (targetIndex >= 0) {
|
||||
final target = boxes[targetIndex];
|
||||
boxes[targetIndex] = BoxDetailData(
|
||||
boxNo: target.boxNo,
|
||||
items: [...target.items, updatedItem],
|
||||
);
|
||||
} else {
|
||||
boxes.add(BoxDetailData(boxNo: targetBoxNo, items: [updatedItem]));
|
||||
}
|
||||
boxes.sort((a, b) => a.boxNo.compareTo(b.boxNo));
|
||||
return BoxMutationResult(boxes: boxes, maxBoxNo: maxBoxNoFor(boxes));
|
||||
}
|
||||
Reference in New Issue
Block a user