karrio-allied-express 2025.5rc1__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 (28) hide show
  1. karrio/mappers/allied_express/__init__.py +3 -0
  2. karrio/mappers/allied_express/mapper.py +55 -0
  3. karrio/mappers/allied_express/proxy.py +74 -0
  4. karrio/mappers/allied_express/settings.py +23 -0
  5. karrio/plugins/allied_express/__init__.py +20 -0
  6. karrio/providers/allied_express/__init__.py +13 -0
  7. karrio/providers/allied_express/error.py +34 -0
  8. karrio/providers/allied_express/rate.py +147 -0
  9. karrio/providers/allied_express/shipment/__init__.py +9 -0
  10. karrio/providers/allied_express/shipment/cancel.py +42 -0
  11. karrio/providers/allied_express/shipment/create.py +126 -0
  12. karrio/providers/allied_express/tracking.py +86 -0
  13. karrio/providers/allied_express/units.py +65 -0
  14. karrio/providers/allied_express/utils.py +141 -0
  15. karrio/schemas/allied_express/__init__.py +0 -0
  16. karrio/schemas/allied_express/label_request.py +48 -0
  17. karrio/schemas/allied_express/label_response.py +60 -0
  18. karrio/schemas/allied_express/rate_request.py +48 -0
  19. karrio/schemas/allied_express/rate_response.py +42 -0
  20. karrio/schemas/allied_express/tracking_request.py +8 -0
  21. karrio/schemas/allied_express/tracking_response.py +33 -0
  22. karrio/schemas/allied_express/void_request.py +9 -0
  23. karrio/schemas/allied_express/void_response.py +27 -0
  24. karrio_allied_express-2025.5rc1.dist-info/METADATA +45 -0
  25. karrio_allied_express-2025.5rc1.dist-info/RECORD +28 -0
  26. karrio_allied_express-2025.5rc1.dist-info/WHEEL +5 -0
  27. karrio_allied_express-2025.5rc1.dist-info/entry_points.txt +2 -0
  28. karrio_allied_express-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,3 @@
1
+ from karrio.mappers.allied_express.mapper import Mapper
2
+ from karrio.mappers.allied_express.proxy import Proxy
3
+ from karrio.mappers.allied_express.settings import Settings
@@ -0,0 +1,55 @@
1
+
2
+ """Karrio Allied Express client mapper."""
3
+
4
+ import typing
5
+ import karrio.lib as lib
6
+ import karrio.api.mapper as mapper
7
+ import karrio.core.models as models
8
+ import karrio.providers.allied_express as provider
9
+ import karrio.mappers.allied_express.settings as provider_settings
10
+
11
+
12
+ class Mapper(mapper.Mapper):
13
+ settings: provider_settings.Settings
14
+
15
+ def create_rate_request(
16
+ self, payload: models.RateRequest
17
+ ) -> lib.Serializable:
18
+ return provider.rate_request(payload, self.settings)
19
+
20
+ def create_tracking_request(
21
+ self, payload: models.TrackingRequest
22
+ ) -> lib.Serializable:
23
+ return provider.tracking_request(payload, self.settings)
24
+
25
+ def create_shipment_request(
26
+ self, payload: models.ShipmentRequest
27
+ ) -> lib.Serializable:
28
+ return provider.shipment_request(payload, self.settings)
29
+
30
+ def create_cancel_shipment_request(
31
+ self, payload: models.ShipmentCancelRequest
32
+ ) -> lib.Serializable[str]:
33
+ return provider.shipment_cancel_request(payload, self.settings)
34
+
35
+
36
+ def parse_cancel_shipment_response(
37
+ self, response: lib.Deserializable[str]
38
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
39
+ return provider.parse_shipment_cancel_response(response, self.settings)
40
+
41
+ def parse_rate_response(
42
+ self, response: lib.Deserializable[str]
43
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
44
+ return provider.parse_rate_response(response, self.settings)
45
+
46
+ def parse_shipment_response(
47
+ self, response: lib.Deserializable[str]
48
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
49
+ return provider.parse_shipment_response(response, self.settings)
50
+
51
+ def parse_tracking_response(
52
+ self, response: lib.Deserializable[str]
53
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
54
+ return provider.parse_tracking_response(response, self.settings)
55
+
@@ -0,0 +1,74 @@
1
+ """Karrio Allied Express client proxy."""
2
+
3
+ import karrio.lib as lib
4
+ import karrio.api.proxy as proxy
5
+ import karrio.providers.allied_express.utils as provider_utils
6
+ import karrio.mappers.allied_express.settings as provider_settings
7
+
8
+
9
+ class Proxy(proxy.Proxy):
10
+ settings: provider_settings.Settings
11
+
12
+ def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
13
+ response = lib.request(
14
+ url=f"{self.settings.server_url}/calculatePrice",
15
+ data=request.serialize(),
16
+ trace=self.trace_as("json"),
17
+ method="POST",
18
+ headers={
19
+ "Authorization": f"Basic {self.settings.authorization}",
20
+ "Content-Type": "application/json",
21
+ },
22
+ )
23
+
24
+ return lib.Deserializable(response, provider_utils.parse_response, request.ctx)
25
+
26
+ def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
27
+ response = lib.request(
28
+ url=f"{self.settings.server_url}/GetLabelfull",
29
+ data=request.serialize(),
30
+ trace=self.trace_as("json"),
31
+ method="POST",
32
+ headers={
33
+ "Authorization": f"Basic {self.settings.authorization}",
34
+ "Content-Type": "application/json",
35
+ },
36
+ )
37
+
38
+ return lib.Deserializable(response, provider_utils.parse_response, request.ctx)
39
+
40
+ def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
41
+ payload = request.serialize()
42
+ response = lib.request(
43
+ url=f"{self.settings.server_url}/cancelJob/{payload['shipmentno']}/{payload['postalcode']}",
44
+ trace=self.trace_as("json"),
45
+ method="POST",
46
+ headers={
47
+ "Authorization": f"Basic {self.settings.authorization}",
48
+ "Content-Type": "application/json",
49
+ },
50
+ )
51
+
52
+ return lib.Deserializable(response, provider_utils.parse_response)
53
+
54
+ def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
55
+ response = lib.run_asynchronously(
56
+ lambda payload: (
57
+ payload["shipmentno"],
58
+ lib.request(
59
+ url=f"{self.settings.server_url}/getShipmentsStatus/{payload['shipmentno']}",
60
+ trace=self.trace_as("json"),
61
+ method="POST",
62
+ headers={
63
+ "Authorization": f"Basic {self.settings.authorization}",
64
+ "Content-Type": "application/json",
65
+ },
66
+ ),
67
+ ),
68
+ request.serialize(),
69
+ )
70
+
71
+ return lib.Deserializable(
72
+ response,
73
+ lambda __: [(no, provider_utils.parse_response(_)) for no, _ in __],
74
+ )
@@ -0,0 +1,23 @@
1
+ """Karrio Allied Express client settings."""
2
+
3
+ import attr
4
+ import karrio.providers.allied_express.utils as utils
5
+
6
+
7
+ @attr.s(auto_attribs=True)
8
+ class Settings(utils.Settings):
9
+ """Allied Express connection settings."""
10
+
11
+ # required carrier specific properties
12
+ username: str
13
+ password: str
14
+ account: str = None
15
+ service_type: utils.AlliedServiceType = "R" # type: ignore
16
+
17
+ # generic properties
18
+ id: str = None
19
+ test_mode: bool = False
20
+ carrier_id: str = "allied_express"
21
+ account_country_code: str = "AU"
22
+ metadata: dict = {}
23
+ config: dict = {}
@@ -0,0 +1,20 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.allied_express as mappers
3
+ import karrio.providers.allied_express.units as units
4
+ import karrio.providers.allied_express.utils as utils
5
+
6
+
7
+ METADATA = metadata.PluginMetadata(
8
+ status="beta",
9
+ id="allied_express",
10
+ label="Allied Express",
11
+ # Integrations
12
+ Mapper=mappers.Mapper,
13
+ Proxy=mappers.Proxy,
14
+ Settings=mappers.Settings,
15
+ # Data Units
16
+ is_hub=False,
17
+ services=units.ShippingService,
18
+ options=units.ShippingOption,
19
+ connection_configs=utils.ConnectionConfig,
20
+ )
@@ -0,0 +1,13 @@
1
+
2
+ from karrio.providers.allied_express.utils import Settings
3
+ from karrio.providers.allied_express.rate import parse_rate_response, rate_request
4
+ from karrio.providers.allied_express.shipment import (
5
+ parse_shipment_cancel_response,
6
+ parse_shipment_response,
7
+ shipment_cancel_request,
8
+ shipment_request,
9
+ )
10
+ from karrio.providers.allied_express.tracking import (
11
+ parse_tracking_response,
12
+ tracking_request,
13
+ )
@@ -0,0 +1,34 @@
1
+ import typing
2
+ import karrio.core.models as models
3
+ import karrio.providers.allied_express.utils as provider_utils
4
+
5
+
6
+ def parse_error_response(
7
+ response: provider_utils.AlliedResponse,
8
+ settings: provider_utils.Settings,
9
+ **kwargs,
10
+ ) -> typing.List[models.Message]:
11
+ errors = []
12
+
13
+ if response.error is not None:
14
+ errors.append(["500", response.error])
15
+
16
+ if response.is_error and response.data is not None:
17
+ errors.append(
18
+ [
19
+ "400",
20
+ response.data["result"].get("statusError")
21
+ or response.data["result"].get("errors"),
22
+ ]
23
+ )
24
+
25
+ return [
26
+ models.Message(
27
+ carrier_id=settings.carrier_id,
28
+ carrier_name=settings.carrier_name,
29
+ code=code,
30
+ message=message,
31
+ details={**kwargs},
32
+ )
33
+ for code, message in errors
34
+ ]
@@ -0,0 +1,147 @@
1
+ import karrio.schemas.allied_express.rate_request as allied
2
+ import karrio.schemas.allied_express.rate_response as rating
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.units as units
6
+ import karrio.core.models as models
7
+ import karrio.providers.allied_express.error as error
8
+ import karrio.providers.allied_express.utils as provider_utils
9
+ import karrio.providers.allied_express.units as provider_units
10
+
11
+
12
+ def parse_rate_response(
13
+ _response: lib.Deserializable[provider_utils.AlliedResponse],
14
+ settings: provider_utils.Settings,
15
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
16
+ response = _response.deserialize()
17
+
18
+ messages = error.parse_error_response(response, settings)
19
+ rates = [
20
+ _extract_details(rate.data["result"], settings, _response.ctx)
21
+ for rate in [response]
22
+ if not rate.is_error and "result" in (rate.data or {})
23
+ ]
24
+
25
+ return rates, messages
26
+
27
+
28
+ def _extract_details(
29
+ data: dict,
30
+ settings: provider_utils.Settings,
31
+ ctx: dict,
32
+ ) -> models.RateDetails:
33
+ rate = lib.to_object(rating.ResultType, data)
34
+ service = provider_units.ShippingService.map(
35
+ ctx.get("service")
36
+ or settings.connection_config.account_service_type.state
37
+ or settings.service_type
38
+ or "R"
39
+ )
40
+ charges = [
41
+ ("Job charge", lib.to_money(rate.jobCharge)),
42
+ *((s.chargeCode, lib.to_money(s.netValue)) for s in rate.surcharges),
43
+ ]
44
+
45
+ return models.RateDetails(
46
+ carrier_id=settings.carrier_id,
47
+ carrier_name=settings.carrier_name,
48
+ service=service.name_or_key,
49
+ total_charge=lib.to_money(rate.totalCharge),
50
+ currency=units.Currency.AUD.name,
51
+ extra_charges=[
52
+ models.ChargeDetails(
53
+ name=name,
54
+ amount=amount,
55
+ currency=units.Currency.AUD.name,
56
+ )
57
+ for name, amount in charges
58
+ if amount > 0
59
+ ],
60
+ meta=dict(
61
+ service_name=service.name,
62
+ ),
63
+ )
64
+
65
+
66
+ def rate_request(
67
+ payload: models.RateRequest,
68
+ settings: provider_utils.Settings,
69
+ ) -> lib.Serializable:
70
+ shipper = lib.to_address(payload.shipper)
71
+ recipient = lib.to_address(payload.recipient)
72
+ services = lib.to_services(payload.services, provider_units.ShippingService)
73
+ options = lib.to_shipping_options(
74
+ payload.options,
75
+ option_type=provider_units.ShippingOption,
76
+ )
77
+ packages = lib.to_packages(
78
+ payload.parcels,
79
+ options=options,
80
+ package_option_type=provider_units.ShippingOption,
81
+ shipping_options_initializer=provider_units.shipping_options_initializer,
82
+ )
83
+ service = (
84
+ getattr(services.first, "value", None)
85
+ or settings.connection_config.account_service_type.state
86
+ or settings.service_type
87
+ or "R"
88
+ )
89
+
90
+ request = allied.RateRequestType(
91
+ bookedBy=shipper.contact,
92
+ account=settings.account,
93
+ instructions=options.instructions.state or "N/A",
94
+ itemCount=len(packages),
95
+ items=[
96
+ allied.ItemType(
97
+ dangerous=(True if pkg.options.dangerous_good.state else False),
98
+ height=pkg.height.map(provider_units.MeasurementOptions).CM,
99
+ length=pkg.length.map(provider_units.MeasurementOptions).CM,
100
+ width=pkg.width.map(provider_units.MeasurementOptions).CM,
101
+ weight=pkg.weight.map(provider_units.MeasurementOptions).KG,
102
+ volume=pkg.volume.map(provider_units.MeasurementOptions).m3,
103
+ itemCount=(pkg.items.quantity if any(pkg.items) else 1),
104
+ )
105
+ for pkg in packages
106
+ ],
107
+ jobStopsP=allied.JobStopsType(
108
+ companyName=(shipper.company_name or shipper.contact),
109
+ contact=shipper.contact,
110
+ emailAddress=shipper.email or " ",
111
+ geographicAddress=allied.GeographicAddressType(
112
+ address1=shipper.address_line1,
113
+ address2=shipper.address_line2 or " ",
114
+ country=shipper.country_code,
115
+ postCode=shipper.postal_code,
116
+ state=shipper.state_code,
117
+ suburb=shipper.city,
118
+ ),
119
+ phoneNumber=shipper.phone_number or "(00) 0000 0000",
120
+ ),
121
+ jobStopsD=allied.JobStopsType(
122
+ companyName=(recipient.company_name or recipient.contact),
123
+ contact=recipient.contact,
124
+ emailAddress=recipient.email or " ",
125
+ geographicAddress=allied.GeographicAddressType(
126
+ address1=recipient.address_line1,
127
+ address2=recipient.address_line2 or " ",
128
+ country=recipient.country_code,
129
+ postCode=recipient.postal_code,
130
+ state=recipient.state_code,
131
+ suburb=recipient.city,
132
+ ),
133
+ phoneNumber=recipient.phone_number or "(00) 0000 0000",
134
+ ),
135
+ referenceNumbers=([payload.reference] if any(payload.reference or "") else []),
136
+ serviceLevel=service,
137
+ weight=packages.weight.map(provider_units.MeasurementOptions).KG,
138
+ volume=packages.volume.map(provider_units.MeasurementOptions).m3,
139
+ )
140
+
141
+ return lib.Serializable(
142
+ request,
143
+ lambda _: lib.to_json(_)
144
+ .replace("jobStopsP", "jobStops_P")
145
+ .replace("jobStopsD", "jobStops_D"),
146
+ dict(service=service),
147
+ )
@@ -0,0 +1,9 @@
1
+
2
+ from karrio.providers.allied_express.shipment.create import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.allied_express.shipment.cancel import (
7
+ parse_shipment_cancel_response,
8
+ shipment_cancel_request,
9
+ )
@@ -0,0 +1,42 @@
1
+ import karrio.schemas.allied_express.void_request as allied
2
+ import karrio.schemas.allied_express.void_response as shipping
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.models as models
6
+ import karrio.providers.allied_express.error as error
7
+ import karrio.providers.allied_express.utils as provider_utils
8
+ import karrio.providers.allied_express.units as provider_units
9
+
10
+
11
+ def parse_shipment_cancel_response(
12
+ _response: lib.Deserializable[provider_utils.AlliedResponse],
13
+ settings: provider_utils.Settings,
14
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
15
+ response = _response.deserialize()
16
+ messages = error.parse_error_response(response, settings)
17
+ success = not response.is_error and (response.data.get("result")) == "0"
18
+
19
+ confirmation = (
20
+ models.ConfirmationDetails(
21
+ carrier_id=settings.carrier_id,
22
+ carrier_name=settings.carrier_name,
23
+ operation="Cancel Shipment",
24
+ success=success,
25
+ )
26
+ if success
27
+ else None
28
+ )
29
+
30
+ return confirmation, messages
31
+
32
+
33
+ def shipment_cancel_request(
34
+ payload: models.ShipmentCancelRequest,
35
+ settings: provider_utils.Settings,
36
+ ) -> lib.Serializable:
37
+ request = allied.VoidRequestType(
38
+ shipmentno=payload.shipment_identifier,
39
+ postalcode=(payload.options or {}).get("postal_code", ""),
40
+ )
41
+
42
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,126 @@
1
+ import karrio.schemas.allied_express.label_request as allied
2
+ import karrio.schemas.allied_express.label_response as shipping
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.units as units
6
+ import karrio.core.models as models
7
+ import karrio.providers.allied_express.error as error
8
+ import karrio.providers.allied_express.utils as provider_utils
9
+ import karrio.providers.allied_express.units as provider_units
10
+
11
+
12
+ def parse_shipment_response(
13
+ _response: lib.Deserializable[provider_utils.AlliedResponse],
14
+ settings: provider_utils.Settings,
15
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
16
+ response = _response.deserialize()
17
+
18
+ messages = error.parse_error_response(response, settings)
19
+ shipment = (
20
+ _extract_details(response, settings, ctx=_response.ctx)
21
+ if not response.is_error and "result" in (response.data or {})
22
+ else None
23
+ )
24
+
25
+ return shipment, messages
26
+
27
+
28
+ def _extract_details(
29
+ data: provider_utils.AlliedResponse,
30
+ settings: provider_utils.Settings,
31
+ ctx: dict = {},
32
+ ) -> models.ShipmentDetails:
33
+ shipment: shipping.LabelResponseType = lib.to_object(
34
+ shipping.LabelResponseType, data.response
35
+ )
36
+ label = shipment.soapenvBody.ns1getLabelResponse.result
37
+
38
+ return models.ShipmentDetails(
39
+ carrier_id=settings.carrier_id,
40
+ carrier_name=settings.carrier_name,
41
+ tracking_number=shipment.Tracking,
42
+ shipment_identifier=shipment.Tracking,
43
+ label_type="PDF",
44
+ docs=models.Documents(label=label),
45
+ meta=dict(
46
+ postal_code=ctx.get("postal_code", ""),
47
+ ),
48
+ )
49
+
50
+
51
+ def shipment_request(
52
+ payload: models.ShipmentRequest,
53
+ settings: provider_utils.Settings,
54
+ ) -> lib.Serializable:
55
+ shipper = lib.to_address(payload.shipper)
56
+ recipient = lib.to_address(payload.recipient)
57
+ service = provider_units.ShippingService.map(payload.service).value_or_key
58
+ options = lib.to_shipping_options(
59
+ payload.options,
60
+ option_type=provider_units.ShippingOption,
61
+ )
62
+ packages = lib.to_packages(
63
+ payload.parcels,
64
+ options=options,
65
+ package_option_type=provider_units.ShippingOption,
66
+ shipping_options_initializer=provider_units.shipping_options_initializer,
67
+ )
68
+
69
+ request = allied.LabelRequestType(
70
+ bookedBy=shipper.contact,
71
+ account=settings.account,
72
+ instructions=options.instructions.state or "N/A",
73
+ itemCount=len(packages),
74
+ items=[
75
+ allied.ItemType(
76
+ dangerous=(True if pkg.options.dangerous_good.state else False),
77
+ height=pkg.height.map(provider_units.MeasurementOptions).CM,
78
+ length=pkg.length.map(provider_units.MeasurementOptions).CM,
79
+ width=pkg.width.map(provider_units.MeasurementOptions).CM,
80
+ weight=pkg.weight.map(provider_units.MeasurementOptions).KG,
81
+ volume=pkg.volume.map(provider_units.MeasurementOptions).m3,
82
+ itemCount=(pkg.items.quantity if any(pkg.items) else 1),
83
+ )
84
+ for pkg in packages
85
+ ],
86
+ jobStopsP=allied.JobStopsType(
87
+ companyName=(shipper.company_name or shipper.contact),
88
+ contact=shipper.contact,
89
+ emailAddress=shipper.email or " ",
90
+ geographicAddress=allied.GeographicAddressType(
91
+ address1=shipper.address_line1,
92
+ address2=shipper.address_line2 or " ",
93
+ country=shipper.country_code,
94
+ postCode=shipper.postal_code,
95
+ state=shipper.state_code,
96
+ suburb=shipper.city,
97
+ ),
98
+ phoneNumber=shipper.phone_number or "(00) 0000 0000",
99
+ ),
100
+ jobStopsD=allied.JobStopsType(
101
+ companyName=(recipient.company_name or recipient.contact),
102
+ contact=recipient.contact,
103
+ emailAddress=recipient.email or " ",
104
+ geographicAddress=allied.GeographicAddressType(
105
+ address1=recipient.address_line1,
106
+ address2=recipient.address_line2 or " ",
107
+ country=recipient.country_code,
108
+ postCode=recipient.postal_code,
109
+ state=recipient.state_code,
110
+ suburb=recipient.city,
111
+ ),
112
+ phoneNumber=recipient.phone_number or "(00) 0000 0000",
113
+ ),
114
+ referenceNumbers=([payload.reference] if any(payload.reference or "") else []),
115
+ weight=packages.weight.map(provider_units.MeasurementOptions).KG,
116
+ volume=packages.volume.map(provider_units.MeasurementOptions).m3,
117
+ serviceLevel=service,
118
+ )
119
+
120
+ return lib.Serializable(
121
+ request,
122
+ lambda _: lib.to_json(_)
123
+ .replace("jobStopsP", "jobStops_P")
124
+ .replace("jobStopsD", "jobStops_D"),
125
+ dict(postal_code=recipient.postal_code),
126
+ )
@@ -0,0 +1,86 @@
1
+ import karrio.schemas.allied_express.tracking_request as allied
2
+ import karrio.schemas.allied_express.tracking_response as tracking
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.units as units
6
+ import karrio.core.models as models
7
+ import karrio.providers.allied_express.error as error
8
+ import karrio.providers.allied_express.utils as provider_utils
9
+ import karrio.providers.allied_express.units as provider_units
10
+
11
+
12
+ def parse_tracking_response(
13
+ _response: lib.Deserializable[
14
+ typing.List[typing.Tuple[str, provider_utils.AlliedResponse]]
15
+ ],
16
+ settings: provider_utils.Settings,
17
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
18
+ responses = _response.deserialize()
19
+
20
+ messages: typing.List[models.Message] = sum(
21
+ [
22
+ error.parse_error_response(response, settings, tracking_number=_)
23
+ for _, response in responses
24
+ if response.is_error
25
+ ],
26
+ start=[],
27
+ )
28
+ tracking_details = [
29
+ _extract_details(details.data["result"], settings)
30
+ for _, details in responses
31
+ if not details.is_error and "result" in (details.data or {})
32
+ ]
33
+
34
+ return tracking_details, messages
35
+
36
+
37
+ def _extract_details(
38
+ data: dict,
39
+ settings: provider_utils.Settings,
40
+ ) -> models.TrackingDetails:
41
+ result = lib.to_object(tracking.ResultType, data)
42
+ description = result.statusBarcodesList.scannedStatus or "In Transit"
43
+ delivered = "delivered" in description
44
+ status = next(
45
+ (
46
+ status.name
47
+ for status in list(provider_units.TrackingStatus)
48
+ if description in status.value
49
+ ),
50
+ provider_units.TrackingStatus.in_transit.name,
51
+ )
52
+
53
+ return models.TrackingDetails(
54
+ carrier_id=settings.carrier_id,
55
+ carrier_name=settings.carrier_name,
56
+ tracking_number=result.statusBarcodesList.consignmentNote,
57
+ events=[
58
+ models.TrackingEvent(
59
+ code=result.statusBarcodesList.scannedBarcode,
60
+ location=result.statusBarcodesList.depotLocation,
61
+ description=description,
62
+ date=lib.fdate(
63
+ result.statusBarcodesList.scannnedTimestamp,
64
+ "%Y-%m-%dT%H:%M:%S.%f%z",
65
+ ),
66
+ time=lib.flocaltime(
67
+ result.statusBarcodesList.scannnedTimestamp,
68
+ "%Y-%m-%dT%H:%M:%S.%f%z",
69
+ ),
70
+ )
71
+ ],
72
+ status=status,
73
+ delivered=delivered,
74
+ )
75
+
76
+
77
+ def tracking_request(
78
+ payload: models.TrackingRequest,
79
+ settings: provider_utils.Settings,
80
+ ) -> lib.Serializable:
81
+ request = [
82
+ allied.TrackingRequestType(shipmentno=tracking_number)
83
+ for tracking_number in payload.tracking_numbers
84
+ ]
85
+
86
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,65 @@
1
+ import karrio.lib as lib
2
+ import karrio.core.units as units
3
+
4
+ MeasurementOptions = lib.units.MeasurementOptionsType(
5
+ quant=0.1,
6
+ min_volume=0.1,
7
+ )
8
+
9
+
10
+ class PackagingType(lib.StrEnum):
11
+ """Carrier specific packaging type"""
12
+
13
+ PACKAGE = "PACKAGE"
14
+
15
+ """ Unified Packaging type mapping """
16
+ envelope = PACKAGE
17
+ pak = PACKAGE
18
+ tube = PACKAGE
19
+ pallet = PACKAGE
20
+ small_box = PACKAGE
21
+ medium_box = PACKAGE
22
+ your_packaging = PACKAGE
23
+
24
+
25
+ class ShippingService(lib.StrEnum):
26
+ """Carrier specific services"""
27
+
28
+ allied_road_service = "R"
29
+ allied_parcel_service = "P"
30
+ allied_standard_pallet_service = "PT"
31
+ allied_oversized_pallet_service = "PT2"
32
+
33
+
34
+ class ShippingOption(lib.Enum):
35
+ """Carrier specific options"""
36
+
37
+ instructions = lib.OptionEnum("instructions")
38
+ dangerous_good = lib.OptionEnum("dangerous_good", bool)
39
+
40
+
41
+ def shipping_options_initializer(
42
+ options: dict,
43
+ package_options: units.ShippingOptions = None,
44
+ ) -> units.ShippingOptions:
45
+ """
46
+ Apply default values to the given options.
47
+ """
48
+
49
+ if package_options is not None:
50
+ options.update(package_options.content)
51
+
52
+ def items_filter(key: str) -> bool:
53
+ return key in ShippingOption # type: ignore
54
+
55
+ return units.ShippingOptions(options, ShippingOption, items_filter=items_filter)
56
+
57
+
58
+ class TrackingStatus(lib.Enum):
59
+ on_hold = ["Other", "DAMAGED"]
60
+ delivered = ["Freight has been delivered"]
61
+ in_transit = ["IN TRANSIT TO"]
62
+ delivery_failed = ["RETURN TO SENDER"]
63
+ delivery_delayed = ["RETURN TO DEPOT", "CARD LEFT", "LEFT IN DEPOT"]
64
+ out_for_delivery = ["It's on board with driver"]
65
+ ready_for_pickup = ["IN AGENT"]
@@ -0,0 +1,141 @@
1
+ import attr
2
+ import base64
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core as core
6
+
7
+ AlliedServiceType = lib.units.create_enum(
8
+ "AlliedServiceType",
9
+ ["R", "P", "PT", "PT2"],
10
+ )
11
+
12
+
13
+ class Settings(core.Settings):
14
+ """Allied Express connection settings."""
15
+
16
+ username: str
17
+ password: str
18
+ account: str = None
19
+ service_type: AlliedServiceType = AlliedServiceType.R # type: ignore
20
+ account_country_code: str = "AU"
21
+
22
+ @property
23
+ def carrier_name(self):
24
+ return "allied_express"
25
+
26
+ @property
27
+ def server_url(self):
28
+ return self.connection_config.server_url.state or (
29
+ "https://test.aet.mskaleem.com" if self.test_mode else "https://3plapi.com"
30
+ )
31
+
32
+ @property
33
+ def authorization(self):
34
+ pair = "%s:%s" % (self.username, self.password)
35
+ return base64.b64encode(pair.encode("utf-8")).decode("ascii")
36
+
37
+ @property
38
+ def connection_config(self) -> lib.units.Options:
39
+ return lib.to_connection_config(
40
+ self.config or {},
41
+ option_type=ConnectionConfig,
42
+ )
43
+
44
+
45
+ class ConnectionConfig(lib.Enum):
46
+ account_service_type = lib.OptionEnum("account_service_type", AlliedServiceType)
47
+ server_url = lib.OptionEnum("server_url")
48
+ text_color = lib.OptionEnum("text_color")
49
+ brand_color = lib.OptionEnum("brand_color")
50
+
51
+
52
+ @attr.s(auto_attribs=True)
53
+ class AlliedResponse:
54
+ error: typing.Union[str, None] = None
55
+ body: typing.Union[dict, None] = None
56
+ data: typing.Union[dict, None] = None
57
+ envelope: typing.Union[dict, None] = None
58
+ response: typing.Union[dict, None] = None
59
+ is_error: typing.Union[bool, None] = None
60
+
61
+
62
+ def parse_response(response: str) -> AlliedResponse:
63
+ _response = lib.failsafe(
64
+ lambda: lib.to_dict(
65
+ (
66
+ response.replace("soapenv:", "soapenv")
67
+ .replace("@xmlns:", "xmlns")
68
+ .replace("ns1:", "ns1")
69
+ )
70
+ )
71
+ )
72
+
73
+ if _response is None:
74
+ _error = response[: response.find(": {")].strip()
75
+ return AlliedResponse(
76
+ error=_error if any(_error) else response,
77
+ is_error=True,
78
+ )
79
+
80
+ _envelope = _response.get("soapenvEnvelope") or {}
81
+ _body = _envelope.get("soapenvBody") or _response.get("soapenvBody") or {}
82
+
83
+ if "ns1getShipmentsStatusResponse" in _body:
84
+ _data = _body["ns1getShipmentsStatusResponse"]
85
+ return AlliedResponse(
86
+ data=_data,
87
+ body=_body,
88
+ envelope=_envelope,
89
+ response=_response,
90
+ is_error=(
91
+ ("statusError" in (_data or {}).get("result", {}))
92
+ or ("errors" in (_data or {}).get("result", {}))
93
+ ),
94
+ )
95
+
96
+ if "ns1calculatePriceResponse" in _body:
97
+ _data = _body["ns1calculatePriceResponse"]
98
+ return AlliedResponse(
99
+ data=_data,
100
+ body=_body,
101
+ envelope=_envelope,
102
+ response=_response,
103
+ is_error=(
104
+ ("statusError" in (_data or {}).get("result", {}))
105
+ or ("errors" in (_data or {}).get("result", {}))
106
+ ),
107
+ )
108
+
109
+ if "ns1cancelDispatchJobResponse" in _body:
110
+ _data = _body["ns1cancelDispatchJobResponse"]
111
+ return AlliedResponse(
112
+ data=_data,
113
+ body=_body,
114
+ envelope=_envelope,
115
+ response=_response,
116
+ is_error=(
117
+ ((_data or {}).get("result") != "0")
118
+ or ("statusError" in (_data or {}).get("result", {}))
119
+ or ("errors" in (_data or {}).get("result", {}))
120
+ ),
121
+ )
122
+
123
+ if "ns1getLabelResponse" in _body:
124
+ _data = _body["ns1getLabelResponse"]
125
+ return AlliedResponse(
126
+ data=_data,
127
+ body=_body,
128
+ envelope=_envelope,
129
+ response=_response,
130
+ is_error=(
131
+ ("statusError" in (_data or {}).get("result", {}))
132
+ or ("errors" in (_data or {}).get("result", {}))
133
+ ),
134
+ )
135
+
136
+ return AlliedResponse(
137
+ body=_body,
138
+ data=_response,
139
+ envelope=_envelope,
140
+ response=_response,
141
+ )
File without changes
@@ -0,0 +1,48 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class ItemType:
8
+ dangerous: typing.Optional[bool] = None
9
+ height: typing.Optional[int] = None
10
+ itemCount: typing.Optional[int] = None
11
+ length: typing.Optional[int] = None
12
+ volume: typing.Optional[float] = None
13
+ weight: typing.Optional[int] = None
14
+ width: typing.Optional[int] = None
15
+
16
+
17
+ @attr.s(auto_attribs=True)
18
+ class GeographicAddressType:
19
+ address1: typing.Optional[str] = None
20
+ address2: typing.Optional[str] = None
21
+ country: typing.Optional[str] = None
22
+ postCode: typing.Optional[int] = None
23
+ state: typing.Optional[str] = None
24
+ suburb: typing.Optional[str] = None
25
+
26
+
27
+ @attr.s(auto_attribs=True)
28
+ class JobStopsType:
29
+ companyName: typing.Optional[str] = None
30
+ contact: typing.Optional[str] = None
31
+ emailAddress: typing.Optional[str] = None
32
+ geographicAddress: typing.Optional[GeographicAddressType] = jstruct.JStruct[GeographicAddressType]
33
+ phoneNumber: typing.Optional[str] = None
34
+
35
+
36
+ @attr.s(auto_attribs=True)
37
+ class LabelRequestType:
38
+ bookedBy: typing.Optional[str] = None
39
+ account: typing.Optional[str] = None
40
+ instructions: typing.Optional[str] = None
41
+ itemCount: typing.Optional[int] = None
42
+ items: typing.Optional[typing.List[ItemType]] = jstruct.JList[ItemType]
43
+ jobStopsP: typing.Optional[JobStopsType] = jstruct.JStruct[JobStopsType]
44
+ jobStopsD: typing.Optional[JobStopsType] = jstruct.JStruct[JobStopsType]
45
+ referenceNumbers: typing.Optional[typing.List[str]] = None
46
+ serviceLevel: typing.Optional[str] = None
47
+ volume: typing.Optional[float] = None
48
+ weight: typing.Optional[int] = None
@@ -0,0 +1,60 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class SurchargeType:
8
+ chargeCode: typing.Optional[str] = None
9
+ description: typing.Optional[str] = None
10
+ netValue: typing.Optional[str] = None
11
+ quantity: typing.Optional[int] = None
12
+
13
+
14
+ @attr.s(auto_attribs=True)
15
+ class ResultType:
16
+ jobCharge: typing.Optional[str] = None
17
+ surcharges: typing.Optional[typing.List[SurchargeType]] = jstruct.JList[SurchargeType]
18
+ totalCharge: typing.Optional[str] = None
19
+
20
+
21
+ @attr.s(auto_attribs=True)
22
+ class Ns1CalculatePriceResponseType:
23
+ xmlnsns1: typing.Optional[str] = None
24
+ result: typing.Optional[ResultType] = jstruct.JStruct[ResultType]
25
+
26
+
27
+ @attr.s(auto_attribs=True)
28
+ class SoapenvEnvelopeSoapenvBodyType:
29
+ ns1calculatePriceResponse: typing.Optional[Ns1CalculatePriceResponseType] = jstruct.JStruct[Ns1CalculatePriceResponseType]
30
+
31
+
32
+ @attr.s(auto_attribs=True)
33
+ class SoapenvEnvelopeType:
34
+ xmlnssoapenv: typing.Optional[str] = None
35
+ xmlnsxsd: typing.Optional[str] = None
36
+ xmlnsxsi: typing.Optional[str] = None
37
+ soapenvBody: typing.Optional[SoapenvEnvelopeSoapenvBodyType] = jstruct.JStruct[SoapenvEnvelopeSoapenvBodyType]
38
+
39
+
40
+ @attr.s(auto_attribs=True)
41
+ class PriceType:
42
+ soapenvEnvelope: typing.Optional[SoapenvEnvelopeType] = jstruct.JStruct[SoapenvEnvelopeType]
43
+
44
+
45
+ @attr.s(auto_attribs=True)
46
+ class Ns1GetLabelResponseType:
47
+ xmlnsns1: typing.Optional[str] = None
48
+ result: typing.Optional[str] = None
49
+
50
+
51
+ @attr.s(auto_attribs=True)
52
+ class LabelResponseSoapenvBodyType:
53
+ ns1getLabelResponse: typing.Optional[Ns1GetLabelResponseType] = jstruct.JStruct[Ns1GetLabelResponseType]
54
+
55
+
56
+ @attr.s(auto_attribs=True)
57
+ class LabelResponseType:
58
+ Price: typing.Optional[PriceType] = jstruct.JStruct[PriceType]
59
+ Tracking: typing.Optional[str] = None
60
+ soapenvBody: typing.Optional[LabelResponseSoapenvBodyType] = jstruct.JStruct[LabelResponseSoapenvBodyType]
@@ -0,0 +1,48 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class ItemType:
8
+ dangerous: typing.Optional[bool] = None
9
+ height: typing.Optional[int] = None
10
+ itemCount: typing.Optional[int] = None
11
+ length: typing.Optional[int] = None
12
+ volume: typing.Optional[float] = None
13
+ weight: typing.Optional[int] = None
14
+ width: typing.Optional[int] = None
15
+
16
+
17
+ @attr.s(auto_attribs=True)
18
+ class GeographicAddressType:
19
+ address1: typing.Optional[str] = None
20
+ address2: typing.Optional[str] = None
21
+ country: typing.Optional[str] = None
22
+ postCode: typing.Optional[int] = None
23
+ state: typing.Optional[str] = None
24
+ suburb: typing.Optional[str] = None
25
+
26
+
27
+ @attr.s(auto_attribs=True)
28
+ class JobStopsType:
29
+ companyName: typing.Optional[str] = None
30
+ contact: typing.Optional[str] = None
31
+ emailAddress: typing.Optional[str] = None
32
+ geographicAddress: typing.Optional[GeographicAddressType] = jstruct.JStruct[GeographicAddressType]
33
+ phoneNumber: typing.Optional[str] = None
34
+
35
+
36
+ @attr.s(auto_attribs=True)
37
+ class RateRequestType:
38
+ bookedBy: typing.Optional[str] = None
39
+ account: typing.Optional[str] = None
40
+ instructions: typing.Optional[str] = None
41
+ itemCount: typing.Optional[int] = None
42
+ items: typing.Optional[typing.List[ItemType]] = jstruct.JList[ItemType]
43
+ jobStopsP: typing.Optional[JobStopsType] = jstruct.JStruct[JobStopsType]
44
+ jobStopsD: typing.Optional[JobStopsType] = jstruct.JStruct[JobStopsType]
45
+ referenceNumbers: typing.Optional[typing.List[str]] = None
46
+ serviceLevel: typing.Optional[str] = None
47
+ volume: typing.Optional[float] = None
48
+ weight: typing.Optional[int] = None
@@ -0,0 +1,42 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class SurchargeType:
8
+ chargeCode: typing.Optional[str] = None
9
+ description: typing.Optional[str] = None
10
+ netValue: typing.Optional[str] = None
11
+ quantity: typing.Optional[int] = None
12
+
13
+
14
+ @attr.s(auto_attribs=True)
15
+ class ResultType:
16
+ jobCharge: typing.Optional[str] = None
17
+ surcharges: typing.Optional[typing.List[SurchargeType]] = jstruct.JList[SurchargeType]
18
+ totalCharge: typing.Optional[str] = None
19
+
20
+
21
+ @attr.s(auto_attribs=True)
22
+ class Ns1CalculatePriceResponseType:
23
+ xmlnsns1: typing.Optional[str] = None
24
+ result: typing.Optional[ResultType] = jstruct.JStruct[ResultType]
25
+
26
+
27
+ @attr.s(auto_attribs=True)
28
+ class SoapenvBodyType:
29
+ ns1calculatePriceResponse: typing.Optional[Ns1CalculatePriceResponseType] = jstruct.JStruct[Ns1CalculatePriceResponseType]
30
+
31
+
32
+ @attr.s(auto_attribs=True)
33
+ class SoapenvEnvelopeType:
34
+ xmlnssoapenv: typing.Optional[str] = None
35
+ xmlnsxsd: typing.Optional[str] = None
36
+ xmlnsxsi: typing.Optional[str] = None
37
+ soapenvBody: typing.Optional[SoapenvBodyType] = jstruct.JStruct[SoapenvBodyType]
38
+
39
+
40
+ @attr.s(auto_attribs=True)
41
+ class RateResponseType:
42
+ soapenvEnvelope: typing.Optional[SoapenvEnvelopeType] = jstruct.JStruct[SoapenvEnvelopeType]
@@ -0,0 +1,8 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class TrackingRequestType:
8
+ shipmentno: typing.Optional[int] = None
@@ -0,0 +1,33 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class StatusBarcodesListType:
8
+ consignmentNote: typing.Optional[str] = None
9
+ depotLocation: typing.Optional[str] = None
10
+ scannedBarcode: typing.Optional[str] = None
11
+ scannedStatus: typing.Optional[str] = None
12
+ scannnedTimestamp: typing.Optional[str] = None
13
+
14
+
15
+ @attr.s(auto_attribs=True)
16
+ class ResultType:
17
+ statusBarcodesList: typing.Optional[StatusBarcodesListType] = jstruct.JStruct[StatusBarcodesListType]
18
+
19
+
20
+ @attr.s(auto_attribs=True)
21
+ class Ns1GetShipmentsStatusResponseType:
22
+ xmlnsns1: typing.Optional[str] = None
23
+ result: typing.Optional[ResultType] = jstruct.JStruct[ResultType]
24
+
25
+
26
+ @attr.s(auto_attribs=True)
27
+ class SoapenvBodyType:
28
+ ns1getShipmentsStatusResponse: typing.Optional[Ns1GetShipmentsStatusResponseType] = jstruct.JStruct[Ns1GetShipmentsStatusResponseType]
29
+
30
+
31
+ @attr.s(auto_attribs=True)
32
+ class TrackingResponseType:
33
+ soapenvBody: typing.Optional[SoapenvBodyType] = jstruct.JStruct[SoapenvBodyType]
@@ -0,0 +1,9 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class VoidRequestType:
8
+ shipmentno: typing.Optional[int] = None
9
+ postalcode: typing.Optional[int] = None
@@ -0,0 +1,27 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class Ns1CancelDispatchJobResponseType:
8
+ xmlnsns1: typing.Optional[str] = None
9
+ result: typing.Optional[int] = None
10
+
11
+
12
+ @attr.s(auto_attribs=True)
13
+ class SoapenvBodyType:
14
+ ns1cancelDispatchJobResponse: typing.Optional[Ns1CancelDispatchJobResponseType] = jstruct.JStruct[Ns1CancelDispatchJobResponseType]
15
+
16
+
17
+ @attr.s(auto_attribs=True)
18
+ class SoapenvEnvelopeType:
19
+ xmlnssoapenv: typing.Optional[str] = None
20
+ xmlnsxsd: typing.Optional[str] = None
21
+ xmlnsxsi: typing.Optional[str] = None
22
+ soapenvBody: typing.Optional[SoapenvBodyType] = jstruct.JStruct[SoapenvBodyType]
23
+
24
+
25
+ @attr.s(auto_attribs=True)
26
+ class VoidResponseType:
27
+ soapenvEnvelope: typing.Optional[SoapenvEnvelopeType] = jstruct.JStruct[SoapenvEnvelopeType]
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: karrio_allied_express
3
+ Version: 2025.5rc1
4
+ Summary: Karrio - Allied Express Shipping Extension
5
+ Author-email: karrio <hello@karrio.io>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/karrioapi/karrio
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: karrio
14
+
15
+
16
+ # karrio.allied_express
17
+
18
+ This package is a Allied Express extension of the [karrio](https://pypi.org/project/karrio) multi carrier shipping SDK.
19
+
20
+ ## Requirements
21
+
22
+ `Python 3.7+`
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install karrio.allied_express
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ import karrio.sdk as karrio
34
+ from karrio.mappers.allied_express.settings import Settings
35
+
36
+
37
+ # Initialize a carrier gateway
38
+ allied_express = karrio.gateway["allied_express"].create(
39
+ Settings(
40
+ ...
41
+ )
42
+ )
43
+ ```
44
+
45
+ Check the [Karrio Mutli-carrier SDK docs](https://docs.karrio.io) for Shipping API requests
@@ -0,0 +1,28 @@
1
+ karrio/mappers/allied_express/__init__.py,sha256=8NXjNyWIlawq4SM8hVhxcgq-p6wfUrJ6XiSV5mHfl8Q,170
2
+ karrio/mappers/allied_express/mapper.py,sha256=BVBe2MNzJeHfCFrVxoOClkCbFJbt1s997RzySQ4x4c8,2040
3
+ karrio/mappers/allied_express/proxy.py,sha256=f5-LkSzCrL2GXixlZHyf2Dky0lYBieUXFvyllhFWq-w,2789
4
+ karrio/mappers/allied_express/settings.py,sha256=N6-y9jnKUaVy3xPwXhDYc0bsz4Jrqcflr13VJ-yV2Cc,582
5
+ karrio/plugins/allied_express/__init__.py,sha256=kZKqmVG4Mgqgd1H4DPNftyA1_6Mr3bPQGfQP8beGpWI,562
6
+ karrio/providers/allied_express/__init__.py,sha256=ZaBspoVXxTttEzcQgBwlzW8AiXkwISJcb9AMA1kLnpc,424
7
+ karrio/providers/allied_express/error.py,sha256=8AmPzB1LSRVq4OGoLgD9EkGJRZ5YxuGTvhcVn_waL8E,896
8
+ karrio/providers/allied_express/rate.py,sha256=WIyuI6ITD4aRKdPnZcekuGwUNFxYZEsbKunnD2N7e8g,5430
9
+ karrio/providers/allied_express/tracking.py,sha256=CZ0hy24MBg2CUFwkjE7OEt198E5MjxIIBhnnKqWe_Bc,2852
10
+ karrio/providers/allied_express/units.py,sha256=7q0pXy6dlqOujnYeWskpNpULxypESKafcCuqDDKW-7U,1679
11
+ karrio/providers/allied_express/utils.py,sha256=qWPrFRagbw_YT_iBGhiz8mL5VibKcUXLo8hT3npnq-w,4170
12
+ karrio/providers/allied_express/shipment/__init__.py,sha256=I66Y4E1sezhvF0ZmVTh2Tw1xj2xGpBnSdanYceaGdHg,245
13
+ karrio/providers/allied_express/shipment/cancel.py,sha256=UJ1sI4FxW0p11lLxdYX-8O53HwMoPj3z-Y4IRIaB_bI,1429
14
+ karrio/providers/allied_express/shipment/create.py,sha256=wcs8nNe9fXGvHp1Rr5Tqpc0dd5uYNREJQ5MhqIol9dc,4871
15
+ karrio/schemas/allied_express/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ karrio/schemas/allied_express/label_request.py,sha256=ngo3Kg9sRkJPntxKJc7z7gg75Q2jWM_OuZXDjs055J0,1653
17
+ karrio/schemas/allied_express/label_response.py,sha256=Jfith_7em6E3duxJStXG_nke4j0vwgzZMpuNEUGoeNE,1896
18
+ karrio/schemas/allied_express/rate_request.py,sha256=qMTxT9usodSYWdyYlNRvwMxF7DiwFRy0ijATXcxjIEc,1652
19
+ karrio/schemas/allied_express/rate_response.py,sha256=V6jBUje5U2gML5urk8s_WyWkMVMJL0-ypVbfJEbxRaA,1268
20
+ karrio/schemas/allied_express/tracking_request.py,sha256=JKZYhlhzo8f9e8TVcBg9jddR73TJJMo1x4AqKKySKbc,141
21
+ karrio/schemas/allied_express/tracking_response.py,sha256=dFxS-U4LxUW2IjaThfDxsoH5R9RJFe7qFYz6w1EEi1w,1010
22
+ karrio/schemas/allied_express/void_request.py,sha256=UEWq6f-WVtNtiZOwEaeSYMs6XuuDmAKs6tIcjIvPPxA,181
23
+ karrio/schemas/allied_express/void_response.py,sha256=ldHlbqsb-TsfSwBBXiXis6Blpx97fZ1VZn0jqYANR-c,801
24
+ karrio_allied_express-2025.5rc1.dist-info/METADATA,sha256=aFe4-zi-c6mZjFpcnCqVnUqrKjj7aMxv_5MlpByp2Oo,1048
25
+ karrio_allied_express-2025.5rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ karrio_allied_express-2025.5rc1.dist-info/entry_points.txt,sha256=kyxJtJulN0JYgZMTnWl1sIpiT2l_g7TNMlPu8w68w7Q,73
27
+ karrio_allied_express-2025.5rc1.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
28
+ karrio_allied_express-2025.5rc1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [karrio.plugins]
2
+ allied_express = karrio.plugins.allied_express:METADATA
@@ -0,0 +1,3 @@
1
+ dist
2
+ karrio
3
+ schemas