feat(android): add attachment API methods and result classes

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-08-12 14:51:41 +08:00
parent b383328aed
commit b7484915c6
2 changed files with 742 additions and 0 deletions

View File

@@ -372,6 +372,248 @@ void main() {
expect(missing.errorMessage, '指定装箱明细不存在');
});
});
group('ApiService attachment APIs', () {
test('fetchAttachmentCategories parses list', () async {
final mockClient = _MockClient((request) async {
return http.Response(
jsonEncode([
{'id': 1, 'name': '检验证书', 'sort_order': 1},
{'id': 2, 'name': '增发', 'sort_order': 2},
]),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.fetchAttachmentCategories(
baseUrl: 'http://localhost',
);
expect(result.success, isTrue);
expect(result.categories.length, 2);
expect(result.categories.first.name, '检验证书');
});
test('fetchAttachmentCategories handles network failure', () async {
final mockClient = _MockClient((request) async {
throw Exception('Connection refused');
});
final svc = ApiService(client: mockClient);
final result = await svc.fetchAttachmentCategories(
baseUrl: 'http://localhost',
);
expect(result.success, isFalse);
expect(result.errorMessage, '网络异常,请检查网络连接');
});
test('fetchAttachmentStatus parses undetermined zongpai', () async {
final mockClient = _MockClient((request) async {
return http.Response(
jsonEncode({
'zongpai_no': '26BW0011',
'determination': 'undetermined',
'all_complete': false,
'items': [],
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.fetchAttachmentStatus(
baseUrl: 'http://localhost',
zongpaiNo: '26BW0011',
);
expect(result.success, isTrue);
expect(result.determination, 'undetermined');
expect(result.items, isEmpty);
});
test('putAttachmentConfig sends full payload', () async {
late Map<String, dynamic> body;
final mockClient = _MockClient((request) async {
final req = request as http.Request;
body = jsonDecode(req.body) as Map<String, dynamic>;
return http.Response(
jsonEncode({
'zongpai_no': '26BW0011',
'determination': 'has',
'all_complete': false,
'items': [
{
'category_id': 1,
'name': '检验证书',
'expected_qty': 80,
'boxed_qty': 0,
'location_code': null,
'complete': false,
},
],
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.putAttachmentConfig(
baseUrl: 'http://localhost',
zongpaiNo: '26BW0011',
determination: 'has',
items: [AttachmentConfigItemData(categoryId: 1, expectedQty: 80)],
);
expect(result.success, isTrue);
expect(result.items.single.complete, isFalse);
expect(body['items'], [
{'category_id': 1, 'expected_qty': 80},
]);
});
test('registerAttachmentLocation returns 200 with data', () async {
final mockClient = _MockClient((request) async {
return http.Response(
jsonEncode({
'zongpai_no': '26BW0011',
'category_id': 1,
'location_code': 'A01-01-01',
'created_at': '2026-08-12T10:00:00',
'previous_location': null,
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.registerAttachmentLocation(
baseUrl: 'http://localhost',
zongpaiNo: '26BW0011',
categoryId: 1,
locationCode: 'A01-01-01',
);
expect(result.success, isTrue);
expect(result.locationCode, 'A01-01-01');
});
test('saveAttachmentBox sends request with category_id', () async {
late Map<String, dynamic> body;
final mockClient = _MockClient((request) async {
final req = request as http.Request;
body = jsonDecode(req.body) as Map<String, dynamic>;
return http.Response(
jsonEncode({
'box_item_id': 1,
'paichan_no': 'W00009',
'box_no': 10,
'zongpai_no': '26BW0011',
'category_id': 1,
'quantity': 30,
'created_at': '2026-08-12T10:00:00',
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.saveAttachmentBox(
baseUrl: 'http://localhost',
zongpaiNo: '26BW0011',
categoryId: 1,
boxNo: 10,
quantity: 30,
);
expect(result.success, isTrue);
expect(result.boxItemId, 1);
expect(body['category_id'], 1);
});
test('saveAttachmentBox returns duplicate on 409', () async {
final mockClient = _MockClient((request) async {
return http.Response(
jsonEncode({
'error_code': 'DUPLICATE_BOX_NO',
'message': '排产号 W00009 下箱号 10 已存在',
'paichan_no': 'W00009',
'box_no': 10,
}),
409,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.saveAttachmentBox(
baseUrl: 'http://localhost',
zongpaiNo: '26BW0011',
categoryId: 1,
boxNo: 10,
quantity: 30,
);
expect(result.success, isFalse);
expect(result.isDuplicate, isTrue);
});
test('updateAttachmentBox sends PATCH with box_no and quantity', () async {
late Map<String, dynamic> body;
final mockClient = _MockClient((request) async {
final req = request as http.Request;
body = jsonDecode(req.body) as Map<String, dynamic>;
return http.Response(
jsonEncode({
'box_item_id': 1,
'paichan_no': 'W00009',
'box_no': 11,
'zongpai_no': '26BW0011',
'category_id': 1,
'quantity': 20,
'updated_at': '2026-08-12T10:00:00',
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final result = await svc.updateAttachmentBox(
baseUrl: 'http://localhost',
boxItemId: 1,
boxNo: 11,
quantity: 20,
);
expect(result.success, isTrue);
expect(body, {'box_no': 11, 'quantity': 20});
});
test('deleteAttachmentBox handles success and not found', () async {
var calls = 0;
final mockClient = _MockClient((request) async {
calls += 1;
if (calls == 1) {
return http.Response(
jsonEncode({'box_item_id': 1, 'deleted': true}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
}
return http.Response(
jsonEncode({
'error_code': 'BOX_ITEM_NOT_FOUND',
'message': '指定装箱明细不存在',
}),
404,
headers: {'content-type': 'application/json; charset=utf-8'},
);
});
final svc = ApiService(client: mockClient);
final ok = await svc.deleteAttachmentBox(
baseUrl: 'http://localhost',
boxItemId: 1,
);
final missing = await svc.deleteAttachmentBox(
baseUrl: 'http://localhost',
boxItemId: 999,
);
expect(ok.success, isTrue);
expect(missing.success, isFalse);
expect(missing.errorMessage, '指定装箱明细不存在');
});
});
}
class _MockClient extends http.BaseClient {