diff --git a/.auto-claude-status b/.auto-claude-status index 61fea19..f4301a9 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -3,7 +3,7 @@ "spec": "002-migrate-api-routes-from-http-to-grpc", "state": "building", "subtasks": { - "completed": 4, + "completed": 6, "total": 13, "in_progress": 1, "failed": 0 @@ -18,8 +18,8 @@ "max": 1 }, "session": { - "number": 5, + "number": 7, "started_at": "2026-01-20T12:45:59.858836" }, - "last_update": "2026-01-20T12:58:25.218168" + "last_update": "2026-01-20T13:03:21.219002" } \ No newline at end of file diff --git a/lib/api/grpc_client.dart b/lib/api/grpc_client.dart index ec7fd9d..7783b5f 100644 --- a/lib/api/grpc_client.dart +++ b/lib/api/grpc_client.dart @@ -3,7 +3,12 @@ import 'dart:async'; import 'package:grpc/grpc.dart'; import '../generated/delivery_service.pbgrpc.dart'; +import '../models/delivery.dart'; +import '../models/delivery_address.dart'; +import '../models/delivery_contact.dart'; +import '../models/delivery_order.dart'; import '../models/delivery_route.dart'; +import '../models/user_info.dart'; import '../services/auth_service.dart'; import 'grpc_config.dart'; import 'types.dart'; @@ -287,6 +292,114 @@ class GrpcCqrsApiClient { ); } + /// Gets deliveries for a specific route fragment. + /// + /// Returns a [Result] containing a list of [Delivery] objects for the + /// specified [routeFragmentId]. Maps the gRPC [DeliveriesResponse] to + /// domain models. + /// + /// Example: + /// ```dart + /// final result = await client.getDeliveries(routeFragmentId: 123); + /// result.when( + /// success: (deliveries) => displayDeliveries(deliveries), + /// onError: (error) => showError(error.message), + /// ); + /// ``` + Future>> getDeliveries({ + required int routeFragmentId, + }) async { + final request = GetDeliveriesRequest(routeFragmentId: routeFragmentId); + + final result = await _executeWithAuth( + (options) => deliveryClient.getDeliveries(request, options: options), + ); + + return result.when( + success: (response) { + final deliveries = + response.deliveries.map(_mapDeliveryProto).toList(); + return Result.success(deliveries); + }, + onError: (error) => Result.error(error), + ); + } + + /// Maps a [DeliveryProto] to a [Delivery] domain model. + Delivery _mapDeliveryProto(DeliveryProto proto) { + return Delivery( + id: proto.id, + routeFragmentId: proto.routeFragmentId, + deliveryIndex: proto.deliveryIndex, + orders: proto.orders.map(_mapDeliveryOrderProto).toList(), + deliveredBy: + proto.hasDeliveredBy() ? _mapUserInfoProto(proto.deliveredBy) : null, + deliveryAddress: proto.hasDeliveryAddress() + ? _mapDeliveryAddressProto(proto.deliveryAddress) + : null, + deliveredAt: proto.hasDeliveredAt() ? proto.deliveredAt : null, + skippedAt: proto.hasSkippedAt() ? proto.skippedAt : null, + createdAt: proto.createdAt, + updatedAt: proto.hasUpdatedAt() ? proto.updatedAt : null, + delivered: proto.delivered, + hasBeenSkipped: proto.hasBeenSkipped, + isSkipped: proto.isSkipped, + name: proto.name, + ); + } + + /// Maps a [DeliveryAddressProto] to a [DeliveryAddress] domain model. + DeliveryAddress _mapDeliveryAddressProto(DeliveryAddressProto proto) { + return DeliveryAddress( + id: proto.id, + line1: proto.hasLine1() ? proto.line1 : null, + line2: proto.hasLine2() ? proto.line2 : null, + postalCode: proto.hasPostalCode() ? proto.postalCode : null, + city: proto.hasCity() ? proto.city : null, + subdivision: proto.hasSubdivision() ? proto.subdivision : null, + countryCode: proto.hasCountryCode() ? proto.countryCode : null, + latitude: proto.hasLatitude() ? proto.latitude : null, + longitude: proto.hasLongitude() ? proto.longitude : null, + formattedAddress: + proto.hasFormattedAddress() ? proto.formattedAddress : null, + ); + } + + /// Maps a [DeliveryOrderProto] to a [DeliveryOrder] domain model. + DeliveryOrder _mapDeliveryOrderProto(DeliveryOrderProto proto) { + return DeliveryOrder( + id: proto.id, + isNewCustomer: proto.isNewCustomer, + note: proto.hasNote() ? proto.note : null, + totalAmount: proto.totalAmount, + totalPaid: proto.hasTotalPaid() ? proto.totalPaid : null, + totalItems: proto.hasTotalItems() ? proto.totalItems : null, + contacts: proto.contacts.map(_mapDeliveryContactProto).toList(), + contact: + proto.hasContact() ? _mapDeliveryContactProto(proto.contact) : null, + ); + } + + /// Maps a [DeliveryContactProto] to a [DeliveryContact] domain model. + DeliveryContact _mapDeliveryContactProto(DeliveryContactProto proto) { + return DeliveryContact( + firstName: proto.firstName, + lastName: proto.hasLastName() ? proto.lastName : null, + fullName: proto.fullName, + phoneNumber: proto.hasPhoneNumber() ? proto.phoneNumber : null, + ); + } + + /// Maps a [UserInfoProto] to a [UserInfo] domain model. + UserInfo _mapUserInfoProto(UserInfoProto proto) { + return UserInfo( + id: proto.id, + firstName: proto.firstName, + lastName: proto.hasLastName() ? proto.lastName : null, + fullName: proto.fullName, + ); + } + /// Shuts down the gRPC channel and releases resources. /// /// Should be called when the client is no longer needed to properly