karrio-eshipper 2025.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. karrio/mappers/eshipper/__init__.py +3 -0
  2. karrio/mappers/eshipper/mapper.py +50 -0
  3. karrio/mappers/eshipper/proxy.py +65 -0
  4. karrio/mappers/eshipper/settings.py +21 -0
  5. karrio/plugins/eshipper/__init__.py +18 -0
  6. karrio/providers/eshipper/__init__.py +13 -0
  7. karrio/providers/eshipper/error.py +53 -0
  8. karrio/providers/eshipper/metadata.json +13018 -0
  9. karrio/providers/eshipper/rate.py +197 -0
  10. karrio/providers/eshipper/shipment/__init__.py +9 -0
  11. karrio/providers/eshipper/shipment/cancel.py +45 -0
  12. karrio/providers/eshipper/shipment/create.py +295 -0
  13. karrio/providers/eshipper/tracking.py +72 -0
  14. karrio/providers/eshipper/units.py +334 -0
  15. karrio/providers/eshipper/utils.py +76 -0
  16. karrio/schemas/eshipper/__init__.py +0 -0
  17. karrio/schemas/eshipper/cancel_request.py +15 -0
  18. karrio/schemas/eshipper/cancel_response.py +15 -0
  19. karrio/schemas/eshipper/error_response.py +21 -0
  20. karrio/schemas/eshipper/pickup_cancel_response.py +9 -0
  21. karrio/schemas/eshipper/pickup_request.py +25 -0
  22. karrio/schemas/eshipper/pickup_response.py +16 -0
  23. karrio/schemas/eshipper/rate_request.py +181 -0
  24. karrio/schemas/eshipper/rate_response.py +37 -0
  25. karrio/schemas/eshipper/shipping_request.py +184 -0
  26. karrio/schemas/eshipper/shipping_response.py +114 -0
  27. karrio/schemas/eshipper/tracking_request.py +10 -0
  28. karrio/schemas/eshipper/tracking_response.py +62 -0
  29. karrio_eshipper-2025.5.dist-info/METADATA +45 -0
  30. karrio_eshipper-2025.5.dist-info/RECORD +33 -0
  31. karrio_eshipper-2025.5.dist-info/WHEEL +5 -0
  32. karrio_eshipper-2025.5.dist-info/entry_points.txt +2 -0
  33. karrio_eshipper-2025.5.dist-info/top_level.txt +3 -0
@@ -0,0 +1,334 @@
1
+ import typing
2
+ import pathlib
3
+ import karrio.lib as lib
4
+ import karrio.core.units as units
5
+
6
+ METADATA_JSON = lib.to_dict(
7
+ lib.load_file_content(
8
+ pathlib.Path(__file__).resolve().parent / "metadata.json"
9
+ ).replace('"NULL"', "null")
10
+ )
11
+ KARRIO_CARRIER_MAPPING = {
12
+ "fed_ex": "fedex",
13
+ "e_shipper": "eshipper",
14
+ "tforce_freight": "tforce",
15
+ "federal_express": "fedex",
16
+ "canada_post": "canadapost",
17
+ "e_shipper_ltl": "eshipper",
18
+ "fedex_freight_ltl": "fedex",
19
+ "e_shipper_trucking": "eshipper",
20
+ }
21
+
22
+
23
+ class PackagingType(lib.StrEnum):
24
+ """Carrier specific packaging type"""
25
+
26
+ Drum = "Drum"
27
+ Boxes = "Boxes"
28
+ Rolls = "Rolls"
29
+ Pipes = "Pipes"
30
+ Bales = "Bales"
31
+ Bags = "Bags"
32
+ Pallet = "Pallet"
33
+ Cylinder = "Cylinder"
34
+ Pails = "Pails"
35
+ Reels = "Reels"
36
+ Crate = "Crate"
37
+ Bucket = "Bucket"
38
+ Bundle = "Bundle"
39
+ Can = "Can"
40
+ Carton = "Carton"
41
+ Case = "Case"
42
+ Coil = "Coil"
43
+ Pieces = "Pieces"
44
+ Skid = "Skid"
45
+
46
+ """ Unified Packaging type mapping """
47
+ tube = Cylinder
48
+ pallet = Pallet
49
+ small_box = Boxes
50
+ medium_box = Boxes
51
+
52
+
53
+ class ShippingOption(lib.Enum):
54
+ """Carrier specific options"""
55
+
56
+ eshipper_signature_required = lib.OptionEnum("signatureRequired", bool)
57
+ eshipper_insurance_type = lib.OptionEnum("insuranceType")
58
+ eshipper_dangerous_goods_type = lib.OptionEnum("dangerousGoodsType", bool)
59
+ eshipper_cod = lib.OptionEnum("cod", float)
60
+ eshipper_is_saturday_service = lib.OptionEnum("isSaturdayService", bool)
61
+ eshipper_hold_for_pickup_required = lib.OptionEnum("holdForPickupRequired", bool)
62
+ eshipper_special_equipment = lib.OptionEnum("specialEquipment", bool)
63
+ eshipper_inside_delivery = lib.OptionEnum("insideDelivery", bool)
64
+ eshipper_delivery_appointment = lib.OptionEnum("deliveryAppointment", bool)
65
+ eshipper_inside_pickup = lib.OptionEnum("insidePickup", bool)
66
+ eshipper_saturday_pickup_required = lib.OptionEnum("saturdayPickupRequired", bool)
67
+ eshipper_stackable = lib.OptionEnum("stackable", bool)
68
+
69
+ """ Unified Option type mapping """
70
+ cash_on_delivery = eshipper_cod
71
+ insurance = eshipper_insurance_type
72
+ signature_confirmation = eshipper_signature_required
73
+ saturday_delivery = eshipper_is_saturday_service
74
+ hold_at_location = eshipper_hold_for_pickup_required
75
+ dangerous_good = eshipper_dangerous_goods_type
76
+
77
+
78
+ def shipping_options_initializer(
79
+ options: dict,
80
+ package_options: units.ShippingOptions = None,
81
+ ) -> units.ShippingOptions:
82
+ """
83
+ Apply default values to the given options.
84
+ """
85
+
86
+ if package_options is not None:
87
+ options.update(package_options.content)
88
+
89
+ def items_filter(key: str) -> bool:
90
+ return key in ShippingOption # type: ignore
91
+
92
+ return units.ShippingOptions(options, ShippingOption, items_filter=items_filter)
93
+
94
+
95
+ class TrackingStatus(lib.Enum):
96
+ on_hold = ["on_hold"]
97
+ delivered = ["delivered"]
98
+ in_transit = ["in_transit"]
99
+ delivery_failed = ["delivery_failed"]
100
+ delivery_delayed = ["delivery_delayed"]
101
+ out_for_delivery = ["out_for_delivery"]
102
+ ready_for_pickup = ["ready_for_pickup"]
103
+
104
+
105
+ def to_carrier_code(carrierDTO: typing.Dict[str, str]) -> str:
106
+ _code = lib.to_snake_case((carrierDTO or {}).get("name") or "eshipper")
107
+
108
+ # map carrier names to their corresponding Karrio code
109
+ return KARRIO_CARRIER_MAPPING.get(_code, _code)
110
+
111
+
112
+ def to_service_code(service: typing.Dict[str, str]) -> str:
113
+ parts = list(
114
+ dict.fromkeys(
115
+ [
116
+ to_carrier_code(service.get("carrierDTO")), # type: ignore
117
+ *[
118
+ _.lower()
119
+ for _ in (
120
+ service.get("esServicename") or service.get("name")
121
+ ).split(" ")
122
+ ],
123
+ ][::-1]
124
+ )
125
+ )[::-1]
126
+ output = (
127
+ lib.to_slug(" ".join(["eshipper", *parts]))
128
+ .replace("_eshipper", "")
129
+ .replace("__", "_")
130
+ )
131
+
132
+ return output
133
+
134
+
135
+ def get_service(search: str, test_mode: bool = False, service_id: str = None):
136
+ prod_metadata = METADATA_JSON["PROD_SERVICES"]
137
+ test_metadata = METADATA_JSON["DEV_SERVICES"]
138
+ metadata = lib.identity(
139
+ test_metadata + prod_metadata if test_mode else prod_metadata + test_metadata
140
+ )
141
+
142
+ return next(
143
+ (
144
+ service
145
+ for service in metadata
146
+ if to_service_code(service) == search
147
+ or service.get("name") == search
148
+ or str(service.get("id")) == search
149
+ or (service_id and service_id == str(service.get("id")))
150
+ ),
151
+ {},
152
+ )
153
+
154
+
155
+ def get_service_id(search: str, test_mode: bool = False, service_id: str = None):
156
+ return (
157
+ get_service(search, test_mode=test_mode, service_id=service_id).get("id")
158
+ or service_id
159
+ )
160
+
161
+
162
+ def find_service(search: str, test_mode: bool = False, service_id: str = None):
163
+
164
+ if ShippingService.map(search).name:
165
+ return ShippingService.map(search)
166
+
167
+ service = get_service(search, test_mode=test_mode, service_id=service_id)
168
+
169
+ if service:
170
+ return ShippingService.map(to_service_code(service))
171
+
172
+ return ShippingService.map(search)
173
+
174
+
175
+ def get_carrier(
176
+ search: str,
177
+ test_mode: bool = False,
178
+ service_search: str = None,
179
+ service_id: str = None,
180
+ ):
181
+ id_key = "test_id" if test_mode else "prod_id"
182
+ alternate_key = "prod_id" if not test_mode else "test_id"
183
+ service = get_service(service_search, test_mode=test_mode, service_id=service_id)
184
+
185
+ return service.get("carrierDTO") or next(
186
+ (
187
+ carrier
188
+ for carrier in ESHIPPER_CARRIER_METADATA.values()
189
+ if search == carrier.get(id_key) or search == carrier.get("name")
190
+ ),
191
+ next(
192
+ (
193
+ carrier
194
+ for carrier in ESHIPPER_CARRIER_METADATA.values()
195
+ if search == carrier.get(alternate_key) or search == carrier.get("name")
196
+ ),
197
+ {},
198
+ ),
199
+ )
200
+
201
+
202
+ def get_carrier_id(
203
+ search: str,
204
+ test_mode: bool = False,
205
+ service_search: str = None,
206
+ service_id: str = None,
207
+ ):
208
+ return get_carrier(
209
+ search,
210
+ test_mode=test_mode,
211
+ service_search=service_search,
212
+ service_id=service_id,
213
+ ).get("id")
214
+
215
+
216
+ def find_rate_provider(
217
+ search: str,
218
+ test_mode: bool = False,
219
+ service_search: str = None,
220
+ service_id: str = None,
221
+ ):
222
+
223
+ if RateProvider.map(lib.to_snake_case(search)).name:
224
+ return RateProvider.map(lib.to_snake_case(search))
225
+
226
+ carrier = get_carrier(
227
+ search,
228
+ test_mode=test_mode,
229
+ service_search=service_search,
230
+ service_id=service_id,
231
+ )
232
+
233
+ if carrier and RateProvider.map(to_carrier_code(carrier)).name:
234
+ return RateProvider.map(to_carrier_code(carrier))
235
+
236
+ return RateProvider.map(lib.to_snake_case(search))
237
+
238
+
239
+ ESHIPPER_CARRIER_METADATA = {
240
+ lib.to_snake_case(carrier["carrierDTO"]["name"]): {
241
+ **carrier["carrierDTO"],
242
+ "ids": list(
243
+ set(
244
+ [
245
+ s["carrierDTO"]["id"]
246
+ for s in METADATA_JSON["PROD_SERVICES"]
247
+ + METADATA_JSON["DEV_SERVICES"]
248
+ if s["carrierDTO"]["name"] == carrier["carrierDTO"]["name"]
249
+ ]
250
+ )
251
+ ),
252
+ "prod_id": next(
253
+ (
254
+ s["carrierDTO"]["id"]
255
+ for s in METADATA_JSON["PROD_SERVICES"]
256
+ if s["carrierDTO"]["name"] == carrier["carrierDTO"]["name"]
257
+ ),
258
+ None,
259
+ ),
260
+ "test_id": next(
261
+ (
262
+ s["carrierDTO"]["id"]
263
+ for s in METADATA_JSON["DEV_SERVICES"]
264
+ if s["carrierDTO"]["name"] == carrier["carrierDTO"]["name"]
265
+ ),
266
+ None,
267
+ ),
268
+ }
269
+ for carrier in {
270
+ s["carrierDTO"]["name"]: s
271
+ for s in METADATA_JSON["PROD_SERVICES"] + METADATA_JSON["DEV_SERVICES"]
272
+ }.values()
273
+ }
274
+
275
+ ESHIPPER_SERVICE_METADATA = {
276
+ lib.to_snake_case(service.get("esServicename") or service.get("name")): {
277
+ **service,
278
+ "ids": list(
279
+ set(
280
+ [
281
+ s["id"]
282
+ for s in METADATA_JSON["PROD_SERVICES"]
283
+ + METADATA_JSON["DEV_SERVICES"]
284
+ if lib.to_snake_case(s["name"])
285
+ == lib.to_snake_case(service["name"])
286
+ ]
287
+ )
288
+ ),
289
+ "prod_id": next(
290
+ (
291
+ s["id"]
292
+ for s in METADATA_JSON["PROD_SERVICES"]
293
+ if s["name"] == service["name"]
294
+ ),
295
+ None,
296
+ ),
297
+ "test_id": next(
298
+ (
299
+ s["id"]
300
+ for s in METADATA_JSON["DEV_SERVICES"]
301
+ if s["name"] == service["name"]
302
+ ),
303
+ None,
304
+ ),
305
+ "carrier": lib.to_snake_case(service["carrierDTO"]["name"]),
306
+ }
307
+ for service in {
308
+ s["name"]: s
309
+ for s in METADATA_JSON["PROD_SERVICES"] + METADATA_JSON["DEV_SERVICES"]
310
+ }.values()
311
+ }
312
+
313
+
314
+ ShippingService = lib.StrEnum(
315
+ "ShippingService",
316
+ {
317
+ to_service_code(service): service["name"]
318
+ for service in ESHIPPER_SERVICE_METADATA.values()
319
+ },
320
+ )
321
+
322
+ RateProvider = lib.StrEnum(
323
+ "RateProvider",
324
+ {
325
+ to_carrier_code(carrier): slug
326
+ for slug, carrier in ESHIPPER_CARRIER_METADATA.items()
327
+ },
328
+ )
329
+
330
+
331
+ setattr(ShippingService, "service_id", get_service_id)
332
+ setattr(ShippingService, "carrier_id", get_carrier_id)
333
+ setattr(ShippingService, "find", find_service)
334
+ setattr(RateProvider, "find", find_rate_provider)
@@ -0,0 +1,76 @@
1
+ import datetime
2
+ import karrio.lib as lib
3
+ import karrio.core as core
4
+ import karrio.core.errors as errors
5
+
6
+
7
+ class Settings(core.Settings):
8
+ """eShipper connection settings."""
9
+
10
+ principal: str
11
+ credential: str
12
+
13
+ @property
14
+ def carrier_name(self):
15
+ return "eshipper"
16
+
17
+ @property
18
+ def server_url(self):
19
+ return (
20
+ "https://uu2.eshipper.com" if self.test_mode else "https://ww2.eshipper.com"
21
+ )
22
+
23
+ @property
24
+ def access_token(self):
25
+ """Retrieve the "token" using the principal|credential pair
26
+ or collect it from the cache if an unexpired "token" exist.
27
+ """
28
+ cache_key = f"{self.carrier_name}|{self.principal}|{self.credential}"
29
+ now = datetime.datetime.now() + datetime.timedelta(minutes=30)
30
+
31
+ auth = self.connection_cache.get(cache_key) or {}
32
+ token = auth.get("token")
33
+ expiry = lib.to_date(auth.get("expiry"), current_format="%Y-%m-%d %H:%M:%S")
34
+
35
+ if token is not None and expiry is not None and expiry > now:
36
+ return token
37
+
38
+ self.connection_cache.set(cache_key, lambda: login(self))
39
+ new_auth = self.connection_cache.get(cache_key)
40
+
41
+ return new_auth["token"]
42
+
43
+
44
+ def login(settings: Settings):
45
+ """Sign in response
46
+ {
47
+ "token": "string",
48
+ "expires_in": "string",
49
+ "token_type": "string",
50
+ "refresh_token": "string",
51
+ "refresh_expires_in": "string"
52
+ }
53
+ """
54
+ import karrio.providers.eshipper.error as error
55
+
56
+ result = lib.request(
57
+ url=f"{settings.server_url}/api/v2/authenticate",
58
+ data=lib.to_json(
59
+ {
60
+ "principal": settings.principal,
61
+ "credential": settings.credential,
62
+ }
63
+ ),
64
+ method="POST",
65
+ headers={"content-Type": "application/json"},
66
+ )
67
+ response = lib.to_dict(result)
68
+ messages = error.parse_error_response(response, settings)
69
+
70
+ if any(messages):
71
+ raise errors.ParsedMessagesError(messages=messages)
72
+
73
+ expiry = datetime.datetime.now() + datetime.timedelta(
74
+ seconds=float(response.get("expires_in", 0))
75
+ )
76
+ return {**response, "expiry": lib.fdatetime(expiry)}
File without changes
@@ -0,0 +1,15 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class OrderType:
8
+ trackingId: typing.Optional[str] = None
9
+ orderId: typing.Optional[str] = None
10
+ message: typing.Optional[str] = None
11
+
12
+
13
+ @attr.s(auto_attribs=True)
14
+ class CancelRequestType:
15
+ order: typing.Optional[OrderType] = jstruct.JStruct[OrderType]
@@ -0,0 +1,15 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class OrderType:
8
+ trackingId: typing.Optional[str] = None
9
+ orderId: typing.Optional[str] = None
10
+ message: typing.Optional[str] = None
11
+
12
+
13
+ @attr.s(auto_attribs=True)
14
+ class CancelResponseType:
15
+ order: typing.Optional[typing.List[OrderType]] = jstruct.JList[OrderType]
@@ -0,0 +1,21 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class FieldErrorType:
8
+ objectName: typing.Optional[str] = None
9
+ field: typing.Optional[str] = None
10
+ message: typing.Optional[str] = None
11
+
12
+
13
+ @attr.s(auto_attribs=True)
14
+ class ErrorResponseType:
15
+ status: typing.Optional[int] = None
16
+ title: typing.Optional[str] = None
17
+ type: typing.Optional[str] = None
18
+ message: typing.Optional[str] = None
19
+ code: typing.Optional[str] = None
20
+ fieldErrors: typing.Optional[typing.List[FieldErrorType]] = jstruct.JList[FieldErrorType]
21
+ thirdPartyMessage: typing.Optional[str] = None
@@ -0,0 +1,9 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class PickupCancelResponseType:
8
+ orderId: typing.Optional[int] = None
9
+ status: typing.Optional[str] = None
@@ -0,0 +1,25 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class TimeType:
8
+ hour: typing.Optional[int] = None
9
+ minute: typing.Optional[int] = None
10
+ second: typing.Optional[int] = None
11
+ nano: typing.Optional[int] = None
12
+
13
+
14
+ @attr.s(auto_attribs=True)
15
+ class PickupRequestType:
16
+ contactName: typing.Optional[str] = None
17
+ phoneNumber: typing.Optional[str] = None
18
+ pickupDate: typing.Optional[str] = None
19
+ pickupTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
20
+ closingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
21
+ palletPickupTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
22
+ palletClosingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
23
+ palletDeliveryClosingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
24
+ location: typing.Optional[str] = None
25
+ instructions: typing.Optional[str] = None
@@ -0,0 +1,16 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class OrderType:
8
+ trackingId: typing.Optional[str] = None
9
+ orderId: typing.Optional[str] = None
10
+ message: typing.Optional[str] = None
11
+
12
+
13
+ @attr.s(auto_attribs=True)
14
+ class PickupResponseType:
15
+ order: typing.Optional[OrderType] = jstruct.JStruct[OrderType]
16
+ message: typing.Optional[str] = None
@@ -0,0 +1,181 @@
1
+ import attr
2
+ import jstruct
3
+ import typing
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class CodAddressType:
8
+ company: typing.Optional[str] = None
9
+ name: typing.Optional[str] = None
10
+ addressLine1: typing.Optional[str] = None
11
+ city: typing.Optional[str] = None
12
+ province: typing.Optional[str] = None
13
+ country: typing.Optional[str] = None
14
+ zip: typing.Optional[str] = None
15
+
16
+
17
+ @attr.s(auto_attribs=True)
18
+ class CodType:
19
+ codAddress: typing.Optional[CodAddressType] = jstruct.JStruct[CodAddressType]
20
+ paymentType: typing.Optional[str] = None
21
+
22
+
23
+ @attr.s(auto_attribs=True)
24
+ class BillingAddressType:
25
+ company: typing.Optional[str] = None
26
+ attention: typing.Optional[str] = None
27
+ address1: typing.Optional[str] = None
28
+ address2: typing.Optional[str] = None
29
+ city: typing.Optional[str] = None
30
+ province: typing.Optional[str] = None
31
+ country: typing.Optional[str] = None
32
+ zip: typing.Optional[str] = None
33
+ email: typing.Optional[str] = None
34
+ phone: typing.Optional[str] = None
35
+
36
+
37
+ @attr.s(auto_attribs=True)
38
+ class ContactType:
39
+ contactCompany: typing.Optional[str] = None
40
+ contactName: typing.Optional[str] = None
41
+ phone: typing.Optional[str] = None
42
+ brokerName: typing.Optional[str] = None
43
+ brokerTaxId: typing.Optional[str] = None
44
+ recipientTaxId: typing.Optional[str] = None
45
+
46
+
47
+ @attr.s(auto_attribs=True)
48
+ class DutiesTaxesType:
49
+ dutiable: typing.Optional[bool] = None
50
+ billTo: typing.Optional[str] = None
51
+ accountNumber: typing.Optional[str] = None
52
+ sedNumber: typing.Optional[str] = None
53
+
54
+
55
+ @attr.s(auto_attribs=True)
56
+ class ItemType:
57
+ hsnCode: typing.Optional[str] = None
58
+ description: typing.Optional[str] = None
59
+ originCountry: typing.Optional[str] = None
60
+ quantity: typing.Optional[int] = None
61
+ unitPrice: typing.Optional[int] = None
62
+ skuCode: typing.Optional[str] = None
63
+
64
+
65
+ @attr.s(auto_attribs=True)
66
+ class ItemsType:
67
+ item: typing.Optional[typing.List[ItemType]] = jstruct.JList[ItemType]
68
+ currency: typing.Optional[str] = None
69
+
70
+
71
+ @attr.s(auto_attribs=True)
72
+ class CustomsInformationType:
73
+ contact: typing.Optional[ContactType] = jstruct.JStruct[ContactType]
74
+ items: typing.Optional[ItemsType] = jstruct.JStruct[ItemsType]
75
+ dutiesTaxes: typing.Optional[DutiesTaxesType] = jstruct.JStruct[DutiesTaxesType]
76
+ billingAddress: typing.Optional[BillingAddressType] = jstruct.JStruct[BillingAddressType]
77
+ remarks: typing.Optional[str] = None
78
+
79
+
80
+ @attr.s(auto_attribs=True)
81
+ class PackageType:
82
+ height: typing.Optional[int] = None
83
+ length: typing.Optional[int] = None
84
+ width: typing.Optional[int] = None
85
+ dimensionUnit: typing.Optional[str] = None
86
+ weight: typing.Optional[int] = None
87
+ weightUnit: typing.Optional[str] = None
88
+ type: typing.Optional[str] = None
89
+ freightClass: typing.Optional[str] = None
90
+ nmfcCode: typing.Optional[str] = None
91
+ insuranceAmount: typing.Optional[int] = None
92
+ codAmount: typing.Optional[int] = None
93
+ description: typing.Optional[str] = None
94
+ harmonizedCode: typing.Optional[str] = None
95
+ skuCode: typing.Optional[str] = None
96
+
97
+
98
+ @attr.s(auto_attribs=True)
99
+ class PackagesType:
100
+ type: typing.Optional[str] = None
101
+ packages: typing.Optional[typing.List[PackageType]] = jstruct.JList[PackageType]
102
+
103
+
104
+ @attr.s(auto_attribs=True)
105
+ class TimeType:
106
+ hour: typing.Optional[int] = None
107
+ minute: typing.Optional[int] = None
108
+ second: typing.Optional[int] = None
109
+ nano: typing.Optional[int] = None
110
+
111
+
112
+ @attr.s(auto_attribs=True)
113
+ class PickupType:
114
+ contactName: typing.Optional[str] = None
115
+ phoneNumber: typing.Optional[str] = None
116
+ pickupDate: typing.Optional[str] = None
117
+ pickupTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
118
+ closingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
119
+ palletPickupTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
120
+ palletClosingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
121
+ palletDeliveryClosingTime: typing.Optional[TimeType] = jstruct.JStruct[TimeType]
122
+ location: typing.Optional[str] = None
123
+ instructions: typing.Optional[str] = None
124
+
125
+
126
+ @attr.s(auto_attribs=True)
127
+ class FromType:
128
+ attention: typing.Optional[str] = None
129
+ company: typing.Optional[str] = None
130
+ address1: typing.Optional[str] = None
131
+ address2: typing.Optional[str] = None
132
+ city: typing.Optional[str] = None
133
+ province: typing.Optional[str] = None
134
+ country: typing.Optional[str] = None
135
+ zip: typing.Optional[str] = None
136
+ email: typing.Optional[str] = None
137
+ phone: typing.Optional[str] = None
138
+ instructions: typing.Optional[str] = None
139
+ residential: typing.Optional[bool] = None
140
+ tailgateRequired: typing.Optional[bool] = None
141
+ confirmDelivery: typing.Optional[bool] = None
142
+ notifyRecipient: typing.Optional[bool] = None
143
+
144
+
145
+ @attr.s(auto_attribs=True)
146
+ class ThirdPartyBillingType:
147
+ carrier: typing.Optional[int] = None
148
+ country: typing.Optional[int] = None
149
+ billToAccountNumber: typing.Optional[str] = None
150
+ billToPostalCode: typing.Optional[str] = None
151
+
152
+
153
+ @attr.s(auto_attribs=True)
154
+ class RateRequestType:
155
+ scheduledShipDate: typing.Optional[str] = None
156
+ raterequestfrom: typing.Optional[FromType] = jstruct.JStruct[FromType]
157
+ to: typing.Optional[FromType] = jstruct.JStruct[FromType]
158
+ packagingUnit: typing.Optional[str] = None
159
+ packages: typing.Optional[PackagesType] = jstruct.JStruct[PackagesType]
160
+ reference1: typing.Optional[str] = None
161
+ reference2: typing.Optional[str] = None
162
+ reference3: typing.Optional[str] = None
163
+ transactionId: typing.Optional[str] = None
164
+ signatureRequired: typing.Optional[str] = None
165
+ insuranceType: typing.Optional[str] = None
166
+ dangerousGoodsType: typing.Optional[str] = None
167
+ pickup: typing.Optional[PickupType] = jstruct.JStruct[PickupType]
168
+ customsInformation: typing.Optional[CustomsInformationType] = jstruct.JStruct[CustomsInformationType]
169
+ customsInBondFreight: typing.Optional[bool] = None
170
+ cod: typing.Optional[CodType] = jstruct.JStruct[CodType]
171
+ isSaturdayService: typing.Optional[bool] = None
172
+ holdForPickupRequired: typing.Optional[bool] = None
173
+ specialEquipment: typing.Optional[bool] = None
174
+ insideDelivery: typing.Optional[bool] = None
175
+ deliveryAppointment: typing.Optional[bool] = None
176
+ insidePickup: typing.Optional[bool] = None
177
+ saturdayPickupRequired: typing.Optional[bool] = None
178
+ stackable: typing.Optional[bool] = None
179
+ serviceId: typing.Optional[int] = None
180
+ thirdPartyBilling: typing.Optional[ThirdPartyBillingType] = jstruct.JStruct[ThirdPartyBillingType]
181
+ commodityType: typing.Optional[str] = None