karrio-veho 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.
- karrio/mappers/veho/__init__.py +3 -0
- karrio/mappers/veho/mapper.py +58 -0
- karrio/mappers/veho/proxy.py +70 -0
- karrio/mappers/veho/settings.py +21 -0
- karrio/plugins/veho/__init__.py +29 -0
- karrio/providers/veho/__init__.py +16 -0
- karrio/providers/veho/error.py +32 -0
- karrio/providers/veho/rate.py +104 -0
- karrio/providers/veho/shipment/__init__.py +9 -0
- karrio/providers/veho/shipment/cancel.py +83 -0
- karrio/providers/veho/shipment/create.py +123 -0
- karrio/providers/veho/tracking.py +105 -0
- karrio/providers/veho/units.py +78 -0
- karrio/providers/veho/utils.py +43 -0
- karrio/schemas/veho/__init__.py +0 -0
- karrio/schemas/veho/error_response.py +16 -0
- karrio/schemas/veho/rate_request.py +26 -0
- karrio/schemas/veho/rate_response.py +24 -0
- karrio/schemas/veho/shipment_cancel_request.py +9 -0
- karrio/schemas/veho/shipment_cancel_response.py +10 -0
- karrio/schemas/veho/shipment_request.py +73 -0
- karrio/schemas/veho/shipment_response.py +28 -0
- karrio/schemas/veho/tracking_request.py +8 -0
- karrio/schemas/veho/tracking_response.py +31 -0
- karrio_veho-2025.5rc7.dist-info/METADATA +44 -0
- karrio_veho-2025.5rc7.dist-info/RECORD +29 -0
- karrio_veho-2025.5rc7.dist-info/WHEEL +5 -0
- karrio_veho-2025.5rc7.dist-info/entry_points.txt +2 -0
- karrio_veho-2025.5rc7.dist-info/top_level.txt +3 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
"""Karrio Veho 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.veho as provider
|
8
|
+
import karrio.mappers.veho.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_shipment_cancel_request(
|
30
|
+
self, payload: models.ShipmentCancelRequest
|
31
|
+
) -> lib.Serializable[str]:
|
32
|
+
return provider.shipment_cancel_request(payload, self.settings)
|
33
|
+
|
34
|
+
def create_cancel_shipment_request(
|
35
|
+
self, payload: models.ShipmentCancelRequest
|
36
|
+
) -> lib.Serializable[str]:
|
37
|
+
return provider.shipment_cancel_request(payload, self.settings)
|
38
|
+
|
39
|
+
def parse_cancel_shipment_response(
|
40
|
+
self, response: lib.Deserializable[str]
|
41
|
+
) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
|
42
|
+
return provider.parse_shipment_cancel_response(response, self.settings)
|
43
|
+
|
44
|
+
def parse_rate_response(
|
45
|
+
self, response: lib.Deserializable[str]
|
46
|
+
) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
|
47
|
+
return provider.parse_rate_response(response, self.settings)
|
48
|
+
|
49
|
+
def parse_shipment_response(
|
50
|
+
self, response: lib.Deserializable[str]
|
51
|
+
) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
|
52
|
+
return provider.parse_shipment_response(response, self.settings)
|
53
|
+
|
54
|
+
def parse_tracking_response(
|
55
|
+
self, response: lib.Deserializable[str]
|
56
|
+
) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
|
57
|
+
return provider.parse_tracking_response(response, self.settings)
|
58
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
"""Karrio Veho client proxy."""
|
2
|
+
|
3
|
+
import karrio.lib as lib
|
4
|
+
import karrio.api.proxy as proxy
|
5
|
+
import karrio.mappers.veho.settings as provider_settings
|
6
|
+
|
7
|
+
|
8
|
+
class Proxy(proxy.Proxy):
|
9
|
+
settings: provider_settings.Settings
|
10
|
+
|
11
|
+
def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
12
|
+
response = lib.request(
|
13
|
+
url=f"{self.settings.server_url}/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
|
+
"apikey": self.settings.api_key,
|
20
|
+
},
|
21
|
+
)
|
22
|
+
|
23
|
+
return lib.Deserializable(response, lib.to_dict)
|
24
|
+
|
25
|
+
def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
26
|
+
response = lib.request(
|
27
|
+
url=f"{self.settings.server_url}/shipments",
|
28
|
+
data=lib.to_json(request.serialize()),
|
29
|
+
trace=self.trace_as("json"),
|
30
|
+
method="POST",
|
31
|
+
headers={
|
32
|
+
"Content-Type": "application/json",
|
33
|
+
"apikey": self.settings.api_key,
|
34
|
+
},
|
35
|
+
)
|
36
|
+
|
37
|
+
return lib.Deserializable(response, lib.to_dict)
|
38
|
+
|
39
|
+
def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
40
|
+
shipment_id = request.serialize().get("shipmentIdentifier")
|
41
|
+
|
42
|
+
response = lib.request(
|
43
|
+
url=f"{self.settings.server_url}/shipments/{shipment_id}/cancel",
|
44
|
+
trace=self.trace_as("json"),
|
45
|
+
method="POST",
|
46
|
+
headers={
|
47
|
+
"Content-Type": "application/json",
|
48
|
+
"apikey": self.settings.api_key,
|
49
|
+
},
|
50
|
+
)
|
51
|
+
|
52
|
+
return lib.Deserializable(response, lib.to_dict)
|
53
|
+
|
54
|
+
def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
|
55
|
+
request_data = request.serialize()
|
56
|
+
tracking_numbers = request_data.get("trackingNumbers", [])
|
57
|
+
|
58
|
+
# Make a single request with all tracking numbers
|
59
|
+
response = lib.request(
|
60
|
+
url=f"{self.settings.server_url}/tracking",
|
61
|
+
data=lib.to_json({"trackingNumbers": tracking_numbers}),
|
62
|
+
trace=self.trace_as("json"),
|
63
|
+
method="POST",
|
64
|
+
headers={
|
65
|
+
"Content-Type": "application/json",
|
66
|
+
"apikey": self.settings.api_key,
|
67
|
+
},
|
68
|
+
)
|
69
|
+
|
70
|
+
return lib.Deserializable(response, lib.to_dict)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
"""Karrio Veho client settings."""
|
2
|
+
|
3
|
+
import attr
|
4
|
+
import karrio.providers.veho.utils as provider_utils
|
5
|
+
|
6
|
+
|
7
|
+
@attr.s(auto_attribs=True)
|
8
|
+
class Settings(provider_utils.Settings):
|
9
|
+
"""Veho connection settings."""
|
10
|
+
|
11
|
+
# Add carrier specific API connection properties here
|
12
|
+
api_key: str
|
13
|
+
account_number: str = None
|
14
|
+
|
15
|
+
# generic properties
|
16
|
+
id: str = None
|
17
|
+
test_mode: bool = False
|
18
|
+
carrier_id: str = "veho"
|
19
|
+
account_country_code: str = None
|
20
|
+
metadata: dict = {}
|
21
|
+
config: dict = {}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from karrio.core.metadata import PluginMetadata
|
2
|
+
|
3
|
+
from karrio.mappers.veho.mapper import Mapper
|
4
|
+
from karrio.mappers.veho.proxy import Proxy
|
5
|
+
from karrio.mappers.veho.settings import Settings
|
6
|
+
import karrio.providers.veho.units as units
|
7
|
+
import karrio.providers.veho.utils as utils
|
8
|
+
|
9
|
+
|
10
|
+
# This METADATA object is used by Karrio to discover and register this plugin
|
11
|
+
# when loaded through Python entrypoints or local plugin directories.
|
12
|
+
# The entrypoint is defined in pyproject.toml under [project.entry-points."karrio.plugins"]
|
13
|
+
METADATA = PluginMetadata(
|
14
|
+
id="veho",
|
15
|
+
label="Veho",
|
16
|
+
description="Veho shipping integration for Karrio",
|
17
|
+
# Integrations
|
18
|
+
Mapper=Mapper,
|
19
|
+
Proxy=Proxy,
|
20
|
+
Settings=Settings,
|
21
|
+
# Data Units
|
22
|
+
is_hub=False,
|
23
|
+
# options=units.ShippingOption,
|
24
|
+
# services=units.ShippingService,
|
25
|
+
connection_configs=utils.ConnectionConfig,
|
26
|
+
# Extra info
|
27
|
+
website="",
|
28
|
+
documentation="",
|
29
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""Karrio Veho provider imports."""
|
2
|
+
from karrio.providers.veho.utils import Settings
|
3
|
+
from karrio.providers.veho.rate import (
|
4
|
+
parse_rate_response,
|
5
|
+
rate_request,
|
6
|
+
)
|
7
|
+
from karrio.providers.veho.shipment import (
|
8
|
+
parse_shipment_cancel_response,
|
9
|
+
parse_shipment_response,
|
10
|
+
shipment_cancel_request,
|
11
|
+
shipment_request,
|
12
|
+
)
|
13
|
+
from karrio.providers.veho.tracking import (
|
14
|
+
parse_tracking_response,
|
15
|
+
tracking_request,
|
16
|
+
)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""Karrio Veho error parser."""
|
2
|
+
|
3
|
+
import typing
|
4
|
+
import karrio.lib as lib
|
5
|
+
import karrio.core.models as models
|
6
|
+
import karrio.providers.veho.utils as provider_utils
|
7
|
+
|
8
|
+
|
9
|
+
def parse_error_response(
|
10
|
+
response: dict,
|
11
|
+
settings: provider_utils.Settings,
|
12
|
+
**kwargs,
|
13
|
+
) -> typing.List[models.Message]:
|
14
|
+
errors: list = []
|
15
|
+
|
16
|
+
# Check for error in response
|
17
|
+
if isinstance(response, dict) and "error" in response:
|
18
|
+
error_data = response["error"]
|
19
|
+
errors.append(
|
20
|
+
models.Message(
|
21
|
+
carrier_id=settings.carrier_id,
|
22
|
+
carrier_name=settings.carrier_name,
|
23
|
+
code=error_data.get("code", ""),
|
24
|
+
message=error_data.get("message", ""),
|
25
|
+
details=dict(
|
26
|
+
details=error_data.get("details", ""),
|
27
|
+
**kwargs
|
28
|
+
),
|
29
|
+
)
|
30
|
+
)
|
31
|
+
|
32
|
+
return errors
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""Karrio Veho rate API implementation."""
|
2
|
+
|
3
|
+
import karrio.schemas.veho.rate_request as veho
|
4
|
+
import karrio.schemas.veho.rate_response as rating
|
5
|
+
|
6
|
+
import typing
|
7
|
+
import karrio.lib as lib
|
8
|
+
import karrio.core.units as units
|
9
|
+
import karrio.core.models as models
|
10
|
+
import karrio.providers.veho.error as error
|
11
|
+
import karrio.providers.veho.utils as provider_utils
|
12
|
+
import karrio.providers.veho.units as provider_units
|
13
|
+
|
14
|
+
|
15
|
+
def parse_rate_response(
|
16
|
+
_response: lib.Deserializable[dict],
|
17
|
+
settings: provider_utils.Settings,
|
18
|
+
) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
|
19
|
+
response = _response.deserialize()
|
20
|
+
|
21
|
+
messages = error.parse_error_response(response, settings)
|
22
|
+
|
23
|
+
# Extract rates array and convert each item to typed object
|
24
|
+
rates_data = response.get("rates", []) if isinstance(response, dict) else []
|
25
|
+
rates = [_extract_details(rate_data, settings) for rate_data in rates_data]
|
26
|
+
|
27
|
+
return rates, messages
|
28
|
+
|
29
|
+
|
30
|
+
def _extract_details(
|
31
|
+
data: dict,
|
32
|
+
settings: provider_utils.Settings,
|
33
|
+
) -> models.RateDetails:
|
34
|
+
"""
|
35
|
+
Extract rate details from rate response data using typed objects
|
36
|
+
"""
|
37
|
+
# Convert individual rate item to typed object
|
38
|
+
rate_item = lib.to_object(rating.RateItem, data)
|
39
|
+
|
40
|
+
# Use typed properties
|
41
|
+
service = rate_item.serviceCode or ""
|
42
|
+
service_name = rate_item.serviceName or ""
|
43
|
+
total = rate_item.totalCharge or 0.0
|
44
|
+
currency = rate_item.currency or "USD"
|
45
|
+
transit_days = int(rate_item.transitDays or 0)
|
46
|
+
|
47
|
+
return models.RateDetails(
|
48
|
+
carrier_id=settings.carrier_id,
|
49
|
+
carrier_name=settings.carrier_name,
|
50
|
+
service=service,
|
51
|
+
total_charge=total,
|
52
|
+
currency=currency,
|
53
|
+
transit_days=transit_days,
|
54
|
+
meta=dict(
|
55
|
+
service_name=service_name,
|
56
|
+
),
|
57
|
+
)
|
58
|
+
|
59
|
+
|
60
|
+
def rate_request(payload: models.RateRequest, settings: provider_utils.Settings) -> lib.Serializable:
|
61
|
+
"""Create a rate request for the carrier API."""
|
62
|
+
shipper = lib.to_address(payload.shipper)
|
63
|
+
recipient = lib.to_address(payload.recipient)
|
64
|
+
packages = lib.to_packages(payload.parcels)
|
65
|
+
package = packages.single
|
66
|
+
|
67
|
+
# Create simple request structure that matches test expectations
|
68
|
+
request = {
|
69
|
+
"shipper": {
|
70
|
+
"addressLine1": shipper.address_line1,
|
71
|
+
"city": shipper.city,
|
72
|
+
"postalCode": shipper.postal_code,
|
73
|
+
"countryCode": shipper.country_code,
|
74
|
+
"stateCode": shipper.state_code,
|
75
|
+
"personName": shipper.person_name,
|
76
|
+
"companyName": shipper.company_name,
|
77
|
+
"phoneNumber": shipper.phone_number,
|
78
|
+
"email": shipper.email,
|
79
|
+
},
|
80
|
+
"recipient": {
|
81
|
+
"addressLine1": recipient.address_line1,
|
82
|
+
"city": recipient.city,
|
83
|
+
"postalCode": recipient.postal_code,
|
84
|
+
"countryCode": recipient.country_code,
|
85
|
+
"stateCode": recipient.state_code,
|
86
|
+
"personName": recipient.person_name,
|
87
|
+
"companyName": recipient.company_name,
|
88
|
+
"phoneNumber": recipient.phone_number,
|
89
|
+
"email": recipient.email,
|
90
|
+
},
|
91
|
+
"packages": [
|
92
|
+
{
|
93
|
+
"weight": package.weight.value,
|
94
|
+
"weightUnit": package.weight.unit,
|
95
|
+
"length": package.length.value if package.length else None,
|
96
|
+
"width": package.width.value if package.width else None,
|
97
|
+
"height": package.height.value if package.height else None,
|
98
|
+
"dimensionUnit": package.dimension_unit if package.dimension_unit else None,
|
99
|
+
"packagingType": package.packaging_type or "BOX",
|
100
|
+
}
|
101
|
+
],
|
102
|
+
}
|
103
|
+
|
104
|
+
return lib.Serializable(request, lib.to_dict)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
"""Karrio Veho shipment cancel API implementation."""
|
2
|
+
|
3
|
+
import karrio.schemas.veho.shipment_cancel_request as veho
|
4
|
+
import karrio.schemas.veho.shipment_cancel_response as shipping
|
5
|
+
|
6
|
+
import typing
|
7
|
+
import karrio.lib as lib
|
8
|
+
import karrio.core.units as units
|
9
|
+
import karrio.core.models as models
|
10
|
+
import karrio.providers.veho.error as error
|
11
|
+
import karrio.providers.veho.utils as provider_utils
|
12
|
+
import karrio.providers.veho.units as provider_units
|
13
|
+
|
14
|
+
|
15
|
+
def parse_shipment_cancel_response(
|
16
|
+
_response: lib.Deserializable[dict],
|
17
|
+
settings: provider_utils.Settings,
|
18
|
+
) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
|
19
|
+
"""
|
20
|
+
Parse shipment cancellation response from carrier API
|
21
|
+
|
22
|
+
_response: The carrier response to deserialize
|
23
|
+
settings: The carrier connection settings
|
24
|
+
|
25
|
+
Returns a tuple with (ConfirmationDetails, List[Message])
|
26
|
+
"""
|
27
|
+
response = _response.deserialize()
|
28
|
+
messages = error.parse_error_response(response, settings)
|
29
|
+
|
30
|
+
confirmation = _extract_details(response, settings) if "success" in response else None
|
31
|
+
|
32
|
+
return confirmation, messages
|
33
|
+
|
34
|
+
|
35
|
+
def _extract_details(
|
36
|
+
data: dict,
|
37
|
+
settings: provider_utils.Settings,
|
38
|
+
) -> models.ConfirmationDetails:
|
39
|
+
"""Extract cancellation confirmation details from carrier response data using typed objects."""
|
40
|
+
# Convert to typed object
|
41
|
+
cancel_response = lib.to_object(shipping.ShipmentCancelResponse, data)
|
42
|
+
success = cancel_response.success or False
|
43
|
+
|
44
|
+
return models.ConfirmationDetails(
|
45
|
+
carrier_id=settings.carrier_id,
|
46
|
+
carrier_name=settings.carrier_name,
|
47
|
+
operation="Cancel Shipment",
|
48
|
+
success=success,
|
49
|
+
)
|
50
|
+
|
51
|
+
|
52
|
+
def shipment_cancel_request(
|
53
|
+
payload: models.ShipmentCancelRequest,
|
54
|
+
settings: provider_utils.Settings,
|
55
|
+
) -> lib.Serializable:
|
56
|
+
"""
|
57
|
+
Create a shipment cancellation request for the carrier API
|
58
|
+
|
59
|
+
payload: The standardized ShipmentCancelRequest from karrio
|
60
|
+
settings: The carrier connection settings
|
61
|
+
|
62
|
+
Returns a Serializable object that can be sent to the carrier API
|
63
|
+
"""
|
64
|
+
|
65
|
+
# Create JSON request for shipment cancellation
|
66
|
+
# Example implementation:
|
67
|
+
# import karrio.schemas.veho.shipment_cancel_request as veho_req
|
68
|
+
#
|
69
|
+
# request = veho_req.ShipmentCancelRequestType(
|
70
|
+
# shipmentId=payload.shipment_identifier,
|
71
|
+
# accountNumber=settings.account_number,
|
72
|
+
# # Add any other required fields
|
73
|
+
# )
|
74
|
+
#
|
75
|
+
# return lib.Serializable(request, lib.to_dict)
|
76
|
+
|
77
|
+
# For development, return a simple JSON request
|
78
|
+
request = {
|
79
|
+
"shipmentIdentifier": payload.shipment_identifier,
|
80
|
+
}
|
81
|
+
|
82
|
+
return lib.Serializable(request, lib.to_dict)
|
83
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
"""Karrio Veho shipment API implementation."""
|
2
|
+
|
3
|
+
import karrio.schemas.veho.shipment_request as veho
|
4
|
+
import karrio.schemas.veho.shipment_response as shipping
|
5
|
+
|
6
|
+
import typing
|
7
|
+
import karrio.lib as lib
|
8
|
+
import karrio.core.units as units
|
9
|
+
import karrio.core.models as models
|
10
|
+
import karrio.providers.veho.error as error
|
11
|
+
import karrio.providers.veho.utils as provider_utils
|
12
|
+
import karrio.providers.veho.units as provider_units
|
13
|
+
|
14
|
+
|
15
|
+
def parse_shipment_response(
|
16
|
+
_response: lib.Deserializable[dict],
|
17
|
+
settings: provider_utils.Settings,
|
18
|
+
) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
|
19
|
+
response = _response.deserialize()
|
20
|
+
messages = error.parse_error_response(response, settings)
|
21
|
+
shipment = _extract_details(response, settings) if "error" not in response else {}
|
22
|
+
|
23
|
+
return shipment, messages
|
24
|
+
|
25
|
+
|
26
|
+
def _extract_details(
|
27
|
+
data: dict,
|
28
|
+
settings: provider_utils.Settings,
|
29
|
+
) -> models.ShipmentDetails:
|
30
|
+
"""Extract shipment details from carrier response data using typed objects."""
|
31
|
+
# Extract shipment data and convert to typed object
|
32
|
+
shipment_data = data.get("shipment", {})
|
33
|
+
|
34
|
+
if not shipment_data:
|
35
|
+
return models.ShipmentDetails(
|
36
|
+
carrier_id=settings.carrier_id,
|
37
|
+
carrier_name=settings.carrier_name,
|
38
|
+
)
|
39
|
+
|
40
|
+
# Convert to typed object
|
41
|
+
shipment = lib.to_object(shipping.ShipmentData, shipment_data)
|
42
|
+
|
43
|
+
tracking_number = shipment.trackingNumber or ""
|
44
|
+
shipment_id = shipment.shipmentId or ""
|
45
|
+
service_code = shipment.serviceCode or ""
|
46
|
+
|
47
|
+
# Extract label data from labelData object
|
48
|
+
label_image = None
|
49
|
+
label_format = "PDF"
|
50
|
+
if shipment.labelData:
|
51
|
+
label_data = lib.to_object(shipping.LabelData, shipment.labelData) if isinstance(shipment.labelData, dict) else shipment.labelData
|
52
|
+
label_image = label_data.image
|
53
|
+
label_format = label_data.format or "PDF"
|
54
|
+
|
55
|
+
# Extract invoice image
|
56
|
+
invoice_image = shipment.invoiceImage
|
57
|
+
|
58
|
+
return models.ShipmentDetails(
|
59
|
+
carrier_id=settings.carrier_id,
|
60
|
+
carrier_name=settings.carrier_name,
|
61
|
+
tracking_number=tracking_number,
|
62
|
+
shipment_identifier=shipment_id,
|
63
|
+
label_type=label_format,
|
64
|
+
docs=models.Documents(
|
65
|
+
label=label_image,
|
66
|
+
invoice=invoice_image,
|
67
|
+
) if label_image or invoice_image else None,
|
68
|
+
meta=dict(
|
69
|
+
service_code=service_code,
|
70
|
+
),
|
71
|
+
)
|
72
|
+
|
73
|
+
|
74
|
+
def shipment_request(
|
75
|
+
payload: models.ShipmentRequest,
|
76
|
+
settings: provider_utils.Settings,
|
77
|
+
) -> lib.Serializable:
|
78
|
+
"""Create a shipment request for the carrier API."""
|
79
|
+
shipper = lib.to_address(payload.shipper)
|
80
|
+
recipient = lib.to_address(payload.recipient)
|
81
|
+
package = lib.to_packages(payload.parcels).single
|
82
|
+
options = lib.to_shipping_options(payload.options)
|
83
|
+
service = provider_units.ShippingService.map(payload.service).value_or_key
|
84
|
+
|
85
|
+
request = {
|
86
|
+
"shipper": {
|
87
|
+
"addressLine1": shipper.address_line1,
|
88
|
+
"city": shipper.city,
|
89
|
+
"postalCode": shipper.postal_code,
|
90
|
+
"countryCode": shipper.country_code,
|
91
|
+
"stateCode": shipper.state_code,
|
92
|
+
"personName": shipper.person_name,
|
93
|
+
"companyName": shipper.company_name,
|
94
|
+
"phoneNumber": shipper.phone_number,
|
95
|
+
"email": shipper.email,
|
96
|
+
},
|
97
|
+
"recipient": {
|
98
|
+
"addressLine1": recipient.address_line1,
|
99
|
+
"city": recipient.city,
|
100
|
+
"postalCode": recipient.postal_code,
|
101
|
+
"countryCode": recipient.country_code,
|
102
|
+
"stateCode": recipient.state_code,
|
103
|
+
"personName": recipient.person_name,
|
104
|
+
"companyName": recipient.company_name,
|
105
|
+
"phoneNumber": recipient.phone_number,
|
106
|
+
"email": recipient.email,
|
107
|
+
},
|
108
|
+
"packages": [
|
109
|
+
{
|
110
|
+
"weight": package.weight.value,
|
111
|
+
"weightUnit": package.weight.unit,
|
112
|
+
"length": package.length.value if package.length else None,
|
113
|
+
"width": package.width.value if package.width else None,
|
114
|
+
"height": package.height.value if package.height else None,
|
115
|
+
"dimensionUnit": package.dimension_unit if package.dimension_unit else None,
|
116
|
+
"packagingType": package.packaging_type or "BOX",
|
117
|
+
}
|
118
|
+
],
|
119
|
+
"serviceCode": service,
|
120
|
+
"labelFormat": payload.label_type or "PDF",
|
121
|
+
}
|
122
|
+
|
123
|
+
return lib.Serializable(request, lib.to_dict)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
"""Karrio Veho tracking API implementation."""
|
2
|
+
|
3
|
+
import karrio.schemas.veho.tracking_request as veho
|
4
|
+
import karrio.schemas.veho.tracking_response as tracking
|
5
|
+
|
6
|
+
import typing
|
7
|
+
import karrio.lib as lib
|
8
|
+
import karrio.core.units as units
|
9
|
+
import karrio.core.models as models
|
10
|
+
import karrio.providers.veho.error as error
|
11
|
+
import karrio.providers.veho.utils as provider_utils
|
12
|
+
import karrio.providers.veho.units as provider_units
|
13
|
+
|
14
|
+
|
15
|
+
def parse_tracking_response(
|
16
|
+
_response: lib.Deserializable[dict],
|
17
|
+
settings: provider_utils.Settings,
|
18
|
+
) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
|
19
|
+
response = _response.deserialize()
|
20
|
+
messages = error.parse_error_response(response, settings)
|
21
|
+
|
22
|
+
# Extract tracking info array and convert each item to typed object
|
23
|
+
tracking_info_data = response.get("trackingInfo", []) if isinstance(response, dict) else []
|
24
|
+
tracking_details = [
|
25
|
+
_extract_details(info_data, settings)
|
26
|
+
for info_data in tracking_info_data
|
27
|
+
]
|
28
|
+
|
29
|
+
return tracking_details, messages
|
30
|
+
|
31
|
+
|
32
|
+
def _extract_details(
|
33
|
+
data: dict,
|
34
|
+
settings: provider_utils.Settings,
|
35
|
+
) -> models.TrackingDetails:
|
36
|
+
"""Extract tracking details from carrier response data using typed objects."""
|
37
|
+
# Convert to typed object
|
38
|
+
tracking_info = lib.to_object(tracking.TrackingInfo, data)
|
39
|
+
|
40
|
+
tracking_number = tracking_info.trackingNumber or ""
|
41
|
+
status = tracking_info.status or ""
|
42
|
+
estimated_delivery = tracking_info.estimatedDelivery
|
43
|
+
|
44
|
+
# Extract events from typed objects
|
45
|
+
events = []
|
46
|
+
if tracking_info.events:
|
47
|
+
for event_data in tracking_info.events:
|
48
|
+
# Convert event to typed object if it's still a dict
|
49
|
+
if isinstance(event_data, dict):
|
50
|
+
event = lib.to_object(tracking.TrackingEvent, event_data)
|
51
|
+
else:
|
52
|
+
event = event_data
|
53
|
+
|
54
|
+
events.append({
|
55
|
+
"date": event.date or "",
|
56
|
+
"time": event.time or "",
|
57
|
+
"code": event.code or "",
|
58
|
+
"description": event.description or "",
|
59
|
+
"location": event.location or ""
|
60
|
+
})
|
61
|
+
|
62
|
+
# Map carrier status to karrio standard tracking status
|
63
|
+
mapped_status = next(
|
64
|
+
(
|
65
|
+
status_enum.name
|
66
|
+
for status_enum in list(provider_units.TrackingStatus)
|
67
|
+
if status in status_enum.value
|
68
|
+
),
|
69
|
+
status,
|
70
|
+
)
|
71
|
+
|
72
|
+
return models.TrackingDetails(
|
73
|
+
carrier_id=settings.carrier_id,
|
74
|
+
carrier_name=settings.carrier_name,
|
75
|
+
tracking_number=tracking_number,
|
76
|
+
events=[
|
77
|
+
models.TrackingEvent(
|
78
|
+
date=lib.fdate(event["date"]),
|
79
|
+
description=event["description"],
|
80
|
+
code=event["code"],
|
81
|
+
time=event["time"], # Keep original time format
|
82
|
+
location=event["location"],
|
83
|
+
)
|
84
|
+
for event in events
|
85
|
+
],
|
86
|
+
estimated_delivery=lib.fdate(estimated_delivery) if estimated_delivery else None,
|
87
|
+
delivered=mapped_status == "delivered" if mapped_status == "delivered" else None,
|
88
|
+
status=mapped_status,
|
89
|
+
)
|
90
|
+
|
91
|
+
|
92
|
+
def tracking_request(
|
93
|
+
payload: models.TrackingRequest,
|
94
|
+
settings: provider_utils.Settings,
|
95
|
+
) -> lib.Serializable:
|
96
|
+
"""Create a tracking request for the carrier API."""
|
97
|
+
tracking_numbers = payload.tracking_numbers
|
98
|
+
reference = payload.reference
|
99
|
+
|
100
|
+
request = {
|
101
|
+
"trackingNumbers": tracking_numbers,
|
102
|
+
"reference": reference,
|
103
|
+
}
|
104
|
+
|
105
|
+
return lib.Serializable(request, lib.to_dict)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import karrio.lib as lib
|
2
|
+
import karrio.core.units as units
|
3
|
+
|
4
|
+
|
5
|
+
class PackagingType(lib.StrEnum):
|
6
|
+
"""Veho specific packaging type"""
|
7
|
+
PACKAGE = "PACKAGE"
|
8
|
+
|
9
|
+
"""Unified Packaging type mapping"""
|
10
|
+
envelope = PACKAGE
|
11
|
+
pak = PACKAGE
|
12
|
+
tube = PACKAGE
|
13
|
+
pallet = PACKAGE
|
14
|
+
small_box = PACKAGE
|
15
|
+
medium_box = PACKAGE
|
16
|
+
your_packaging = PACKAGE
|
17
|
+
|
18
|
+
|
19
|
+
class ShippingService(lib.StrEnum):
|
20
|
+
"""Veho specific services"""
|
21
|
+
veho_ground_plus = "groundPlus"
|
22
|
+
veho_premium_economy = "premiumEconomy"
|
23
|
+
|
24
|
+
|
25
|
+
class ShippingOption(lib.Enum):
|
26
|
+
"""Veho specific options"""
|
27
|
+
delivery_max_datetime = lib.OptionEnum("delivery_max_datetime", str)
|
28
|
+
label_date = lib.OptionEnum("label_date", str)
|
29
|
+
|
30
|
+
"""Unified Option type mapping"""
|
31
|
+
insurance = delivery_max_datetime
|
32
|
+
|
33
|
+
|
34
|
+
def shipping_options_initializer(
|
35
|
+
options: dict,
|
36
|
+
package_options: units.ShippingOptions = None,
|
37
|
+
) -> units.ShippingOptions:
|
38
|
+
"""
|
39
|
+
Apply default values to the given options.
|
40
|
+
"""
|
41
|
+
_options = options.copy()
|
42
|
+
|
43
|
+
if package_options is not None:
|
44
|
+
_options.update(package_options.content)
|
45
|
+
|
46
|
+
def items_filter(key: str) -> bool:
|
47
|
+
return key in ShippingOption
|
48
|
+
|
49
|
+
return units.ShippingOptions(_options, ShippingOption, items_filter=items_filter)
|
50
|
+
|
51
|
+
|
52
|
+
class TrackingStatus(lib.Enum):
|
53
|
+
on_hold = ["on_hold"]
|
54
|
+
delivered = ["delivered"]
|
55
|
+
in_transit = ["in_transit"]
|
56
|
+
delivery_failed = ["delivery_failed"]
|
57
|
+
delivery_delayed = ["delivery_delayed"]
|
58
|
+
out_for_delivery = ["out_for_delivery"]
|
59
|
+
ready_for_pickup = ["ready_for_pickup"]
|
60
|
+
|
61
|
+
|
62
|
+
def is_ground_plus(service: str) -> bool:
|
63
|
+
"""Check if the service is Veho Ground Plus"""
|
64
|
+
return service == ShippingService.veho_ground_plus
|
65
|
+
|
66
|
+
|
67
|
+
def is_premium_economy(service: str) -> bool:
|
68
|
+
"""Check if the service is Veho Premium Economy"""
|
69
|
+
return service == ShippingService.veho_premium_economy
|
70
|
+
|
71
|
+
|
72
|
+
def get_service_name(service: str) -> str:
|
73
|
+
"""Get the display name for a service"""
|
74
|
+
service_names = {
|
75
|
+
ShippingService.veho_ground_plus: "Veho Ground Plus",
|
76
|
+
ShippingService.veho_premium_economy: "Veho Premium Economy",
|
77
|
+
}
|
78
|
+
return service_names.get(service, service)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import base64
|
2
|
+
import datetime
|
3
|
+
import karrio.lib as lib
|
4
|
+
import karrio.core as core
|
5
|
+
import karrio.core.errors as errors
|
6
|
+
|
7
|
+
|
8
|
+
class Settings(core.Settings):
|
9
|
+
"""Veho connection settings."""
|
10
|
+
|
11
|
+
api_key: str
|
12
|
+
account_number: str = None
|
13
|
+
|
14
|
+
@property
|
15
|
+
def carrier_name(self):
|
16
|
+
return "veho"
|
17
|
+
|
18
|
+
@property
|
19
|
+
def server_url(self):
|
20
|
+
return (
|
21
|
+
"https://api.shipveho.com"
|
22
|
+
if not self.test_mode
|
23
|
+
else "https://sandbox.api.shipveho.com"
|
24
|
+
)
|
25
|
+
|
26
|
+
@property
|
27
|
+
def tracking_url(self):
|
28
|
+
return "https://www.shipveho.com/tracking/{}"
|
29
|
+
|
30
|
+
@property
|
31
|
+
def connection_config(self) -> lib.units.Options:
|
32
|
+
return lib.to_connection_config(
|
33
|
+
self.config or {},
|
34
|
+
option_type=ConnectionConfig,
|
35
|
+
)
|
36
|
+
|
37
|
+
|
38
|
+
class ConnectionConfig(lib.Enum):
|
39
|
+
shipping_options = lib.OptionEnum("shipping_options", list)
|
40
|
+
shipping_services = lib.OptionEnum("shipping_services", list)
|
41
|
+
label_type = lib.OptionEnum("label_type", str, "PDF")
|
42
|
+
delivery_max_datetime = lib.OptionEnum("delivery_max_datetime", str)
|
43
|
+
label_date = lib.OptionEnum("label_date", str)
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from attr import define, field
|
2
|
+
from typing import Optional, List
|
3
|
+
|
4
|
+
|
5
|
+
@define
|
6
|
+
class ErrorDetail:
|
7
|
+
code: Optional[str] = None
|
8
|
+
message: Optional[str] = None
|
9
|
+
field: Optional[str] = None
|
10
|
+
|
11
|
+
|
12
|
+
@define
|
13
|
+
class ErrorResponse:
|
14
|
+
errors: Optional[List[ErrorDetail]] = None
|
15
|
+
message: Optional[str] = None
|
16
|
+
status_code: Optional[int] = None
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
Veho Rate Request Schema based on OpenAPI spec
|
3
|
+
"""
|
4
|
+
|
5
|
+
import attr
|
6
|
+
import typing
|
7
|
+
|
8
|
+
|
9
|
+
@attr.s(auto_attribs=True)
|
10
|
+
class Package:
|
11
|
+
"""Package for quote request"""
|
12
|
+
length: float
|
13
|
+
width: float
|
14
|
+
height: float
|
15
|
+
weight: float
|
16
|
+
|
17
|
+
|
18
|
+
@attr.s(auto_attribs=True)
|
19
|
+
class SimpleQuoteRequest:
|
20
|
+
"""Veho Simple Quote Request - matches OpenAPI spec"""
|
21
|
+
|
22
|
+
originationZip: str
|
23
|
+
deliveryZip: str
|
24
|
+
packages: typing.List[Package]
|
25
|
+
shipDate: typing.Optional[str] = None
|
26
|
+
serviceClass: typing.Optional[str] = "groundPlus"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"""
|
2
|
+
Veho Rate Response Schema matching test data structure
|
3
|
+
"""
|
4
|
+
|
5
|
+
import attr
|
6
|
+
import typing
|
7
|
+
|
8
|
+
|
9
|
+
@attr.s(auto_attribs=True)
|
10
|
+
class RateItem:
|
11
|
+
"""Veho Rate Item - matches test API structure"""
|
12
|
+
|
13
|
+
serviceCode: typing.Optional[str] = None
|
14
|
+
serviceName: typing.Optional[str] = None
|
15
|
+
totalCharge: typing.Optional[float] = None
|
16
|
+
currency: typing.Optional[str] = "USD"
|
17
|
+
transitDays: typing.Optional[int] = None
|
18
|
+
|
19
|
+
|
20
|
+
@attr.s(auto_attribs=True)
|
21
|
+
class RateResponse:
|
22
|
+
"""Veho Rate Response containing rates array"""
|
23
|
+
|
24
|
+
rates: typing.Optional[typing.List[RateItem]] = None
|
@@ -0,0 +1,73 @@
|
|
1
|
+
"""
|
2
|
+
Veho Shipment Request Schema based on OpenAPI spec
|
3
|
+
"""
|
4
|
+
|
5
|
+
import attr
|
6
|
+
import typing
|
7
|
+
|
8
|
+
|
9
|
+
@attr.s(auto_attribs=True)
|
10
|
+
class OrderAddress:
|
11
|
+
"""Order Address - matches OpenAPI spec"""
|
12
|
+
street: str
|
13
|
+
city: str
|
14
|
+
state: str
|
15
|
+
zipCode: str
|
16
|
+
apartment: typing.Optional[str] = None
|
17
|
+
country: typing.Optional[str] = None
|
18
|
+
|
19
|
+
|
20
|
+
@attr.s(auto_attribs=True)
|
21
|
+
class FromAddress:
|
22
|
+
"""From Address - matches OpenAPI spec"""
|
23
|
+
addressLine1: str
|
24
|
+
city: str
|
25
|
+
state: str
|
26
|
+
zipCode: str
|
27
|
+
addressLine2: typing.Optional[str] = None
|
28
|
+
country: typing.Optional[str] = None
|
29
|
+
|
30
|
+
|
31
|
+
@attr.s(auto_attribs=True)
|
32
|
+
class SpecialHandling:
|
33
|
+
"""Special Handling - matches OpenAPI spec"""
|
34
|
+
dryIce: typing.Optional[dict] = None
|
35
|
+
hazmat: typing.Optional[dict] = None
|
36
|
+
|
37
|
+
|
38
|
+
@attr.s(auto_attribs=True)
|
39
|
+
class OrderRequestPackage:
|
40
|
+
"""Order Request Package - matches OpenAPI spec"""
|
41
|
+
externalId: typing.Optional[str] = None
|
42
|
+
length: typing.Optional[float] = None
|
43
|
+
width: typing.Optional[float] = None
|
44
|
+
height: typing.Optional[float] = None
|
45
|
+
weight: typing.Optional[float] = None
|
46
|
+
declaredValue: typing.Optional[float] = None
|
47
|
+
barCode: typing.Optional[str] = None
|
48
|
+
description: typing.Optional[str] = None
|
49
|
+
specialHandling: typing.Optional[SpecialHandling] = None
|
50
|
+
quoteId: typing.Optional[str] = None
|
51
|
+
dispatchDate: typing.Optional[str] = None
|
52
|
+
shipDate: typing.Optional[str] = None
|
53
|
+
tenderFacilityId: typing.Optional[str] = None
|
54
|
+
|
55
|
+
|
56
|
+
@attr.s(auto_attribs=True)
|
57
|
+
class OrderRequest:
|
58
|
+
"""Veho Order Request - matches OpenAPI spec"""
|
59
|
+
|
60
|
+
destination: OrderAddress
|
61
|
+
recipient: str
|
62
|
+
fromAddress: FromAddress
|
63
|
+
serviceClass: typing.Optional[str] = "groundPlus"
|
64
|
+
externalId: typing.Optional[str] = None
|
65
|
+
merchantId: typing.Optional[str] = None
|
66
|
+
fromName: typing.Optional[str] = None
|
67
|
+
packages: typing.Optional[typing.List[OrderRequestPackage]] = None
|
68
|
+
company: typing.Optional[str] = None
|
69
|
+
phone: typing.Optional[str] = None
|
70
|
+
instructions: typing.Optional[str] = None
|
71
|
+
slaDeliveryDate: typing.Optional[str] = None
|
72
|
+
consumerExpectedServiceDate: typing.Optional[str] = None
|
73
|
+
packageCount: typing.Optional[int] = None
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import attr
|
2
|
+
import typing
|
3
|
+
|
4
|
+
|
5
|
+
@attr.s(auto_attribs=True)
|
6
|
+
class LabelData:
|
7
|
+
"""Veho Label Data structure"""
|
8
|
+
|
9
|
+
format: typing.Optional[str] = None
|
10
|
+
image: typing.Optional[str] = None
|
11
|
+
|
12
|
+
|
13
|
+
@attr.s(auto_attribs=True)
|
14
|
+
class ShipmentData:
|
15
|
+
"""Veho Shipment Data structure"""
|
16
|
+
|
17
|
+
trackingNumber: typing.Optional[str] = None
|
18
|
+
shipmentId: typing.Optional[str] = None
|
19
|
+
labelData: typing.Optional[LabelData] = None
|
20
|
+
invoiceImage: typing.Optional[str] = None
|
21
|
+
serviceCode: typing.Optional[str] = None
|
22
|
+
|
23
|
+
|
24
|
+
@attr.s(auto_attribs=True)
|
25
|
+
class ShipmentResponse:
|
26
|
+
"""Veho Shipment Response containing shipment object"""
|
27
|
+
|
28
|
+
shipment: typing.Optional[ShipmentData] = None
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import attr
|
2
|
+
import typing
|
3
|
+
|
4
|
+
|
5
|
+
@attr.s(auto_attribs=True)
|
6
|
+
class TrackingEvent:
|
7
|
+
"""Veho Tracking Event structure"""
|
8
|
+
|
9
|
+
date: typing.Optional[str] = None
|
10
|
+
time: typing.Optional[str] = None
|
11
|
+
code: typing.Optional[str] = None
|
12
|
+
description: typing.Optional[str] = None
|
13
|
+
location: typing.Optional[str] = None
|
14
|
+
|
15
|
+
|
16
|
+
@attr.s(auto_attribs=True)
|
17
|
+
class TrackingInfo:
|
18
|
+
"""Veho Tracking Info structure"""
|
19
|
+
|
20
|
+
trackingNumber: typing.Optional[str] = None
|
21
|
+
status: typing.Optional[str] = None
|
22
|
+
statusDetails: typing.Optional[str] = None
|
23
|
+
estimatedDelivery: typing.Optional[str] = None
|
24
|
+
events: typing.Optional[typing.List[TrackingEvent]] = None
|
25
|
+
|
26
|
+
|
27
|
+
@attr.s(auto_attribs=True)
|
28
|
+
class TrackingResponse:
|
29
|
+
"""Veho Tracking Response containing trackingInfo array"""
|
30
|
+
|
31
|
+
trackingInfo: typing.Optional[typing.List[TrackingInfo]] = None
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: karrio_veho
|
3
|
+
Version: 2025.5rc7
|
4
|
+
Summary: Karrio - Veho 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.11
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
Requires-Dist: karrio
|
14
|
+
|
15
|
+
# karrio.veho
|
16
|
+
|
17
|
+
This package is a Veho extension of the [karrio](https://pypi.org/project/karrio) multi carrier shipping SDK.
|
18
|
+
|
19
|
+
## Requirements
|
20
|
+
|
21
|
+
`Python 3.11+`
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
```bash
|
26
|
+
pip install karrio.veho
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```python
|
32
|
+
import karrio.sdk as karrio
|
33
|
+
from karrio.mappers.veho.settings import Settings
|
34
|
+
|
35
|
+
|
36
|
+
# Initialize a carrier gateway
|
37
|
+
veho = karrio.gateway["veho"].create(
|
38
|
+
Settings(
|
39
|
+
...
|
40
|
+
)
|
41
|
+
)
|
42
|
+
```
|
43
|
+
|
44
|
+
Check the [Karrio Mutli-carrier SDK docs](https://docs.karrio.io) for Shipping API requests
|
@@ -0,0 +1,29 @@
|
|
1
|
+
karrio/mappers/veho/__init__.py,sha256=IhSfq4EWRQK7mZ-H-D4c77YNAd-i5OofPo1v7vcdIko,139
|
2
|
+
karrio/mappers/veho/mapper.py,sha256=_hPoy6Peo53s9bBu-TSL4WLpuQqTrqtUrzXhwY9-zOM,2205
|
3
|
+
karrio/mappers/veho/proxy.py,sha256=SQHO4jThk6n7KIgXCSE9P42sK7QhgyKHTdWUzFfj7J4,2457
|
4
|
+
karrio/mappers/veho/settings.py,sha256=WXGTFme3vhotTFrUKHAADPkj0opbjaB99KkiLInsQ1o,499
|
5
|
+
karrio/plugins/veho/__init__.py,sha256=RRknnfI33kj91j93PTuP9rgLer-hF1d-_dlzTZo01Bk,926
|
6
|
+
karrio/providers/veho/__init__.py,sha256=gDjr_Di_oloZNxMnq563-Vte1JeKiCBWmQNbG6GGvBM,431
|
7
|
+
karrio/providers/veho/error.py,sha256=k37spCUZcscb__4RYJIt4JvvSbGwcQTdhvgfHzFLzQw,890
|
8
|
+
karrio/providers/veho/rate.py,sha256=6iQQ8C4ONJstEhI1iEi-FnzY9txVRw6_sbbu5NBwKEw,3726
|
9
|
+
karrio/providers/veho/tracking.py,sha256=bTMfdbFTHScAt-kRLjMk7cXHqAif6wPlBfTz0xkpohw,3565
|
10
|
+
karrio/providers/veho/units.py,sha256=_v0-5LPVL5L3qY4E8ytZxITQVLHe_Cw9qPmQtsvD19Y,2160
|
11
|
+
karrio/providers/veho/utils.py,sha256=_ImpPTYs5zar-Oz1WJzNxePju6ownDtSN2l_h5UtDqc,1132
|
12
|
+
karrio/providers/veho/shipment/__init__.py,sha256=pKpTtzzW6Sd89yTKFgWom1F3mUjU8s6zmxZUJF2gwUE,225
|
13
|
+
karrio/providers/veho/shipment/cancel.py,sha256=E78xzQGinexuaSjYVJAd87R5M451eyDKSKObCNyJ3sE,2667
|
14
|
+
karrio/providers/veho/shipment/create.py,sha256=o4SceEFvzbHUiRGirywp2zhAASpuMYz-I1nmrkVq6Ik,4486
|
15
|
+
karrio/schemas/veho/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
+
karrio/schemas/veho/error_response.py,sha256=-8j01O1NW1moVacGjofpQqJtopBMp-NxOEjr3J-0PhI,342
|
17
|
+
karrio/schemas/veho/rate_request.py,sha256=koxmnnNEZQN5CQsmkJnomOJyAsYYVP0MnQSw3RpQ4V4,528
|
18
|
+
karrio/schemas/veho/rate_response.py,sha256=IM1OmNDCr7hRwYW0LQLOoCDFw7Q0xYTuFD-Qm_I0rzY,582
|
19
|
+
karrio/schemas/veho/shipment_cancel_request.py,sha256=QdmQ2FM2wXYMZUJS5q7jtKWrsIsAL-kQyV_zfPySq3g,181
|
20
|
+
karrio/schemas/veho/shipment_cancel_response.py,sha256=vWy6Vcn0n_J-ZsUOi-aaXkiAgmccGjvvbQSYsS83K7E,214
|
21
|
+
karrio/schemas/veho/shipment_request.py,sha256=EwC2yMq-8w1dg593vYxvoEvqbrlB38ROxkw_lAhCCJs,2179
|
22
|
+
karrio/schemas/veho/shipment_response.py,sha256=io3jpTNda_nAxuFKm_XxYQUSj4ua8zNhEKLgl7DB43w,686
|
23
|
+
karrio/schemas/veho/tracking_request.py,sha256=eX9thHKgCqwaA_hkfAGwglaaP6-HInJjsVG00XfixSw,189
|
24
|
+
karrio/schemas/veho/tracking_response.py,sha256=Ye-NApoMoDEdHm4OZqFq0Xq_B2iWz5dcfFt43lmMbEA,853
|
25
|
+
karrio_veho-2025.5rc7.dist-info/METADATA,sha256=sJeRnBu4e0-GCw1U_kbInh7HrLVQpzNj2D7RuqYB03w,969
|
26
|
+
karrio_veho-2025.5rc7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
27
|
+
karrio_veho-2025.5rc7.dist-info/entry_points.txt,sha256=0Z7ZZZzg7FhTZdF1IMqFMqi2lf7l4y1Y0dOU37E8QAI,44
|
28
|
+
karrio_veho-2025.5rc7.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
|
29
|
+
karrio_veho-2025.5rc7.dist-info/RECORD,,
|