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.
- karrio/mappers/allied_express/__init__.py +3 -0
- karrio/mappers/allied_express/mapper.py +55 -0
- karrio/mappers/allied_express/proxy.py +74 -0
- karrio/mappers/allied_express/settings.py +23 -0
- karrio/plugins/allied_express/__init__.py +20 -0
- karrio/providers/allied_express/__init__.py +13 -0
- karrio/providers/allied_express/error.py +34 -0
- karrio/providers/allied_express/rate.py +147 -0
- karrio/providers/allied_express/shipment/__init__.py +9 -0
- karrio/providers/allied_express/shipment/cancel.py +42 -0
- karrio/providers/allied_express/shipment/create.py +126 -0
- karrio/providers/allied_express/tracking.py +86 -0
- karrio/providers/allied_express/units.py +65 -0
- karrio/providers/allied_express/utils.py +141 -0
- karrio/schemas/allied_express/__init__.py +0 -0
- karrio/schemas/allied_express/label_request.py +48 -0
- karrio/schemas/allied_express/label_response.py +60 -0
- karrio/schemas/allied_express/rate_request.py +48 -0
- karrio/schemas/allied_express/rate_response.py +42 -0
- karrio/schemas/allied_express/tracking_request.py +8 -0
- karrio/schemas/allied_express/tracking_response.py +33 -0
- karrio/schemas/allied_express/void_request.py +9 -0
- karrio/schemas/allied_express/void_response.py +27 -0
- karrio_allied_express-2025.5rc1.dist-info/METADATA +45 -0
- karrio_allied_express-2025.5rc1.dist-info/RECORD +28 -0
- karrio_allied_express-2025.5rc1.dist-info/WHEEL +5 -0
- karrio_allied_express-2025.5rc1.dist-info/entry_points.txt +2 -0
- karrio_allied_express-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -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,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,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,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,,
|