Files
worker/lib/features/account/data/datasources/address_remote_datasource.dart
Phuoc Nguyen 0dda402246 update address
2025-11-18 17:04:00 +07:00

209 lines
6.7 KiB
Dart

/// Address Remote Data Source
///
/// Handles API calls to Frappe ERPNext address endpoints.
library;
import 'package:dio/dio.dart';
import 'package:worker/core/errors/exceptions.dart';
import 'package:worker/features/account/data/models/address_model.dart';
/// Address Remote Data Source
///
/// Provides methods to interact with address API endpoints.
/// Online-only approach - no offline caching.
class AddressRemoteDataSource {
final Dio _dio;
AddressRemoteDataSource(this._dio);
/// Get list of addresses
///
/// Fetches all addresses for the authenticated user.
/// Optionally filter by default address.
///
/// API: GET /api/method/building_material.building_material.api.address.get_list
Future<List<AddressModel>> getAddresses({
int limitStart = 0,
int limitPageLength = 0,
bool? isDefault,
}) async {
try {
_debugPrint('Fetching addresses list...');
final response = await _dio.post(
'/api/method/building_material.building_material.api.address.get_list',
data: {
'limit_start': limitStart,
'limit_page_length': limitPageLength,
if (isDefault != null) 'is_default': isDefault,
},
);
if (response.statusCode == 200) {
final data = response.data;
_debugPrint('Response data: $data');
// Extract addresses from response
if (data is Map<String, dynamic> && data.containsKey('message')) {
final message = data['message'];
_debugPrint('Message type: ${message.runtimeType}');
// Handle array response
if (message is List) {
_debugPrint('Parsing ${message.length} addresses from list');
final addresses = <AddressModel>[];
for (var i = 0; i < message.length; i++) {
try {
final item = message[i] as Map<String, dynamic>;
_debugPrint('Parsing address $i: $item');
final address = AddressModel.fromJson(item);
addresses.add(address);
} catch (e) {
_debugPrint('Error parsing address $i: $e');
rethrow;
}
}
_debugPrint('Fetched ${addresses.length} addresses');
return addresses;
}
// Handle object with data field
if (message is Map<String, dynamic> && message.containsKey('data')) {
final dataList = message['data'] as List;
_debugPrint('Parsing ${dataList.length} addresses from data field');
final addresses = <AddressModel>[];
for (var i = 0; i < dataList.length; i++) {
try {
final item = dataList[i] as Map<String, dynamic>;
_debugPrint('Parsing address $i: $item');
final address = AddressModel.fromJson(item);
addresses.add(address);
} catch (e) {
_debugPrint('Error parsing address $i: $e');
rethrow;
}
}
_debugPrint('Fetched ${addresses.length} addresses');
return addresses;
}
}
throw const ServerException('Invalid response format');
} else {
throw ServerException(
'Failed to fetch addresses: ${response.statusCode}',
);
}
} catch (e) {
_debugPrint('Error fetching addresses: $e');
rethrow;
}
}
/// Create or update address
///
/// If name is provided (not empty), updates existing address.
/// If name is null/empty, creates new address.
///
/// Per API docs: When name field is null/empty, the API creates a new address.
/// When name has a value, the API updates the existing address.
///
/// API: POST /api/method/building_material.building_material.api.address.update
Future<AddressModel> saveAddress(AddressModel address) async {
try {
final isUpdate = address.name.isNotEmpty;
_debugPrint(
isUpdate
? 'Updating address: ${address.name}'
: 'Creating new address',
);
// toJson() already handles setting name to null for creation
final data = address.toJson();
_debugPrint('Request data: $data');
final response = await _dio.post(
'/api/method/building_material.building_material.api.address.update',
data: data,
);
if (response.statusCode == 200) {
final data = response.data;
_debugPrint('Response data: $data');
// Check for API error response (even with 200 status)
if (data is Map<String, dynamic> && data.containsKey('message')) {
final message = data['message'];
// Check for error response format
if (message is Map<String, dynamic> && message.containsKey('error')) {
final error = message['error'] as String;
_debugPrint('API error: $error');
throw ServerException(error);
}
// Handle direct address object
if (message is Map<String, dynamic>) {
final savedAddress = AddressModel.fromJson(message);
_debugPrint('Address saved: ${savedAddress.name}');
return savedAddress;
}
// Handle nested data
if (message is Map<String, dynamic> && message.containsKey('data')) {
final savedAddress =
AddressModel.fromJson(message['data'] as Map<String, dynamic>);
_debugPrint('Address saved: ${savedAddress.name}');
return savedAddress;
}
}
throw const ServerException('Invalid response format');
} else {
throw ServerException(
'Failed to save address: ${response.statusCode}',
);
}
} catch (e) {
_debugPrint('Error saving address: $e');
rethrow;
}
}
/// Delete address
///
/// Note: API endpoint for delete not provided in docs.
/// This is a placeholder - adjust when endpoint is available.
Future<void> deleteAddress(String name) async {
try {
_debugPrint('Deleting address: $name');
// TODO: Update with actual delete endpoint when available
final response = await _dio.post(
'/api/method/building_material.building_material.api.address.delete',
data: {'name': name},
);
if (response.statusCode == 200) {
_debugPrint('Address deleted: $name');
return;
} else {
throw ServerException(
'Failed to delete address: ${response.statusCode}',
);
}
} catch (e) {
_debugPrint('Error deleting address: $e');
rethrow;
}
}
/// Debug print helper
void _debugPrint(String message) {
// ignore: avoid_print
print('[AddressRemoteDataSource] $message');
}
}