karrio-mydhl 2025.5rc7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. karrio/mappers/mydhl/__init__.py +3 -0
  2. karrio/mappers/mydhl/mapper.py +99 -0
  3. karrio/mappers/mydhl/proxy.py +148 -0
  4. karrio/mappers/mydhl/settings.py +24 -0
  5. karrio/plugins/mydhl/__init__.py +24 -0
  6. karrio/providers/mydhl/__init__.py +28 -0
  7. karrio/providers/mydhl/address.py +65 -0
  8. karrio/providers/mydhl/error.py +77 -0
  9. karrio/providers/mydhl/pickup/__init__.py +14 -0
  10. karrio/providers/mydhl/pickup/cancel.py +62 -0
  11. karrio/providers/mydhl/pickup/create.py +94 -0
  12. karrio/providers/mydhl/pickup/update.py +108 -0
  13. karrio/providers/mydhl/rate.py +112 -0
  14. karrio/providers/mydhl/shipment/__init__.py +9 -0
  15. karrio/providers/mydhl/shipment/cancel.py +91 -0
  16. karrio/providers/mydhl/shipment/create.py +100 -0
  17. karrio/providers/mydhl/tracking.py +86 -0
  18. karrio/providers/mydhl/units.py +99 -0
  19. karrio/providers/mydhl/utils.py +92 -0
  20. karrio/schemas/mydhl/__init__.py +0 -0
  21. karrio/schemas/mydhl/address_validation_request.py +13 -0
  22. karrio/schemas/mydhl/address_validation_response.py +25 -0
  23. karrio/schemas/mydhl/error_response.py +13 -0
  24. karrio/schemas/mydhl/pickup_cancel_request.py +10 -0
  25. karrio/schemas/mydhl/pickup_cancel_response.py +8 -0
  26. karrio/schemas/mydhl/pickup_create_request.py +108 -0
  27. karrio/schemas/mydhl/pickup_create_response.py +11 -0
  28. karrio/schemas/mydhl/pickup_update_request.py +110 -0
  29. karrio/schemas/mydhl/pickup_update_response.py +11 -0
  30. karrio/schemas/mydhl/rate_request.py +114 -0
  31. karrio/schemas/mydhl/rate_response.py +143 -0
  32. karrio/schemas/mydhl/shipment_request.py +275 -0
  33. karrio/schemas/mydhl/shipment_response.py +90 -0
  34. karrio/schemas/mydhl/tracking_response.py +112 -0
  35. karrio_mydhl-2025.5rc7.dist-info/METADATA +44 -0
  36. karrio_mydhl-2025.5rc7.dist-info/RECORD +39 -0
  37. karrio_mydhl-2025.5rc7.dist-info/WHEEL +5 -0
  38. karrio_mydhl-2025.5rc7.dist-info/entry_points.txt +2 -0
  39. karrio_mydhl-2025.5rc7.dist-info/top_level.txt +3 -0
@@ -0,0 +1,3 @@
1
+ from karrio.mappers.mydhl.mapper import Mapper
2
+ from karrio.mappers.mydhl.proxy import Proxy
3
+ from karrio.mappers.mydhl.settings import Settings
@@ -0,0 +1,99 @@
1
+ """Karrio MyDHL client mapper."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.api.mapper as mapper
6
+ import karrio.core.models as models
7
+ import karrio.providers.mydhl as provider
8
+ import karrio.mappers.mydhl.settings as provider_settings
9
+
10
+
11
+ class Mapper(mapper.Mapper):
12
+ settings: provider_settings.Settings
13
+
14
+ def create_rate_request(
15
+ self, payload: models.RateRequest
16
+ ) -> lib.Serializable:
17
+ return provider.rate_request(payload, self.settings)
18
+
19
+ def create_tracking_request(
20
+ self, payload: models.TrackingRequest
21
+ ) -> lib.Serializable:
22
+ return provider.tracking_request(payload, self.settings)
23
+
24
+ def create_shipment_request(
25
+ self, payload: models.ShipmentRequest
26
+ ) -> lib.Serializable:
27
+ return provider.shipment_request(payload, self.settings)
28
+
29
+ def create_pickup_request(
30
+ self, payload: models.PickupRequest
31
+ ) -> lib.Serializable:
32
+ return provider.pickup_request(payload, self.settings)
33
+
34
+ def create_pickup_update_request(
35
+ self, payload: models.PickupUpdateRequest
36
+ ) -> lib.Serializable:
37
+ return provider.pickup_update_request(payload, self.settings)
38
+
39
+ def create_cancel_pickup_request(
40
+ self, payload: models.PickupCancelRequest
41
+ ) -> lib.Serializable:
42
+ return provider.pickup_cancel_request(payload, self.settings)
43
+
44
+ def create_cancel_shipment_request(
45
+ self, payload: models.ShipmentCancelRequest
46
+ ) -> lib.Serializable[str]:
47
+ return provider.shipment_cancel_request(payload, self.settings)
48
+
49
+ def create_shipment_cancel_request(
50
+ self, payload: models.ShipmentCancelRequest
51
+ ) -> lib.Serializable[str]:
52
+ return provider.shipment_cancel_request(payload, self.settings)
53
+
54
+ def create_address_validation_request(
55
+ self, payload: models.AddressValidationRequest
56
+ ) -> lib.Serializable:
57
+ return provider.address_validation_request(payload, self.settings)
58
+
59
+
60
+ def parse_cancel_pickup_response(
61
+ self, response: lib.Deserializable[str]
62
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
63
+ return provider.parse_pickup_cancel_response(response, self.settings)
64
+
65
+ def parse_cancel_shipment_response(
66
+ self, response: lib.Deserializable[str]
67
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
68
+ return provider.parse_shipment_cancel_response(response, self.settings)
69
+
70
+ def parse_pickup_response(
71
+ self, response: lib.Deserializable[str]
72
+ ) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
73
+ return provider.parse_pickup_response(response, self.settings)
74
+
75
+ def parse_pickup_update_response(
76
+ self, response: lib.Deserializable[str]
77
+ ) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
78
+ return provider.parse_pickup_update_response(response, self.settings)
79
+
80
+ def parse_rate_response(
81
+ self, response: lib.Deserializable[str]
82
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
83
+ return provider.parse_rate_response(response, self.settings)
84
+
85
+ def parse_shipment_response(
86
+ self, response: lib.Deserializable[str]
87
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
88
+ return provider.parse_shipment_response(response, self.settings)
89
+
90
+ def parse_tracking_response(
91
+ self, response: lib.Deserializable[str]
92
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
93
+ return provider.parse_tracking_response(response, self.settings)
94
+
95
+ def parse_address_validation_response(
96
+ self, response: lib.Deserializable[str]
97
+ ) -> typing.Tuple[typing.List[models.AddressValidationDetails], typing.List[models.Message]]:
98
+ return provider.parse_address_validation_response(response, self.settings)
99
+
@@ -0,0 +1,148 @@
1
+ """Karrio MyDHL client proxy."""
2
+
3
+ import karrio.lib as lib
4
+ import karrio.api.proxy as proxy
5
+ import karrio.mappers.mydhl.settings as provider_settings
6
+
7
+ class Proxy(proxy.Proxy):
8
+ settings: provider_settings.Settings
9
+
10
+ def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
11
+ """Get rates using POST /rates for multi-piece shipments."""
12
+ response = lib.request(
13
+ url=f"{self.settings.server_url}/mydhlapi/rates",
14
+ data=lib.to_json(request.serialize()),
15
+ trace=self.trace_as("json"),
16
+ method="POST",
17
+ headers={
18
+ "Content-Type": "application/json",
19
+ "Authorization": f"Basic {self.settings.authorization}",
20
+ "Accept": "application/json",
21
+ },
22
+ )
23
+ return lib.Deserializable(response, lib.to_dict)
24
+
25
+ def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
26
+ """Create shipment using POST /shipments."""
27
+ response = lib.request(
28
+ url=f"{self.settings.server_url}/shipments",
29
+ data=lib.to_json(request.serialize()),
30
+ trace=self.trace_as("json"),
31
+ method="POST",
32
+ headers={
33
+ "Content-Type": "application/json",
34
+ "Authorization": f"Basic {self.settings.authorization}",
35
+ "Accept": "application/json",
36
+ },
37
+ )
38
+ return lib.Deserializable(response, lib.to_dict)
39
+
40
+ def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
41
+ """Cancel shipment using DELETE /shipments/{shipmentTrackingNumber}."""
42
+ data = request.serialize()
43
+ shipment_id = data.get("shipmentIdentifier") or data.get("shipmentId") or data.get("shipmentTrackingNumber")
44
+
45
+ response = lib.request(
46
+ url=f"{self.settings.server_url}/shipments/{shipment_id}/cancel",
47
+ trace=self.trace_as("json"),
48
+ method="DELETE",
49
+ headers={
50
+ "Authorization": f"Basic {self.settings.authorization}",
51
+ "Accept": "application/json",
52
+ },
53
+ )
54
+ return lib.Deserializable(response, lib.to_dict)
55
+
56
+ def get_tracking(self, request: lib.Serializable) -> lib.Deserializable:
57
+ """Get tracking using GET /tracking with tracking numbers."""
58
+ def _get_tracking(tracking_number: str):
59
+ return tracking_number, lib.request(
60
+ url=f"{self.settings.server_url}/tracking",
61
+ trace=self.trace_as("json"),
62
+ method="GET",
63
+ headers={
64
+ "Authorization": f"Basic {self.settings.authorization}",
65
+ "Accept": "application/json",
66
+ },
67
+ )
68
+
69
+ data = request.serialize()
70
+ tracking_numbers = data.get("trackingNumbers", [])
71
+ responses = lib.run_concurently(_get_tracking, tracking_numbers)
72
+ return lib.Deserializable(
73
+ responses,
74
+ lambda res: [
75
+ (num, lib.to_dict(track)) for num, track in res if any(track.strip())
76
+ ],
77
+ )
78
+
79
+ def schedule_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
80
+ """Schedule pickup using POST /pickups."""
81
+ response = lib.request(
82
+ url=f"{self.settings.server_url}/mydhlapi/pickups",
83
+ data=lib.to_json(request.serialize()),
84
+ trace=self.trace_as("json"),
85
+ method="POST",
86
+ headers={
87
+ "Content-Type": "application/json",
88
+ "Authorization": f"Basic {self.settings.authorization}",
89
+ "Accept": "application/json",
90
+ },
91
+ )
92
+ return lib.Deserializable(response, lib.to_dict)
93
+
94
+ def modify_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
95
+ """Update pickup using PATCH /pickups/{dispatchConfirmationNumber}."""
96
+ data = request.serialize()
97
+ dispatch_confirmation_number = data.get("dispatchConfirmationNumber")
98
+
99
+ response = lib.request(
100
+ url=f"{self.settings.server_url}/mydhlapi/pickups/{dispatch_confirmation_number}",
101
+ data=lib.to_json(data),
102
+ trace=self.trace_as("json"),
103
+ method="PATCH",
104
+ headers={
105
+ "Content-Type": "application/json",
106
+ "Authorization": f"Basic {self.settings.authorization}",
107
+ "Accept": "application/json",
108
+ },
109
+ )
110
+ return lib.Deserializable(response, lib.to_dict)
111
+
112
+ def cancel_pickup(self, request: lib.Serializable) -> lib.Deserializable[str]:
113
+ """Cancel pickup using DELETE /pickups/{dispatchConfirmationNumber}."""
114
+ data = request.serialize()
115
+ dispatch_confirmation_number = data.get("dispatchConfirmationNumber")
116
+ requestor_name = data.get("requestorName", "System")
117
+ reason = data.get("reason", "Customer request")
118
+
119
+ response = lib.request(
120
+ url=f"{self.settings.server_url}/mydhlapi/pickups/{dispatch_confirmation_number}",
121
+ trace=self.trace_as("json"),
122
+ method="DELETE",
123
+ headers={
124
+ "Authorization": f"Basic {self.settings.authorization}",
125
+ "Accept": "application/json",
126
+ },
127
+ params={
128
+ "requestorName": requestor_name,
129
+ "reason": reason,
130
+ },
131
+ )
132
+ return lib.Deserializable(response, lib.to_dict)
133
+
134
+ def validate_address(self, request: lib.Serializable) -> lib.Deserializable[str]:
135
+ """Validate address using GET /address-validate."""
136
+ data = request.serialize()
137
+
138
+ response = lib.request(
139
+ url=f"{self.settings.server_url}/mydhlapi/address-validate",
140
+ trace=self.trace_as("json"),
141
+ method="GET",
142
+ headers={
143
+ "Authorization": f"Basic {self.settings.authorization}",
144
+ "Accept": "application/json",
145
+ },
146
+ params=data,
147
+ )
148
+ return lib.Deserializable(response, lib.to_dict)
@@ -0,0 +1,24 @@
1
+ """Karrio MyDHL client settings."""
2
+
3
+ import attr
4
+ import jstruct
5
+ import karrio.lib as lib
6
+ import karrio.providers.mydhl.utils as provider_utils
7
+
8
+
9
+ @attr.s(auto_attribs=True)
10
+ class Settings(provider_utils.Settings):
11
+ """MyDHL connection settings."""
12
+
13
+ # MyDHL API credentials (required)
14
+ username: str # MyDHL API username
15
+ password: str # MyDHL API password
16
+ account_number: str # MyDHL customer account number
17
+
18
+ # generic properties
19
+ id: str = None
20
+ test_mode: bool = False
21
+ carrier_id: str = "mydhl"
22
+ account_country_code: str = None
23
+ metadata: dict = {}
24
+ config: dict = {}
@@ -0,0 +1,24 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.mydhl as mappers
3
+ import karrio.providers.mydhl.units as units
4
+ import karrio.providers.mydhl.utils as utils
5
+
6
+
7
+ # This METADATA object is used by Karrio to discover and register this plugin
8
+ # when loaded through Python entrypoints or local plugin directories.
9
+ # The entrypoint is defined in pyproject.toml under [project.entry-points."karrio.plugins"]
10
+ METADATA = metadata.Metadata(
11
+ id="mydhl",
12
+ label="MyDHL",
13
+ # Integrations
14
+ Mapper=mappers.Mapper,
15
+ Proxy=mappers.Proxy,
16
+ Settings=mappers.Settings,
17
+ # Data Units
18
+ is_hub=False,
19
+ options=units.ShippingOption,
20
+ services=units.ShippingService,
21
+ package_presets=units.PackagePresets,
22
+ packaging_types=units.PackagingType,
23
+ connection_configs=utils.ConnectionConfig,
24
+ )
@@ -0,0 +1,28 @@
1
+ """Karrio MyDHL provider imports."""
2
+ from karrio.providers.mydhl.utils import Settings
3
+ from karrio.providers.mydhl.rate import (
4
+ parse_rate_response,
5
+ rate_request,
6
+ )
7
+ from karrio.providers.mydhl.shipment import (
8
+ parse_shipment_cancel_response,
9
+ parse_shipment_response,
10
+ shipment_cancel_request,
11
+ shipment_request,
12
+ )
13
+ from karrio.providers.mydhl.pickup import (
14
+ parse_pickup_cancel_response,
15
+ parse_pickup_response,
16
+ parse_pickup_update_response,
17
+ pickup_cancel_request,
18
+ pickup_request,
19
+ pickup_update_request,
20
+ )
21
+ from karrio.providers.mydhl.tracking import (
22
+ parse_tracking_response,
23
+ tracking_request,
24
+ )
25
+ from karrio.providers.mydhl.address import (
26
+ parse_address_validation_response,
27
+ address_validation_request,
28
+ )
@@ -0,0 +1,65 @@
1
+ """Karrio MyDHL address validation API implementation."""
2
+
3
+ import karrio.schemas.mydhl.address_validation_request as mydhl_req
4
+ import karrio.schemas.mydhl.address_validation_response as mydhl_res
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.models as models
9
+ import karrio.providers.mydhl.error as error
10
+ import karrio.providers.mydhl.utils as provider_utils
11
+
12
+
13
+ def parse_address_validation_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[typing.List[models.AddressValidationDetails], typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+ messages = error.parse_error_response(response, settings)
19
+
20
+ # Only create validation details if there are no errors
21
+ validation_details = []
22
+ if not messages:
23
+ validation_details = [_extract_details(response, settings)]
24
+
25
+ return validation_details, messages
26
+
27
+
28
+ def _extract_details(
29
+ data: dict,
30
+ settings: provider_utils.Settings,
31
+ ) -> models.AddressValidationDetails:
32
+ """Extract address validation details from MyDHL response."""
33
+ validation = lib.to_object(mydhl_res.AddressValidationResponseType, data)
34
+
35
+ warnings = getattr(validation, 'warnings', []) or []
36
+ addresses = getattr(validation, 'address', []) or []
37
+
38
+ return models.AddressValidationDetails(
39
+ carrier_id=settings.carrier_id,
40
+ carrier_name=settings.carrier_name,
41
+ success=len(warnings) == 0,
42
+ complete_address=models.Address(
43
+ city=addresses[0].cityName if addresses else "",
44
+ postal_code=addresses[0].postalCode if addresses else "",
45
+ country_code=addresses[0].countryCode if addresses else "",
46
+ ) if addresses else None,
47
+ )
48
+
49
+
50
+ def address_validation_request(
51
+ payload: models.AddressValidationRequest,
52
+ settings: provider_utils.Settings,
53
+ ) -> lib.Serializable:
54
+ """Create MyDHL address validation request."""
55
+ address = lib.to_address(payload.address)
56
+
57
+ request = mydhl_req.AddressValidationRequestType(
58
+ type="delivery",
59
+ countryCode=address.country_code,
60
+ postalCode=address.postal_code,
61
+ cityName=address.city,
62
+ strictValidation=True,
63
+ )
64
+
65
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,77 @@
1
+ """Karrio MyDHL error parser."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.models as models
6
+ import karrio.providers.mydhl.utils as provider_utils
7
+
8
+ def parse_error_response(
9
+ response: dict,
10
+ settings: provider_utils.Settings,
11
+ **kwargs,
12
+ ) -> typing.List[models.Message]:
13
+ """Parse MyDHL error responses."""
14
+
15
+ # Handle standard error response format
16
+ if "detail" in response:
17
+ return [
18
+ models.Message(
19
+ carrier_id=settings.carrier_id,
20
+ carrier_name=settings.carrier_name,
21
+ code=response.get("status", ""),
22
+ message=response.get("detail", ""),
23
+ details=dict(
24
+ instance=response.get("instance", ""),
25
+ title=response.get("title", ""),
26
+ **kwargs
27
+ ),
28
+ )
29
+ ]
30
+
31
+ # Handle error object format
32
+ if "error" in response:
33
+ error = response["error"]
34
+ return [
35
+ models.Message(
36
+ carrier_id=settings.carrier_id,
37
+ carrier_name=settings.carrier_name,
38
+ code=error.get("code", ""),
39
+ message=error.get("message", ""),
40
+ details=dict(
41
+ details=error.get("details", ""),
42
+ **kwargs
43
+ ),
44
+ )
45
+ ]
46
+
47
+ # Handle additional error formats if present
48
+ errors = response.get("errors", [])
49
+ additional_details = response.get("additionalDetails", [])
50
+
51
+ messages = []
52
+
53
+ # Process main errors
54
+ for error in errors:
55
+ messages.append(
56
+ models.Message(
57
+ carrier_id=settings.carrier_id,
58
+ carrier_name=settings.carrier_name,
59
+ code=error.get("code", ""),
60
+ message=error.get("message", ""),
61
+ details=dict(error, **kwargs),
62
+ )
63
+ )
64
+
65
+ # Process additional validation errors
66
+ for detail in additional_details:
67
+ messages.append(
68
+ models.Message(
69
+ carrier_id=settings.carrier_id,
70
+ carrier_name=settings.carrier_name,
71
+ code="validation_error",
72
+ message=detail,
73
+ details=dict(validation_detail=detail, **kwargs),
74
+ )
75
+ )
76
+
77
+ return messages
@@ -0,0 +1,14 @@
1
+ """Karrio MyDHL pickup API imports."""
2
+
3
+ from karrio.providers.mydhl.pickup.create import (
4
+ parse_pickup_response,
5
+ pickup_request,
6
+ )
7
+ from karrio.providers.mydhl.pickup.update import (
8
+ parse_pickup_update_response,
9
+ pickup_update_request,
10
+ )
11
+ from karrio.providers.mydhl.pickup.cancel import (
12
+ parse_pickup_cancel_response,
13
+ pickup_cancel_request,
14
+ )
@@ -0,0 +1,62 @@
1
+ """Karrio MyDHL pickup cancellation API implementation."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.models as models
6
+ import karrio.providers.mydhl.error as error
7
+ import karrio.providers.mydhl.utils as provider_utils
8
+
9
+
10
+ def parse_pickup_cancel_response(
11
+ _response: lib.Deserializable[dict],
12
+ settings: provider_utils.Settings,
13
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
14
+ """Parse pickup cancellation response from carrier API"""
15
+ response = _response.deserialize()
16
+ messages = error.parse_error_response(response, settings)
17
+
18
+ # Check if cancellation was successful
19
+ success = _extract_cancellation_status(response)
20
+ confirmation = (
21
+ models.ConfirmationDetails(
22
+ carrier_id=settings.carrier_id,
23
+ carrier_name=settings.carrier_name,
24
+ success=success,
25
+ operation="Cancel Pickup",
26
+ ) if success else None
27
+ )
28
+
29
+ return confirmation, messages
30
+
31
+
32
+ def _extract_cancellation_status(
33
+ response: dict
34
+ ) -> bool:
35
+ """Extract cancellation success status from carrier response"""
36
+
37
+ # Example implementation for JSON response:
38
+ # return response.get("status", "").lower() == "cancelled"
39
+
40
+ # For development, always return success
41
+ return True
42
+
43
+
44
+
45
+ def pickup_cancel_request(
46
+ payload: models.PickupCancelRequest,
47
+ settings: provider_utils.Settings,
48
+ ) -> lib.Serializable:
49
+ """Create pickup cancellation request for carrier API"""
50
+ # Extract cancellation details
51
+ confirmation_number = payload.confirmation_number
52
+
53
+
54
+ # Example implementation for JSON request:
55
+ request = {
56
+ "dispatchConfirmationNumber": confirmation_number,
57
+ "requestorName": getattr(payload, 'requestor_name', "System"),
58
+ "reason": getattr(payload, 'reason', "Customer request")
59
+ }
60
+
61
+ return lib.Serializable(request, lib.to_dict)
62
+
@@ -0,0 +1,94 @@
1
+ """Karrio MyDHL pickup create API implementation."""
2
+
3
+ import karrio.schemas.mydhl.pickup_create_request as mydhl_req
4
+ import karrio.schemas.mydhl.pickup_create_response as mydhl_res
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.models as models
9
+ import karrio.providers.mydhl.error as error
10
+ import karrio.providers.mydhl.utils as provider_utils
11
+
12
+
13
+ def parse_pickup_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[models.PickupDetails, typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+ messages = error.parse_error_response(response, settings)
19
+ pickup = _extract_details(response, settings) if response and not messages else None
20
+
21
+ return pickup, messages
22
+
23
+
24
+ def _extract_details(
25
+ data: dict,
26
+ settings: provider_utils.Settings,
27
+ ) -> models.PickupDetails:
28
+ """Extract pickup details from MyDHL response."""
29
+ # For simple JSON responses, extract directly
30
+ return models.PickupDetails(
31
+ carrier_id=settings.carrier_id,
32
+ carrier_name=settings.carrier_name,
33
+ confirmation_number=data.get("confirmationNumber", ""),
34
+ pickup_date=lib.fdate(data.get("pickupDate")),
35
+ ready_time=data.get("readyTime"),
36
+ closing_time=data.get("closingTime"),
37
+ )
38
+
39
+
40
+ def pickup_request(
41
+ payload: models.PickupRequest,
42
+ settings: provider_utils.Settings,
43
+ ) -> lib.Serializable:
44
+ """Create MyDHL pickup request."""
45
+ address = lib.to_address(payload.address)
46
+
47
+ request = mydhl_req.PickupCreateRequestType(
48
+ plannedPickupDateAndTime=lib.fdatetime(
49
+ payload.pickup_date,
50
+ current_format="%Y-%m-%d",
51
+ output_format="%Y-%m-%dT%H:%M:%S GMT+01:00"
52
+ ),
53
+ closeTime=payload.closing_time or "18:00",
54
+ location="reception",
55
+ accounts=[
56
+ mydhl_req.AccountType(
57
+ typeCode="shipper",
58
+ number=settings.account_number,
59
+ )
60
+ ],
61
+ customerDetails=mydhl_req.CustomerDetailsType(
62
+ shipperDetails=mydhl_req.DetailsType(
63
+ postalAddress=mydhl_req.PostalAddressType(
64
+ cityName=address.city,
65
+ countryCode=address.country_code,
66
+ postalCode=address.postal_code,
67
+ addressLine1=address.address_line1,
68
+ ),
69
+ contactInformation=mydhl_req.ContactInformationType(
70
+ companyName=address.company_name,
71
+ fullName=address.person_name,
72
+ phone=address.phone_number,
73
+ email=address.email,
74
+ ),
75
+ ),
76
+ ),
77
+ shipmentDetails=[
78
+ mydhl_req.ShipmentDetailType(
79
+ productCode="U",
80
+ packages=[
81
+ mydhl_req.PackageType(
82
+ weight=1.0,
83
+ dimensions=mydhl_req.DimensionsType(
84
+ length=1,
85
+ width=1,
86
+ height=1,
87
+ ),
88
+ )
89
+ ],
90
+ )
91
+ ],
92
+ )
93
+
94
+ return lib.Serializable(request, lib.to_dict)