/// 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> 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 && 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 = []; for (var i = 0; i < message.length; i++) { try { final item = message[i] as Map; _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 && message.containsKey('data')) { final dataList = message['data'] as List; _debugPrint('Parsing ${dataList.length} addresses from data field'); final addresses = []; for (var i = 0; i < dataList.length; i++) { try { final item = dataList[i] as Map; _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 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 && data.containsKey('message')) { final message = data['message']; // Check for error response format if (message is Map && message.containsKey('error')) { final error = message['error'] as String; _debugPrint('API error: $error'); throw ServerException(error); } // Handle direct address object if (message is Map) { final savedAddress = AddressModel.fromJson(message); _debugPrint('Address saved: ${savedAddress.name}'); return savedAddress; } // Handle nested data if (message is Map && message.containsKey('data')) { final savedAddress = AddressModel.fromJson(message['data'] as Map); _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 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'); } }