karrio-cli 2025.5rc3__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_cli/__init__.py +0 -0
- karrio_cli/__main__.py +105 -0
- karrio_cli/ai/README.md +335 -0
- karrio_cli/ai/__init__.py +0 -0
- karrio_cli/ai/commands.py +102 -0
- karrio_cli/ai/karrio_ai/__init__.py +1 -0
- karrio_cli/ai/karrio_ai/agent.py +972 -0
- karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
- karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
- karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
- karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
- karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
- karrio_cli/ai/karrio_ai/rag_system.py +503 -0
- karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
- karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
- karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
- karrio_cli/commands/__init__.py +0 -0
- karrio_cli/commands/codegen.py +336 -0
- karrio_cli/commands/login.py +139 -0
- karrio_cli/commands/plugins.py +168 -0
- karrio_cli/commands/sdk.py +870 -0
- karrio_cli/common/queries.py +101 -0
- karrio_cli/common/utils.py +368 -0
- karrio_cli/resources/__init__.py +0 -0
- karrio_cli/resources/carriers.py +91 -0
- karrio_cli/resources/connections.py +207 -0
- karrio_cli/resources/events.py +151 -0
- karrio_cli/resources/logs.py +151 -0
- karrio_cli/resources/orders.py +144 -0
- karrio_cli/resources/shipments.py +210 -0
- karrio_cli/resources/trackers.py +287 -0
- karrio_cli/templates/__init__.py +9 -0
- karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
- karrio_cli/templates/address.py +308 -0
- karrio_cli/templates/docs.py +150 -0
- karrio_cli/templates/documents.py +428 -0
- karrio_cli/templates/manifest.py +396 -0
- karrio_cli/templates/pickup.py +839 -0
- karrio_cli/templates/rates.py +638 -0
- karrio_cli/templates/sdk.py +947 -0
- karrio_cli/templates/shipments.py +892 -0
- karrio_cli/templates/tracking.py +437 -0
- karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
- karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
- karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
- karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
- karrio_cli-2025.5rc3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,638 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
PROVIDER_RATE_TEMPLATE = Template('''"""Karrio {{name}} rate API implementation."""
|
4
|
+
|
5
|
+
# IMPLEMENTATION INSTRUCTIONS:
|
6
|
+
# 1. Uncomment the imports when the schema types are generated
|
7
|
+
# 2. Import the specific request and response types you need
|
8
|
+
# 3. Create a request instance with the appropriate request type
|
9
|
+
# 4. Extract data from the response to populate the RateDetails
|
10
|
+
#
|
11
|
+
# NOTE: JSON schema types are generated with "Type" suffix (e.g., RateRequestType),
|
12
|
+
# while XML schema types don't have this suffix (e.g., RateRequest).
|
13
|
+
|
14
|
+
import karrio.schemas.{{id}}.rate_request as {{id}}_req
|
15
|
+
import karrio.schemas.{{id}}.rate_response as {{id}}_res
|
16
|
+
|
17
|
+
import typing
|
18
|
+
import karrio.lib as lib
|
19
|
+
import karrio.core.units as units
|
20
|
+
import karrio.core.models as models
|
21
|
+
import karrio.providers.{{id}}.error as error
|
22
|
+
import karrio.providers.{{id}}.utils as provider_utils
|
23
|
+
import karrio.providers.{{id}}.units as provider_units
|
24
|
+
|
25
|
+
|
26
|
+
def parse_rate_response(
|
27
|
+
_response: lib.Deserializable[{% if is_xml_api %}lib.Element{% else %}dict{% endif %}],
|
28
|
+
settings: provider_utils.Settings,
|
29
|
+
) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
|
30
|
+
response = _response.deserialize()
|
31
|
+
|
32
|
+
messages = error.parse_error_response(response, settings)
|
33
|
+
|
34
|
+
# Extract rate objects from the response - adjust based on carrier API structure
|
35
|
+
{% if is_xml_api %}
|
36
|
+
# For XML APIs, find the path to rate elements
|
37
|
+
rate_elements = response.xpath(".//rate") if hasattr(response, 'xpath') else []
|
38
|
+
rates = [_extract_details(rate, settings) for rate in rate_elements]
|
39
|
+
{% else %}
|
40
|
+
# For JSON APIs, find the path to rate objects
|
41
|
+
rate_objects = response.get("rates", []) if hasattr(response, 'get') else []
|
42
|
+
rates = [_extract_details(rate, settings) for rate in rate_objects]
|
43
|
+
{% endif %}
|
44
|
+
|
45
|
+
return rates, messages
|
46
|
+
|
47
|
+
|
48
|
+
def _extract_details(
|
49
|
+
data: {% if is_xml_api %}lib.Element{% else %}dict{% endif %},
|
50
|
+
settings: provider_utils.Settings,
|
51
|
+
) -> models.RateDetails:
|
52
|
+
"""
|
53
|
+
Extract rate details from carrier response data
|
54
|
+
|
55
|
+
data: The carrier-specific rate data structure
|
56
|
+
settings: The carrier connection settings
|
57
|
+
|
58
|
+
Returns a RateDetails object with extracted rate information
|
59
|
+
"""
|
60
|
+
# Convert the carrier data to a proper object for easy attribute access
|
61
|
+
{% if is_xml_api %}
|
62
|
+
# For XML APIs, convert Element to proper response object
|
63
|
+
rate = lib.to_object({{id}}_res.Rate, data)
|
64
|
+
|
65
|
+
# Now access data through the object attributes
|
66
|
+
service = rate.service_code
|
67
|
+
service_name = rate.service_name
|
68
|
+
total = float(rate.total_charge) if hasattr(rate, 'total_charge') and rate.total_charge else 0.0
|
69
|
+
currency = rate.currency or "USD"
|
70
|
+
transit_days = int(rate.transit_days) if hasattr(rate, 'transit_days') and rate.transit_days else 0
|
71
|
+
{% else %}
|
72
|
+
# For JSON APIs, convert dict to proper response object
|
73
|
+
rate = lib.to_object({{id}}_res.RateResponseType, data)
|
74
|
+
|
75
|
+
# Now access data through the object attributes
|
76
|
+
service = rate.serviceCode if hasattr(rate, 'serviceCode') else ""
|
77
|
+
service_name = rate.serviceName if hasattr(rate, 'serviceName') else ""
|
78
|
+
total = float(rate.totalCharge) if hasattr(rate, 'totalCharge') and rate.totalCharge else 0.0
|
79
|
+
currency = rate.currency if hasattr(rate, 'currency') else "USD"
|
80
|
+
transit_days = int(rate.transitDays) if hasattr(rate, 'transitDays') and rate.transitDays else 0
|
81
|
+
{% endif %}
|
82
|
+
|
83
|
+
return models.RateDetails(
|
84
|
+
carrier_id=settings.carrier_id,
|
85
|
+
carrier_name=settings.carrier_name,
|
86
|
+
service=service,
|
87
|
+
total_charge=lib.to_money(total, currency),
|
88
|
+
currency=currency,
|
89
|
+
transit_days=transit_days,
|
90
|
+
meta=dict(
|
91
|
+
service_name=service_name,
|
92
|
+
# Add any other useful metadata from the carrier response
|
93
|
+
),
|
94
|
+
)
|
95
|
+
|
96
|
+
|
97
|
+
def rate_request(
|
98
|
+
payload: models.RateRequest,
|
99
|
+
settings: provider_utils.Settings,
|
100
|
+
) -> lib.Serializable:
|
101
|
+
"""
|
102
|
+
Create a rate request for the carrier API
|
103
|
+
|
104
|
+
payload: The standardized RateRequest from karrio
|
105
|
+
settings: The carrier connection settings
|
106
|
+
|
107
|
+
Returns a Serializable object that can be sent to the carrier API
|
108
|
+
"""
|
109
|
+
# Convert karrio models to carrier-specific format
|
110
|
+
shipper = lib.to_address(payload.shipper)
|
111
|
+
recipient = lib.to_address(payload.recipient)
|
112
|
+
packages = lib.to_packages(payload.parcels)
|
113
|
+
services = lib.to_services(payload.services, provider_units.ShippingService)
|
114
|
+
options = lib.to_shipping_options(
|
115
|
+
payload.options,
|
116
|
+
package_options=packages.options,
|
117
|
+
initializer=provider_units.shipping_options_initializer,
|
118
|
+
)
|
119
|
+
|
120
|
+
# Create the carrier-specific request object
|
121
|
+
{% if is_xml_api %}
|
122
|
+
# For XML API request
|
123
|
+
request = {{id}}_req.RateRequest(
|
124
|
+
# Map shipper details
|
125
|
+
shipper={{id}}_req.Address(
|
126
|
+
address_line1=shipper.address_line1,
|
127
|
+
city=shipper.city,
|
128
|
+
postal_code=shipper.postal_code,
|
129
|
+
country_code=shipper.country_code,
|
130
|
+
state_code=shipper.state_code,
|
131
|
+
person_name=shipper.person_name,
|
132
|
+
company_name=shipper.company_name,
|
133
|
+
phone_number=shipper.phone_number,
|
134
|
+
email=shipper.email,
|
135
|
+
),
|
136
|
+
# Map recipient details
|
137
|
+
recipient={{id}}_req.Address(
|
138
|
+
address_line1=recipient.address_line1,
|
139
|
+
city=recipient.city,
|
140
|
+
postal_code=recipient.postal_code,
|
141
|
+
country_code=recipient.country_code,
|
142
|
+
state_code=recipient.state_code,
|
143
|
+
person_name=recipient.person_name,
|
144
|
+
company_name=recipient.company_name,
|
145
|
+
phone_number=recipient.phone_number,
|
146
|
+
email=recipient.email,
|
147
|
+
),
|
148
|
+
# Map package details
|
149
|
+
packages=[
|
150
|
+
{{id}}_req.Package(
|
151
|
+
weight=package.weight.value,
|
152
|
+
weight_unit=provider_units.WeightUnit[package.weight.unit].value,
|
153
|
+
length=package.length.value if package.length else None,
|
154
|
+
width=package.width.value if package.width else None,
|
155
|
+
height=package.height.value if package.height else None,
|
156
|
+
dimension_unit=provider_units.DimensionUnit[package.dimension_unit].value if package.dimension_unit else None,
|
157
|
+
packaging_type=provider_units.PackagingType[package.packaging_type or 'your_packaging'].value,
|
158
|
+
)
|
159
|
+
for package in packages
|
160
|
+
],
|
161
|
+
# Map service codes if specified
|
162
|
+
services=[s.value_or_key for s in services] if services else None,
|
163
|
+
# Add customer number and other account details
|
164
|
+
customer_number=settings.customer_number,
|
165
|
+
# Add any other required fields for this carrier's API
|
166
|
+
)
|
167
|
+
{% else %}
|
168
|
+
# For JSON API request
|
169
|
+
request = {{id}}_req.RateRequestType(
|
170
|
+
# Map shipper details
|
171
|
+
shipper={
|
172
|
+
"addressLine1": shipper.address_line1,
|
173
|
+
"city": shipper.city,
|
174
|
+
"postalCode": shipper.postal_code,
|
175
|
+
"countryCode": shipper.country_code,
|
176
|
+
"stateCode": shipper.state_code,
|
177
|
+
"personName": shipper.person_name,
|
178
|
+
"companyName": shipper.company_name,
|
179
|
+
"phoneNumber": shipper.phone_number,
|
180
|
+
"email": shipper.email,
|
181
|
+
},
|
182
|
+
# Map recipient details
|
183
|
+
recipient={
|
184
|
+
"addressLine1": recipient.address_line1,
|
185
|
+
"city": recipient.city,
|
186
|
+
"postalCode": recipient.postal_code,
|
187
|
+
"countryCode": recipient.country_code,
|
188
|
+
"stateCode": recipient.state_code,
|
189
|
+
"personName": recipient.person_name,
|
190
|
+
"companyName": recipient.company_name,
|
191
|
+
"phoneNumber": recipient.phone_number,
|
192
|
+
"email": recipient.email,
|
193
|
+
},
|
194
|
+
# Map package details
|
195
|
+
packages=[
|
196
|
+
{
|
197
|
+
"weight": package.weight.value,
|
198
|
+
"weightUnit": provider_units.WeightUnit[package.weight.unit].value,
|
199
|
+
"length": package.length.value if package.length else None,
|
200
|
+
"width": package.width.value if package.width else None,
|
201
|
+
"height": package.height.value if package.height else None,
|
202
|
+
"dimensionUnit": provider_units.DimensionUnit[package.dimension_unit].value if package.dimension_unit else None,
|
203
|
+
"packagingType": provider_units.PackagingType[package.packaging_type or 'your_packaging'].value,
|
204
|
+
}
|
205
|
+
for package in packages
|
206
|
+
],
|
207
|
+
# Add service code
|
208
|
+
serviceCode=service,
|
209
|
+
# Add account information
|
210
|
+
customerNumber=settings.customer_number,
|
211
|
+
# Add label details
|
212
|
+
labelFormat=payload.label_type or "PDF",
|
213
|
+
# Add any other required fields for the carrier API
|
214
|
+
)
|
215
|
+
{% endif %}
|
216
|
+
|
217
|
+
return lib.Serializable(request, {% if is_xml_api %}lib.to_xml{% else %}lib.to_dict{% endif %})
|
218
|
+
|
219
|
+
'''
|
220
|
+
)
|
221
|
+
|
222
|
+
|
223
|
+
TEST_RATE_TEMPLATE = Template('''"""{{name}} carrier rate tests."""
|
224
|
+
|
225
|
+
import unittest
|
226
|
+
from unittest.mock import patch, ANY
|
227
|
+
from .fixture import gateway
|
228
|
+
import logging
|
229
|
+
import karrio.sdk as karrio
|
230
|
+
import karrio.lib as lib
|
231
|
+
import karrio.core.models as models
|
232
|
+
|
233
|
+
logger = logging.getLogger(__name__)
|
234
|
+
|
235
|
+
|
236
|
+
class Test{{compact_name}}Rating(unittest.TestCase):
|
237
|
+
def setUp(self):
|
238
|
+
self.maxDiff = None
|
239
|
+
self.RateRequest = models.RateRequest(**RatePayload)
|
240
|
+
|
241
|
+
def test_create_rate_request(self):
|
242
|
+
request = gateway.mapper.create_rate_request(self.RateRequest)
|
243
|
+
self.assertEqual(lib.to_dict(request.serialize()), RateRequest)
|
244
|
+
|
245
|
+
def test_get_rates(self):
|
246
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
247
|
+
mock.return_value = {% if is_xml_api %}"<r></r>"{% else %}"{}"{% endif %}
|
248
|
+
karrio.Rating.fetch(self.RateRequest).from_(gateway)
|
249
|
+
self.assertEqual(
|
250
|
+
mock.call_args[1]["url"],
|
251
|
+
f"{gateway.settings.server_url}/rates"
|
252
|
+
)
|
253
|
+
|
254
|
+
def test_parse_rate_response(self):
|
255
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
256
|
+
mock.return_value = RateResponse
|
257
|
+
parsed_response = (
|
258
|
+
karrio.Rating.fetch(self.RateRequest)
|
259
|
+
.from_(gateway)
|
260
|
+
.parse()
|
261
|
+
)
|
262
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedRateResponse)
|
263
|
+
|
264
|
+
def test_parse_error_response(self):
|
265
|
+
with patch("karrio.mappers.{{id}}.proxy.lib.request") as mock:
|
266
|
+
mock.return_value = ErrorResponse
|
267
|
+
parsed_response = (
|
268
|
+
karrio.Rating.fetch(self.RateRequest)
|
269
|
+
.from_(gateway)
|
270
|
+
.parse()
|
271
|
+
)
|
272
|
+
self.assertListEqual(lib.to_dict(parsed_response), ParsedErrorResponse)
|
273
|
+
|
274
|
+
|
275
|
+
if __name__ == "__main__":
|
276
|
+
unittest.main()
|
277
|
+
|
278
|
+
|
279
|
+
RatePayload = {
|
280
|
+
"shipper": {
|
281
|
+
"address_line1": "123 Test Street",
|
282
|
+
"city": "Test City",
|
283
|
+
"postal_code": "12345",
|
284
|
+
"country_code": "US",
|
285
|
+
"state_code": "CA",
|
286
|
+
"person_name": "Test Person",
|
287
|
+
"company_name": "Test Company",
|
288
|
+
"phone_number": "1234567890",
|
289
|
+
"email": "test@example.com"
|
290
|
+
},
|
291
|
+
"recipient": {
|
292
|
+
"address_line1": "123 Test Street",
|
293
|
+
"city": "Test City",
|
294
|
+
"postal_code": "12345",
|
295
|
+
"country_code": "US",
|
296
|
+
"state_code": "CA",
|
297
|
+
"person_name": "Test Person",
|
298
|
+
"company_name": "Test Company",
|
299
|
+
"phone_number": "1234567890",
|
300
|
+
"email": "test@example.com"
|
301
|
+
},
|
302
|
+
"parcels": [{
|
303
|
+
"weight": 10.0,
|
304
|
+
"width": 10.0,
|
305
|
+
"height": 10.0,
|
306
|
+
"length": 10.0,
|
307
|
+
"weight_unit": "KG",
|
308
|
+
"dimension_unit": "CM",
|
309
|
+
"packaging_type": "BOX"
|
310
|
+
}]
|
311
|
+
}
|
312
|
+
|
313
|
+
RateRequest = {% if is_xml_api %}{
|
314
|
+
"shipper": {
|
315
|
+
"address_line1": "123 Test Street",
|
316
|
+
"city": "Test City",
|
317
|
+
"postal_code": "12345",
|
318
|
+
"country_code": "US",
|
319
|
+
"state_code": "CA",
|
320
|
+
"person_name": "Test Person",
|
321
|
+
"company_name": "Test Company",
|
322
|
+
"phone_number": "1234567890",
|
323
|
+
"email": "test@example.com"
|
324
|
+
},
|
325
|
+
"recipient": {
|
326
|
+
"address_line1": "123 Test Street",
|
327
|
+
"city": "Test City",
|
328
|
+
"postal_code": "12345",
|
329
|
+
"country_code": "US",
|
330
|
+
"state_code": "CA",
|
331
|
+
"person_name": "Test Person",
|
332
|
+
"company_name": "Test Company",
|
333
|
+
"phone_number": "1234567890",
|
334
|
+
"email": "test@example.com"
|
335
|
+
},
|
336
|
+
"packages": [
|
337
|
+
{
|
338
|
+
"weight": 10.0,
|
339
|
+
"weight_unit": "KG",
|
340
|
+
"length": 10.0,
|
341
|
+
"width": 10.0,
|
342
|
+
"height": 10.0,
|
343
|
+
"dimension_unit": "CM",
|
344
|
+
"packaging_type": "BOX"
|
345
|
+
}
|
346
|
+
]
|
347
|
+
}{% else %}{
|
348
|
+
"shipper": {
|
349
|
+
"addressLine1": "123 Test Street",
|
350
|
+
"city": "Test City",
|
351
|
+
"postalCode": "12345",
|
352
|
+
"countryCode": "US",
|
353
|
+
"stateCode": "CA",
|
354
|
+
"personName": "Test Person",
|
355
|
+
"companyName": "Test Company",
|
356
|
+
"phoneNumber": "1234567890",
|
357
|
+
"email": "test@example.com"
|
358
|
+
},
|
359
|
+
"recipient": {
|
360
|
+
"addressLine1": "123 Test Street",
|
361
|
+
"city": "Test City",
|
362
|
+
"postalCode": "12345",
|
363
|
+
"countryCode": "US",
|
364
|
+
"stateCode": "CA",
|
365
|
+
"personName": "Test Person",
|
366
|
+
"companyName": "Test Company",
|
367
|
+
"phoneNumber": "1234567890",
|
368
|
+
"email": "test@example.com"
|
369
|
+
},
|
370
|
+
"packages": [
|
371
|
+
{
|
372
|
+
"weight": 10.0,
|
373
|
+
"weightUnit": "KG",
|
374
|
+
"length": 10.0,
|
375
|
+
"width": 10.0,
|
376
|
+
"height": 10.0,
|
377
|
+
"dimensionUnit": "CM",
|
378
|
+
"packagingType": "BOX"
|
379
|
+
}
|
380
|
+
]
|
381
|
+
}{% endif %}
|
382
|
+
|
383
|
+
RateResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
384
|
+
<rate-response>
|
385
|
+
<rate>
|
386
|
+
<service-code>express</service-code>
|
387
|
+
<service-name>Express Service</service-name>
|
388
|
+
<total-charge>25.99</total-charge>
|
389
|
+
<currency>USD</currency>
|
390
|
+
<transit-days>2</transit-days>
|
391
|
+
</rate>
|
392
|
+
<rate>
|
393
|
+
<service-code>ground</service-code>
|
394
|
+
<service-name>Ground Service</service-name>
|
395
|
+
<total-charge>12.99</total-charge>
|
396
|
+
<currency>USD</currency>
|
397
|
+
<transit-days>5</transit-days>
|
398
|
+
</rate>
|
399
|
+
</rate-response>"""{% else %}"""{
|
400
|
+
"rates": [
|
401
|
+
{
|
402
|
+
"serviceCode": "express",
|
403
|
+
"serviceName": "Express Service",
|
404
|
+
"totalCharge": 25.99,
|
405
|
+
"currency": "USD",
|
406
|
+
"transitDays": 2
|
407
|
+
},
|
408
|
+
{
|
409
|
+
"serviceCode": "ground",
|
410
|
+
"serviceName": "Ground Service",
|
411
|
+
"totalCharge": 12.99,
|
412
|
+
"currency": "USD",
|
413
|
+
"transitDays": 5
|
414
|
+
}
|
415
|
+
]
|
416
|
+
}"""{% endif %}
|
417
|
+
|
418
|
+
ErrorResponse = {% if is_xml_api %}"""<?xml version="1.0"?>
|
419
|
+
<error-response>
|
420
|
+
<e>
|
421
|
+
<code>rate_error</code>
|
422
|
+
<message>Unable to get rates</message>
|
423
|
+
<details>Invalid address provided</details>
|
424
|
+
<e>
|
425
|
+
</error-response>"""{% else %}"""{
|
426
|
+
"error": {
|
427
|
+
"code": "rate_error",
|
428
|
+
"message": "Unable to get rates",
|
429
|
+
"details": "Invalid address provided"
|
430
|
+
}
|
431
|
+
}"""{% endif %}
|
432
|
+
|
433
|
+
ParsedRateResponse = [
|
434
|
+
[
|
435
|
+
{
|
436
|
+
"carrier_id": "{{id}}",
|
437
|
+
"carrier_name": "{{id}}",
|
438
|
+
"service": "express",
|
439
|
+
"currency": "USD",
|
440
|
+
"total_charge": 25.99,
|
441
|
+
"transit_days": 2,
|
442
|
+
"meta": {
|
443
|
+
"service_name": "Express Service"
|
444
|
+
}
|
445
|
+
},
|
446
|
+
{
|
447
|
+
"carrier_id": "{{id}}",
|
448
|
+
"carrier_name": "{{id}}",
|
449
|
+
"service": "ground",
|
450
|
+
"currency": "USD",
|
451
|
+
"total_charge": 12.99,
|
452
|
+
"transit_days": 5,
|
453
|
+
"meta": {
|
454
|
+
"service_name": "Ground Service"
|
455
|
+
}
|
456
|
+
}
|
457
|
+
],
|
458
|
+
[]
|
459
|
+
]
|
460
|
+
|
461
|
+
ParsedErrorResponse = [
|
462
|
+
[],
|
463
|
+
[
|
464
|
+
{
|
465
|
+
"carrier_id": "{{id}}",
|
466
|
+
"carrier_name": "{{id}}",
|
467
|
+
"code": "rate_error",
|
468
|
+
"message": "Unable to get rates",
|
469
|
+
"details": {
|
470
|
+
"details": "Invalid address provided"
|
471
|
+
}
|
472
|
+
}
|
473
|
+
]
|
474
|
+
]
|
475
|
+
'''
|
476
|
+
)
|
477
|
+
|
478
|
+
XML_SCHEMA_RATE_REQUEST_TEMPLATE = Template("""<?xml version="1.0"?>
|
479
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/rate" xmlns="http://{{id}}.com/ws/rate" elementFormDefault="qualified">
|
480
|
+
<xsd:element name="rate-request">
|
481
|
+
<xsd:complexType>
|
482
|
+
<xsd:all>
|
483
|
+
<xsd:element name="shipper">
|
484
|
+
<xsd:complexType>
|
485
|
+
<xsd:all>
|
486
|
+
<xsd:element name="address-line1" type="xsd:string" />
|
487
|
+
<xsd:element name="city" type="xsd:string" />
|
488
|
+
<xsd:element name="postal-code" type="xsd:string" />
|
489
|
+
<xsd:element name="country-code" type="xsd:string" />
|
490
|
+
<xsd:element name="state-code" type="xsd:string" minOccurs="0" />
|
491
|
+
<xsd:element name="person-name" type="xsd:string" minOccurs="0" />
|
492
|
+
<xsd:element name="company-name" type="xsd:string" minOccurs="0" />
|
493
|
+
<xsd:element name="phone-number" type="xsd:string" minOccurs="0" />
|
494
|
+
<xsd:element name="email" type="xsd:string" minOccurs="0" />
|
495
|
+
</xsd:all>
|
496
|
+
</xsd:complexType>
|
497
|
+
</xsd:element>
|
498
|
+
<xsd:element name="recipient">
|
499
|
+
<xsd:complexType>
|
500
|
+
<xsd:all>
|
501
|
+
<xsd:element name="address-line1" type="xsd:string" />
|
502
|
+
<xsd:element name="city" type="xsd:string" />
|
503
|
+
<xsd:element name="postal-code" type="xsd:string" />
|
504
|
+
<xsd:element name="country-code" type="xsd:string" />
|
505
|
+
<xsd:element name="state-code" type="xsd:string" minOccurs="0" />
|
506
|
+
<xsd:element name="person-name" type="xsd:string" minOccurs="0" />
|
507
|
+
<xsd:element name="company-name" type="xsd:string" minOccurs="0" />
|
508
|
+
<xsd:element name="phone-number" type="xsd:string" minOccurs="0" />
|
509
|
+
<xsd:element name="email" type="xsd:string" minOccurs="0" />
|
510
|
+
</xsd:all>
|
511
|
+
</xsd:complexType>
|
512
|
+
</xsd:element>
|
513
|
+
<xsd:element name="packages">
|
514
|
+
<xsd:complexType>
|
515
|
+
<xsd:sequence>
|
516
|
+
<xsd:element name="package" maxOccurs="unbounded">
|
517
|
+
<xsd:complexType>
|
518
|
+
<xsd:all>
|
519
|
+
<xsd:element name="weight" type="xsd:decimal" />
|
520
|
+
<xsd:element name="weight-unit" type="xsd:string" />
|
521
|
+
<xsd:element name="length" type="xsd:decimal" minOccurs="0" />
|
522
|
+
<xsd:element name="width" type="xsd:decimal" minOccurs="0" />
|
523
|
+
<xsd:element name="height" type="xsd:decimal" minOccurs="0" />
|
524
|
+
<xsd:element name="dimension-unit" type="xsd:string" minOccurs="0" />
|
525
|
+
<xsd:element name="packaging-type" type="xsd:string" minOccurs="0" />
|
526
|
+
</xsd:all>
|
527
|
+
</xsd:complexType>
|
528
|
+
</xsd:element>
|
529
|
+
</xsd:sequence>
|
530
|
+
</xsd:complexType>
|
531
|
+
</xsd:element>
|
532
|
+
<xsd:element name="services" minOccurs="0">
|
533
|
+
<xsd:complexType>
|
534
|
+
<xsd:sequence>
|
535
|
+
<xsd:element name="service" type="xsd:string" maxOccurs="unbounded" />
|
536
|
+
</xsd:sequence>
|
537
|
+
</xsd:complexType>
|
538
|
+
</xsd:element>
|
539
|
+
<xsd:element name="options" type="xsd:string" minOccurs="0" />
|
540
|
+
</xsd:all>
|
541
|
+
</xsd:complexType>
|
542
|
+
</xsd:element>
|
543
|
+
</xsd:schema>
|
544
|
+
"""
|
545
|
+
)
|
546
|
+
|
547
|
+
XML_SCHEMA_RATE_RESPONSE_TEMPLATE = Template("""<?xml version="1.0"?>
|
548
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://{{id}}.com/ws/rate" xmlns="http://{{id}}.com/ws/rate" elementFormDefault="qualified">
|
549
|
+
<xsd:element name="rate-response">
|
550
|
+
<xsd:complexType>
|
551
|
+
<xsd:sequence>
|
552
|
+
<xsd:element name="rate" maxOccurs="unbounded">
|
553
|
+
<xsd:complexType>
|
554
|
+
<xsd:all>
|
555
|
+
<xsd:element name="service-code" type="xsd:string" />
|
556
|
+
<xsd:element name="service-name" type="xsd:string" minOccurs="0" />
|
557
|
+
<xsd:element name="total-charge" type="xsd:decimal" />
|
558
|
+
<xsd:element name="currency" type="xsd:string" minOccurs="0" />
|
559
|
+
<xsd:element name="transit-days" type="xsd:integer" minOccurs="0" />
|
560
|
+
</xsd:all>
|
561
|
+
</xsd:complexType>
|
562
|
+
</xsd:element>
|
563
|
+
</xsd:sequence>
|
564
|
+
</xsd:complexType>
|
565
|
+
</xsd:element>
|
566
|
+
</xsd:schema>
|
567
|
+
"""
|
568
|
+
)
|
569
|
+
|
570
|
+
JSON_SCHEMA_RATE_REQUEST_TEMPLATE = Template(
|
571
|
+
"""{
|
572
|
+
"rateRequest": {
|
573
|
+
"shipper": {
|
574
|
+
"addressLine1": "123 Main St",
|
575
|
+
"city": "Anytown",
|
576
|
+
"postalCode": "12345",
|
577
|
+
"countryCode": "US",
|
578
|
+
"stateCode": "CA",
|
579
|
+
"personName": "John Doe",
|
580
|
+
"companyName": "ACME Corp",
|
581
|
+
"phoneNumber": "555-123-4567",
|
582
|
+
"email": "john@example.com"
|
583
|
+
},
|
584
|
+
"recipient": {
|
585
|
+
"addressLine1": "456 Oak St",
|
586
|
+
"city": "Somewhere",
|
587
|
+
"postalCode": "67890",
|
588
|
+
"countryCode": "US",
|
589
|
+
"stateCode": "NY",
|
590
|
+
"personName": "Jane Smith",
|
591
|
+
"companyName": "XYZ Inc",
|
592
|
+
"phoneNumber": "555-987-6543",
|
593
|
+
"email": "jane@example.com"
|
594
|
+
},
|
595
|
+
"packages": [
|
596
|
+
{
|
597
|
+
"weight": 10.5,
|
598
|
+
"weightUnit": "KG",
|
599
|
+
"length": 20.0,
|
600
|
+
"width": 15.0,
|
601
|
+
"height": 10.0,
|
602
|
+
"dimensionUnit": "CM",
|
603
|
+
"packagingType": "BOX"
|
604
|
+
}
|
605
|
+
],
|
606
|
+
"services": ["EXPRESS", "GROUND"],
|
607
|
+
"options": {
|
608
|
+
"insurance": true,
|
609
|
+
"signature_required": false
|
610
|
+
}
|
611
|
+
}
|
612
|
+
}
|
613
|
+
"""
|
614
|
+
)
|
615
|
+
|
616
|
+
JSON_SCHEMA_RATE_RESPONSE_TEMPLATE = Template(
|
617
|
+
"""{
|
618
|
+
"rateResponse": {
|
619
|
+
"rates": [
|
620
|
+
{
|
621
|
+
"serviceCode": "EXPRESS",
|
622
|
+
"serviceName": "Express Shipping",
|
623
|
+
"totalCharge": 25.99,
|
624
|
+
"currency": "USD",
|
625
|
+
"transitDays": 2
|
626
|
+
},
|
627
|
+
{
|
628
|
+
"serviceCode": "GROUND",
|
629
|
+
"serviceName": "Ground Shipping",
|
630
|
+
"totalCharge": 12.99,
|
631
|
+
"currency": "USD",
|
632
|
+
"transitDays": 5
|
633
|
+
}
|
634
|
+
]
|
635
|
+
}
|
636
|
+
}
|
637
|
+
"""
|
638
|
+
)
|