karrio-fedex 2025.5rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. karrio/mappers/fedex/__init__.py +3 -0
  2. karrio/mappers/fedex/mapper.py +89 -0
  3. karrio/mappers/fedex/proxy.py +144 -0
  4. karrio/mappers/fedex/settings.py +24 -0
  5. karrio/plugins/fedex/__init__.py +23 -0
  6. karrio/providers/fedex/__init__.py +24 -0
  7. karrio/providers/fedex/document.py +88 -0
  8. karrio/providers/fedex/error.py +66 -0
  9. karrio/providers/fedex/pickup/__init__.py +9 -0
  10. karrio/providers/fedex/pickup/cancel.py +79 -0
  11. karrio/providers/fedex/pickup/create.py +148 -0
  12. karrio/providers/fedex/pickup/update.py +162 -0
  13. karrio/providers/fedex/rate.py +357 -0
  14. karrio/providers/fedex/shipment/__init__.py +9 -0
  15. karrio/providers/fedex/shipment/cancel.py +46 -0
  16. karrio/providers/fedex/shipment/create.py +748 -0
  17. karrio/providers/fedex/tracking.py +154 -0
  18. karrio/providers/fedex/units.py +501 -0
  19. karrio/providers/fedex/utils.py +199 -0
  20. karrio/schemas/fedex/__init__.py +0 -0
  21. karrio/schemas/fedex/cancel_pickup_request.py +31 -0
  22. karrio/schemas/fedex/cancel_pickup_response.py +24 -0
  23. karrio/schemas/fedex/cancel_request.py +17 -0
  24. karrio/schemas/fedex/cancel_response.py +25 -0
  25. karrio/schemas/fedex/error_response.py +16 -0
  26. karrio/schemas/fedex/paperless_request.py +30 -0
  27. karrio/schemas/fedex/paperless_response.py +21 -0
  28. karrio/schemas/fedex/pickup_request.py +106 -0
  29. karrio/schemas/fedex/pickup_response.py +25 -0
  30. karrio/schemas/fedex/rating_request.py +478 -0
  31. karrio/schemas/fedex/rating_responses.py +208 -0
  32. karrio/schemas/fedex/shipping_request.py +731 -0
  33. karrio/schemas/fedex/shipping_responses.py +584 -0
  34. karrio/schemas/fedex/tracking_document_request.py +30 -0
  35. karrio/schemas/fedex/tracking_document_response.py +30 -0
  36. karrio/schemas/fedex/tracking_request.py +23 -0
  37. karrio/schemas/fedex/tracking_response.py +350 -0
  38. karrio_fedex-2025.5rc1.dist-info/METADATA +45 -0
  39. karrio_fedex-2025.5rc1.dist-info/RECORD +42 -0
  40. karrio_fedex-2025.5rc1.dist-info/WHEEL +5 -0
  41. karrio_fedex-2025.5rc1.dist-info/entry_points.txt +2 -0
  42. karrio_fedex-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,154 @@
1
+ import karrio.schemas.fedex.tracking_request as fedex
2
+ import karrio.schemas.fedex.tracking_response as tracking
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.models as models
6
+ import karrio.providers.fedex.error as provider_error
7
+ import karrio.providers.fedex.utils as provider_utils
8
+ import karrio.providers.fedex.units as provider_units
9
+
10
+ DATETIME_FORMATS = ["%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%dT%H:%M:%S"]
11
+
12
+
13
+ def parse_tracking_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+
19
+ results = response.get("output", {}).get("completeTrackResults") or []
20
+ details = [
21
+ _extract_details(result, settings)
22
+ for result in results
23
+ if result.get("trackResults") is not None
24
+ and result["trackResults"][0].get("scanEvents") is not None
25
+ ]
26
+ messages: typing.List[models.Message] = sum(
27
+ [
28
+ provider_error.parse_error_response(
29
+ result.get("trackResults"),
30
+ settings,
31
+ tracking_number=result.get("trackingNumber"),
32
+ )
33
+ for result in results
34
+ if result.get("trackResults") is not None
35
+ ],
36
+ start=provider_error.parse_error_response(response, settings),
37
+ )
38
+
39
+ return details, messages
40
+
41
+
42
+ def _extract_details(
43
+ result: dict,
44
+ settings: provider_utils.Settings,
45
+ ) -> typing.Optional[models.TrackingDetails]:
46
+ package = lib.to_object(tracking.CompleteTrackResultType, result)
47
+ detail = max(
48
+ package.trackResults,
49
+ key=lambda item: max(
50
+ lib.to_date(event.date, try_formats=DATETIME_FORMATS).replace(tzinfo=None)
51
+ for event in item.scanEvents
52
+ ),
53
+ default=None,
54
+ )
55
+ estimated_delivery = lib.failsafe(
56
+ lambda: lib.fdate(
57
+ detail.standardTransitTimeWindow.window.begins
58
+ or detail.estimatedDeliveryTimeWindow.window.begins,
59
+ try_formats=DATETIME_FORMATS,
60
+ )
61
+ )
62
+ status = next(
63
+ (
64
+ status.name
65
+ for status in list(provider_units.TrackingStatus)
66
+ if detail.latestStatusDetail.code in status.value
67
+ ),
68
+ provider_units.TrackingStatus.in_transit.name,
69
+ )
70
+ delivered = status == "delivered"
71
+ img = lib.failsafe(
72
+ lambda: (
73
+ provider_utils.get_proof_of_delivery(package.trackingNumber, settings)
74
+ if delivered
75
+ else None
76
+ )
77
+ )
78
+
79
+ return models.TrackingDetails(
80
+ carrier_name=settings.carrier_name,
81
+ carrier_id=settings.carrier_id,
82
+ tracking_number=package.trackingNumber,
83
+ events=[
84
+ models.TrackingEvent(
85
+ date=lib.fdate(e.date, try_formats=DATETIME_FORMATS),
86
+ time=lib.flocaltime(e.date, try_formats=DATETIME_FORMATS),
87
+ code=e.eventType,
88
+ location=lib.identity(
89
+ lib.join(
90
+ e.scanLocation.city,
91
+ e.scanLocation.stateOrProvinceCode,
92
+ e.scanLocation.postalCode,
93
+ e.scanLocation.countryCode,
94
+ join=True,
95
+ separator=", ",
96
+ )
97
+ if e.scanLocation
98
+ else e.locationType
99
+ ),
100
+ description=(lib.text(e.exceptionDescription) or e.eventDescription),
101
+ )
102
+ for e in detail.scanEvents
103
+ ],
104
+ info=models.TrackingInfo(
105
+ carrier_tracking_link=settings.tracking_url.format(package.trackingNumber),
106
+ shipment_service=lib.failsafe(lambda: detail.serviceDetail.description),
107
+ package_weight_unit=lib.failsafe(
108
+ lambda: detail.shipmentDetails.weight[0].unit
109
+ ),
110
+ package_weight=lib.failsafe(
111
+ lambda: lib.to_decimal(detail.shipmentDetails.weight[0].value)
112
+ ),
113
+ shipment_destination_postal_code=lib.failsafe(
114
+ lambda: detail.destinationLocation.locationContactAndAddress.address.postalCode
115
+ ),
116
+ shipment_destination_country=lib.failsafe(
117
+ lambda: detail.destinationLocation.locationContactAndAddress.address.countryCode
118
+ ),
119
+ shipment_origin_postal_code=lib.failsafe(
120
+ lambda: detail.originLocation.locationContactAndAddress.address.postalCode
121
+ ),
122
+ shipment_origin_country=lib.failsafe(
123
+ lambda: detail.originLocation.locationContactAndAddress.address.countryCode
124
+ ),
125
+ signed_by=lib.failsafe(lambda: detail.deliveryDetails.signedByName),
126
+ ),
127
+ images=lib.identity(models.Images(signature_image=img) if img else None),
128
+ estimated_delivery=estimated_delivery,
129
+ delivered=(status == "delivered"),
130
+ status=status,
131
+ )
132
+
133
+
134
+ def tracking_request(
135
+ payload: models.TrackingRequest,
136
+ settings: provider_utils.Settings,
137
+ ) -> lib.Serializable:
138
+ request = fedex.TrackingRequestType(
139
+ includeDetailedScans=True,
140
+ trackingInfo=[
141
+ fedex.TrackingInfoType(
142
+ shipDateBegin=None,
143
+ shipDateEnd=None,
144
+ trackingNumberInfo=fedex.TrackingNumberInfoType(
145
+ trackingNumber=tracking_number,
146
+ carrierCode=None,
147
+ trackingNumberUniqueId=None,
148
+ ),
149
+ )
150
+ for tracking_number in payload.tracking_numbers
151
+ ],
152
+ )
153
+
154
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,501 @@
1
+ import karrio.lib as lib
2
+ import karrio.core.units as units
3
+
4
+ PRESET_DEFAULTS = dict(
5
+ dimension_unit="IN",
6
+ weight_unit="LB",
7
+ )
8
+ MeasurementOptions = units.MeasurementOptionsType(
9
+ min_in=1,
10
+ min_cm=1,
11
+ )
12
+ COUNTRY_PREFERED_UNITS = dict(
13
+ US=(units.WeightUnit.LB, units.DimensionUnit.IN),
14
+ )
15
+
16
+
17
+ class PackagePresets(lib.Enum):
18
+ fedex_envelope_legal_size = units.PackagePreset(
19
+ **dict(weight=1.0, width=9.5, height=15.5, length=1, packaging_type="envelope"),
20
+ **PRESET_DEFAULTS
21
+ )
22
+ fedex_envelope_without_pouch = units.PackagePreset(
23
+ **dict(weight=1.0, width=9.5, height=15.5, length=1, packaging_type="envelope"),
24
+ **PRESET_DEFAULTS
25
+ )
26
+ fedex_padded_pak = units.PackagePreset(
27
+ **dict(weight=2.2, width=11.75, height=14.75, length=1, packaging_type="pak"),
28
+ **PRESET_DEFAULTS
29
+ )
30
+ fedex_polyethylene_pak = units.PackagePreset(
31
+ **dict(weight=2.2, width=12.0, height=15.5, length=1, packaging_type="pak"),
32
+ **PRESET_DEFAULTS
33
+ )
34
+ fedex_clinical_pak = units.PackagePreset(
35
+ **dict(weight=2.2, width=13.5, height=18.0, length=1, packaging_type="pak"),
36
+ **PRESET_DEFAULTS
37
+ )
38
+ fedex_un_3373_pak = units.PackagePreset(
39
+ **dict(weight=2.2, width=13.5, height=18.0, length=1, packaging_type="pak"),
40
+ **PRESET_DEFAULTS
41
+ )
42
+ fedex_small_box = units.PackagePreset(
43
+ **dict(
44
+ weight=20.0,
45
+ width=12.25,
46
+ height=10.9,
47
+ length=1.5,
48
+ packaging_type="small_box",
49
+ ),
50
+ **PRESET_DEFAULTS
51
+ )
52
+ fedex_medium_box = units.PackagePreset(
53
+ **dict(
54
+ weight=20.0,
55
+ width=13.25,
56
+ height=11.5,
57
+ length=2.38,
58
+ packaging_type="medium_box",
59
+ ),
60
+ **PRESET_DEFAULTS
61
+ )
62
+ fedex_large_box = units.PackagePreset(
63
+ **dict(
64
+ weight=20.0,
65
+ width=17.88,
66
+ height=12.38,
67
+ length=3.0,
68
+ packaging_type="large_box",
69
+ ),
70
+ **PRESET_DEFAULTS
71
+ )
72
+ fedex_extra_large_box = units.PackagePreset(
73
+ **dict(
74
+ weight=20.0,
75
+ width=11.88,
76
+ height=11.00,
77
+ length=10.75,
78
+ packaging_type="extra_large_box",
79
+ ),
80
+ **PRESET_DEFAULTS
81
+ )
82
+ fedex_10_kg_box = units.PackagePreset(
83
+ **dict(
84
+ weight=10.0,
85
+ width=15.81,
86
+ height=12.94,
87
+ length=10.19,
88
+ packaging_type="medium_box",
89
+ ),
90
+ **PRESET_DEFAULTS
91
+ )
92
+ fedex_25_kg_box = units.PackagePreset(
93
+ **dict(
94
+ weight=25.0,
95
+ width=21.56,
96
+ height=16.56,
97
+ length=13.19,
98
+ packaging_type="medium_box",
99
+ ),
100
+ **PRESET_DEFAULTS
101
+ )
102
+ fedex_tube = units.PackagePreset(
103
+ **dict(weight=20.0, width=38.0, height=6.0, length=6.0, packaging_type="tube"),
104
+ **PRESET_DEFAULTS
105
+ )
106
+ fedex_envelope = fedex_envelope_legal_size
107
+ fedex_pak = fedex_padded_pak
108
+
109
+
110
+ class LabelType(lib.Enum):
111
+ PDF_4x6 = ("PDF", "STOCK_4X6")
112
+ PDF_4x6_75 = ("PDF", "STOCK_4X6.75")
113
+ PDF_4x8 = ("PDF", "STOCK_4X8")
114
+ PDF_4x9 = ("PDF", "STOCK_4X9")
115
+ ZPL_4x6 = ("ZPLII", "STOCK_4X6")
116
+ ZPL_4x6_75 = ("ZPLII", "STOCK_4X6.75")
117
+ ZPL_4x8 = ("ZPLII", "STOCK_4X8")
118
+ ZPL_4x9 = ("ZPLII", "STOCK_4X9")
119
+
120
+ """ Unified Label type mapping """
121
+ PDF = PDF_4x6
122
+ ZPL = ZPL_4x6
123
+
124
+
125
+ class Incoterm(lib.Enum):
126
+ DDP = "DDP"
127
+ DDU = "DDU"
128
+ DAP = "DAP"
129
+ DAT = "DAT"
130
+ EXW = "EXW"
131
+ CPT = "CPT"
132
+ C_F = "C&F"
133
+ CIP = "CIP"
134
+ CIF = "CIF"
135
+ FCA = "FCA"
136
+ FOB = "FOB"
137
+
138
+
139
+ class PurposeType(lib.Enum):
140
+ gift = "GIFT"
141
+ not_sold = "NOT_SOLD"
142
+ personal_effects = "PERSONAL_EFFECTS"
143
+ repair_and_return = "REPAIR_AND_RETURN"
144
+ sample = "SAMPLE"
145
+ sold = "SOLD"
146
+ other = None
147
+
148
+ """ Unified Content type mapping """
149
+ documents = other
150
+ merchandise = sold
151
+ return_merchandise = repair_and_return
152
+
153
+
154
+ class PackagingType(lib.Enum):
155
+ fedex_envelope = "FEDEX_ENVELOPE"
156
+ fedex_box = "FEDEX_BOX"
157
+ fedex_small_box = "FEDEX_SMALL_BOX"
158
+ fedex_medium_box = "FEDEX_MEDIUM_BOX"
159
+ fedex_large_box = "FEDEX_LARGE_BOX"
160
+ fedex_extra_large_box = "FEDEX_EXTRA_LARGE_BOX"
161
+ fedex_10kg_box = "FEDEX_10KG_BOX"
162
+ fedex_25kg_box = "FEDEX_25KG_BOX"
163
+ fedex_pak = "FEDEX_PAK"
164
+ fedex_tube = "FEDEX_TUBE"
165
+ your_packaging = "YOUR_PACKAGING"
166
+
167
+ """ Unified Packaging type mapping """
168
+ envelope = fedex_envelope
169
+ pak = fedex_pak
170
+ tube = fedex_tube
171
+ pallet = your_packaging
172
+ small_box = fedex_small_box
173
+ medium_box = fedex_medium_box
174
+ large_box = fedex_large_box
175
+ extra_large_box = fedex_extra_large_box
176
+
177
+
178
+ class SubPackageType(lib.Enum):
179
+ fedex_bag = "BAG"
180
+ fedex_barrel = "BARREL"
181
+ fedex_basket = "BASKET"
182
+ fedex_box = "BOX"
183
+ fedex_bucket = "BUCKET"
184
+ fedex_bundle = "BUNDLE"
185
+ fedex_cage = "CAGE"
186
+ fedex_carton = "CARTON"
187
+ fedex_case = "CASE"
188
+ fedex_chest = "CHEST"
189
+ fedex_container = "CONTAINER"
190
+ fedex_crate = "CRATE"
191
+ fedex_cylinder = "CYLINDER"
192
+ fedex_drum = "DRUM"
193
+ fedex_envelope = "ENVELOPE"
194
+ fedex_hamper = "HAMPER"
195
+ fedex_other = "OTHER"
196
+ fedex_package = "PACKAGE"
197
+ fedex_pail = "PAIL"
198
+ fedex_pallet = "PALLET"
199
+ fedex_parcel = "PARCEL"
200
+ fedex_piece = "PIECE"
201
+ fedex_reel = "REEL"
202
+ fedex_roll = "ROLL"
203
+ fedex_sack = "SACK"
204
+ fedex_shrinkwrapped = "SHRINKWRAPPED"
205
+ fedex_skid = "SKID"
206
+ fedex_tank = "TANK"
207
+ fedex_totebin = "TOTEBIN"
208
+ fedex_tube = "TUBE"
209
+ fedex_unit = "UNIT"
210
+
211
+ """ Unified Packaging type mapping """
212
+ envelope = fedex_envelope
213
+ pak = fedex_other
214
+ tube = fedex_tube
215
+ pallet = fedex_pallet
216
+ small_box = fedex_parcel
217
+ medium_box = fedex_parcel
218
+ large_box = fedex_parcel
219
+ extra_large_box = fedex_parcel
220
+ your_packaging = fedex_other
221
+
222
+
223
+ class PaymentType(lib.Enum):
224
+ account = "ACCOUNT"
225
+ collect = "COLLECT"
226
+ recipient = "RECIPIENT"
227
+ sender = "SENDER"
228
+ third_party = "THIRD_PARTY"
229
+
230
+
231
+ class ConnectionConfig(lib.Enum):
232
+ label_type = lib.OptionEnum("label_type", LabelType)
233
+ smart_post_hub_id = lib.OptionEnum("smart_post_hub_id")
234
+ shipping_options = lib.OptionEnum("shipping_options", list)
235
+ shipping_services = lib.OptionEnum("shipping_services", list)
236
+ locale = lib.OptionEnum(
237
+ "locale", lib.units.create_enum("Locale", ["en_US", "fr_CA"])
238
+ )
239
+
240
+
241
+ class ShippingService(lib.Enum):
242
+ fedex_international_priority_express = "FEDEX_INTERNATIONAL_PRIORITY_EXPRESS"
243
+ fedex_international_first = "INTERNATIONAL_FIRST"
244
+ fedex_international_priority = "FEDEX_INTERNATIONAL_PRIORITY"
245
+ fedex_international_economy = "INTERNATIONAL_ECONOMY"
246
+ fedex_ground = "FEDEX_GROUND"
247
+ fedex_cargo_mail = "FEDEX_CARGO_MAIL"
248
+ fedex_cargo_international_premium = "FEDEX_CARGO_INTERNATIONAL_PREMIUM"
249
+ fedex_first_overnight = "FIRST_OVERNIGHT"
250
+ fedex_first_overnight_freight = "FIRST_OVERNIGHT_FREIGHT"
251
+ fedex_1_day_freight = "FEDEX_1_DAY_FREIGHT"
252
+ fedex_2_day_freight = "FEDEX_2_DAY_FREIGHT"
253
+ fedex_3_day_freight = "FEDEX_3_DAY_FREIGHT"
254
+ fedex_international_priority_freight = "INTERNATIONAL_PRIORITY_FREIGHT"
255
+ fedex_international_economy_freight = "INTERNATIONAL_ECONOMY_FREIGHT"
256
+ fedex_cargo_airport_to_airport = "FEDEX_CARGO_AIRPORT_TO_AIRPORT"
257
+ fedex_international_priority_distribution = "INTERNATIONAL_PRIORITY_DISTRIBUTION"
258
+ fedex_ip_direct_distribution_freight = "FEDEX_IP_DIRECT_DISTRIBUTION_FREIGHT"
259
+ fedex_intl_ground_distribution = "INTL_GROUND_DISTRIBUTION"
260
+ fedex_ground_home_delivery = "GROUND_HOME_DELIVERY"
261
+ fedex_smart_post = "SMART_POST"
262
+ fedex_priority_overnight = "PRIORITY_OVERNIGHT"
263
+ fedex_standard_overnight = "STANDARD_OVERNIGHT"
264
+ fedex_2_day = "FEDEX_2_DAY"
265
+ fedex_2_day_am = "FEDEX_2_DAY_AM"
266
+ fedex_express_saver = "FEDEX_EXPRESS_SAVER"
267
+ fedex_same_day = "SAME_DAY"
268
+ fedex_same_day_city = "SAME_DAY_CITY"
269
+ fedex_one_day_freight = "FEDEX_ONE_DAY_FREIGHT"
270
+ fedex_international_economy_distribution = "INTERNATIONAL_ECONOMY_DISTRIBUTION"
271
+ fedex_international_connect_plus = "FEDEX_INTERNATIONAL_CONNECT_PLUS"
272
+ fedex_international_distribution_freight = "INTERNATIONAL_DISTRIBUTION_FREIGHT"
273
+ fedex_regional_economy = "FEDEX_REGIONAL_ECONOMY"
274
+ fedex_next_day_freight = "FEDEX_NEXT_DAY_FREIGHT"
275
+ fedex_next_day = "FEDEX_NEXT_DAY"
276
+ fedex_next_day_10am = "FEDEX_NEXT_DAY_10AM"
277
+ fedex_next_day_12pm = "FEDEX_NEXT_DAY_12PM"
278
+ fedex_next_day_end_of_day = "FEDEX_NEXT_DAY_END_OF_DAY"
279
+ fedex_distance_deferred = "FEDEX_DISTANCE_DEFERRED"
280
+
281
+
282
+ class ShippingOption(lib.Enum):
283
+ # fmt: off
284
+ fedex_appointment = lib.OptionEnum("APPOINTMENT", bool)
285
+ fedex_broker_select_option = lib.OptionEnum("BROKER_SELECT_OPTION", bool)
286
+ fedex_call_before_delivery = lib.OptionEnum("CALL_BEFORE_DELIVERY", bool)
287
+ fedex_cod = lib.OptionEnum("COD", float)
288
+ fedex_custom_delivery_window = lib.OptionEnum("CUSTOM_DELIVERY_WINDOW", bool)
289
+ fedex_cut_flowers = lib.OptionEnum("CUT_FLOWERS", bool)
290
+ fedex_do_not_break_down_pallets = lib.OptionEnum("DO_NOT_BREAK_DOWN_PALLETS", bool)
291
+ fedex_do_not_stack_pallets = lib.OptionEnum("DO_NOT_STACK_PALLETS", bool)
292
+ fedex_dry_ice = lib.OptionEnum("DRY_ICE", bool)
293
+ fedex_east_coast_special = lib.OptionEnum("EAST_COAST_SPECIAL", bool)
294
+ fedex_exclude_from_consolidation = lib.OptionEnum("EXCLUDE_FROM_CONSOLIDATION", bool)
295
+ fedex_extreme_length = lib.OptionEnum("EXTREME_LENGTH", bool)
296
+ fedex_inside_delivery = lib.OptionEnum("INSIDE_DELIVERY", bool)
297
+ fedex_inside_pickup = lib.OptionEnum("INSIDE_PICKUP", bool)
298
+ fedex_international_controlled_export_service = lib.OptionEnum("INTERNATIONAL_CONTROLLED_EXPORT_SERVICE", bool)
299
+ fedex_third_party_consignee = lib.OptionEnum("THIRD_PARTY_CONSIGNEE", bool)
300
+ fedex_electronic_trade_documents = lib.OptionEnum("ELECTRONIC_TRADE_DOCUMENTS", bool)
301
+ fedex_food = lib.OptionEnum("FOOD", bool)
302
+ fedex_future_day_shipment = lib.OptionEnum("FUTURE_DAY_SHIPMENT", bool)
303
+ fedex_hold_at_location = lib.OptionEnum("HOLD_AT_LOCATION", bool)
304
+ fedex_international_traffic_in_arms_regulations = lib.OptionEnum("INTERNATIONAL_TRAFFIC_IN_ARMS_REGULATIONS", bool)
305
+ fedex_liftgate_delivery = lib.OptionEnum("LIFTGATE_DELIVERY", bool)
306
+ fedex_liftgate_pickup = lib.OptionEnum("LIFTGATE_PICKUP", bool)
307
+ fedex_limited_access_delivery = lib.OptionEnum("LIMITED_ACCESS_DELIVERY", bool)
308
+ fedex_limited_access_pickup = lib.OptionEnum("LIMITED_ACCESS_PICKUP", bool)
309
+ fedex_over_length = lib.OptionEnum("OVER_LENGTH", bool)
310
+ fedex_pending_shipment = lib.OptionEnum("PENDING_SHIPMENT", bool)
311
+ fedex_pharmacy_delivery = lib.OptionEnum("PHARMACY_DELIVERY", bool)
312
+ fedex_poison = lib.OptionEnum("POISON", bool)
313
+ fedex_home_delivery_premium = lib.OptionEnum("HOME_DELIVERY_PREMIUM", bool)
314
+ fedex_protection_from_freezing = lib.OptionEnum("PROTECTION_FROM_FREEZING", bool)
315
+ fedex_returns_clearance = lib.OptionEnum("RETURNS_CLEARANCE", bool)
316
+ fedex_return_shipment = lib.OptionEnum("RETURN_SHIPMENT", bool)
317
+ fedex_saturday_pickup = lib.OptionEnum("SATURDAY_PICKUP", bool)
318
+ fedex_event_notification = lib.OptionEnum("EVENT_NOTIFICATION", bool)
319
+ fedex_delivery_on_invoice_acceptance = lib.OptionEnum("DELIVERY_ON_INVOICE_ACCEPTANCE", bool)
320
+ fedex_top_load = lib.OptionEnum("TOP_LOAD", bool)
321
+
322
+ """ Rating Options """
323
+ fedex_one_rate = lib.OptionEnum("FEDEX_ONE_RATE", bool)
324
+ fedex_freight_guarantee = lib.OptionEnum("FREIGHT_GUARANTEE", bool)
325
+ fedex_saturday_delivery = lib.OptionEnum("SATURDAY_DELIVERY", bool)
326
+ fedex_smart_post_hub_id = lib.OptionEnum("SMART_POST_HUB_ID")
327
+ fedex_smart_post_allowed_indicia = lib.OptionEnum("SMART_POST_ALLOWED_INDICIA")
328
+
329
+ """ Package Options """
330
+
331
+ fedex_alcohol = lib.OptionEnum("ALCOHOL", bool)
332
+ fedex_battery = lib.OptionEnum("BATTERY", bool)
333
+ fedex_dangerous_goods = lib.OptionEnum("DANGEROUS_GOODS", bool)
334
+ fedex_priority_alert = lib.OptionEnum("PRIORITY_ALERT", bool)
335
+ fedex_priority_alert_plus = lib.OptionEnum("PRIORITY_ALERT_PLUS", bool)
336
+ fedex_non_standard_container = lib.OptionEnum("NON_STANDARD_CONTAINER", bool)
337
+ fedex_piece_count_verification = lib.OptionEnum("PIECE_COUNT_VERIFICATION", bool)
338
+ fedex_signature_option = lib.OptionEnum("SIGNATURE_OPTION")
339
+ fedex_evening = lib.OptionEnum("EVENING", bool)
340
+ fedex_date_certain = lib.OptionEnum("DATE_CERTAIN", bool)
341
+
342
+ """ Unified Option type mapping """
343
+ cash_on_delivery = fedex_cod
344
+ dangerous_good = fedex_dangerous_goods
345
+ notification = fedex_event_notification
346
+ saturday_delivery = fedex_saturday_delivery
347
+ paperless_trade = fedex_electronic_trade_documents
348
+ doc_files = lib.OptionEnum("doc_files", lib.to_dict)
349
+ doc_references = lib.OptionEnum("doc_references", lib.to_dict)
350
+ shipper_instructions = lib.OptionEnum("shipper_instructions")
351
+ recipient_instructions = lib.OptionEnum("recipient_instructions")
352
+ # fmt: on
353
+
354
+
355
+ RATING_OPTIONS = [
356
+ "FREIGHT_GUARANTEE",
357
+ "SATURDAY_DELIVERY",
358
+ "SMART_POST_ALLOWED_INDICIA",
359
+ "SMART_POST_HUB_ID",
360
+ ]
361
+ PACKAGE_OPTIONS = [
362
+ "ALCOHOL",
363
+ "APPOINTMENT",
364
+ "BATTERY",
365
+ "COD",
366
+ "DANGEROUS_GOODS",
367
+ "DRY_ICE",
368
+ "PRIORITY_ALERT",
369
+ "PRIORITY_ALERT_PLUS",
370
+ "NON_STANDARD_CONTAINER",
371
+ "PIECE_COUNT_VERIFICATION",
372
+ "SIGNATURE_OPTION",
373
+ "EVENING",
374
+ "DATE_CERTAIN",
375
+ "SATURDAY_PICKUP",
376
+ ]
377
+ SHIPMENT_OPTIONS = [
378
+ "APPOINTMENT",
379
+ "BROKER_SELECT_OPTION",
380
+ "CALL_BEFORE_DELIVERY",
381
+ "COD",
382
+ "CUSTOM_DELIVERY_WINDOW",
383
+ "CUT_FLOWERS",
384
+ "DO_NOT_BREAK_DOWN_PALLETS",
385
+ "DO_NOT_STACK_PALLETS",
386
+ "DRY_ICE",
387
+ "EAST_COAST_SPECIAL",
388
+ "EXCLUDE_FROM_CONSOLIDATION",
389
+ "EXTREME_LENGTH",
390
+ "INSIDE_DELIVERY",
391
+ "INSIDE_PICKUP",
392
+ "INTERNATIONAL_CONTROLLED_EXPORT_SERVICE",
393
+ "FEDEX_ONE_RATE",
394
+ "THIRD_PARTY_CONSIGNEE",
395
+ "ELECTRONIC_TRADE_DOCUMENTS",
396
+ "FOOD",
397
+ "HOLD_AT_LOCATION",
398
+ "INTERNATIONAL_TRAFFIC_IN_ARMS_REGULATIONS",
399
+ "LIFTGATE_DELIVERY",
400
+ "LIFTGATE_PICKUP",
401
+ "LIMITED_ACCESS_DELIVERY",
402
+ "LIMITED_ACCESS_PICKUP",
403
+ "OVER_LENGTH",
404
+ "PENDING_SHIPMENT",
405
+ "PHARMACY_DELIVERY",
406
+ "POISON",
407
+ "HOME_DELIVERY_PREMIUM",
408
+ "PROTECTION_FROM_FREEZING",
409
+ "RETURNS_CLEARANCE",
410
+ "RETURN_SHIPMENT",
411
+ "SATURDAY_DELIVERY",
412
+ "SATURDAY_PICKUP",
413
+ "EVENT_NOTIFICATION",
414
+ "DELIVERY_ON_INVOICE_ACCEPTANCE",
415
+ "TOP_LOAD",
416
+ "FREIGHT_GUARANTEE",
417
+ ]
418
+
419
+
420
+ def shipping_options_initializer(
421
+ options: dict,
422
+ package_options: units.Options = None,
423
+ ) -> units.Options:
424
+ """
425
+ Apply default values to the given options.
426
+ """
427
+ _options = options.copy()
428
+ _add_signature = "fedex_signature_option" not in options
429
+
430
+ if package_options is not None:
431
+ _options.update(package_options.content)
432
+
433
+ if _add_signature:
434
+ _options.update(
435
+ dict(
436
+ fedex_signature_option=(
437
+ "ADULT"
438
+ if _options.get("signature_confirmation")
439
+ else "SERVICE_DEFAULT"
440
+ )
441
+ )
442
+ )
443
+
444
+ def items_filter(key: str) -> bool:
445
+ return key in ShippingOption and key not in ["doc_files", "doc_references"] # type: ignore
446
+
447
+ return units.ShippingOptions(_options, ShippingOption, items_filter=items_filter)
448
+
449
+
450
+ class RateType(lib.Enum):
451
+ payor_account_package = "PAYOR_ACCOUNT_PACKAGE"
452
+ payor_account_shipment = "PAYOR_ACCOUNT_SHIPMENT"
453
+ payor_list_package = "PAYOR_LIST_PACKAGE"
454
+ payor_list_shipment = "PAYOR_LIST_SHIPMENT"
455
+ preferred_account_package = "PREFERRED_ACCOUNT_PACKAGE"
456
+ preferred_account_shipment = "PREFERRED_ACCOUNT_SHIPMENT"
457
+ preferred_list_package = "PREFERRED_LIST_PACKAGE"
458
+ preferred_list_shipment = "PREFERRED_LIST_SHIPMENT"
459
+
460
+
461
+ class SignatureOptionType(lib.Enum):
462
+ adult = "ADULT"
463
+ direct = "DIRECT"
464
+ indirect = "INDIRECT"
465
+ no_signature_required = "NO_SIGNATURE_REQUIRED"
466
+ service_default = "SERVICE_DEFAULT"
467
+
468
+
469
+ class UploadDocumentType(lib.Enum):
470
+ fedex_usmca_commercial_invoice_certification_of_origin = (
471
+ "USMCA_COMMERCIAL_INVOICE_CERTIFICATION_OF_ORIGIN"
472
+ )
473
+ fedex_usmca_certification_of_origin = "USMCA_CERTIFICATION_OF_ORIGIN"
474
+ fedex_certificate_of_origin = "CERTIFICATE_OF_ORIGIN"
475
+ fedex_commercial_invoice = "COMMERCIAL_INVOICE"
476
+ fedex_pro_forma_invoice = "PRO_FORMA_INVOICE"
477
+ fedex_net_rate_sheet = "NET_RATE_SHEET"
478
+ fedex_etd_label = "ETD_LABEL"
479
+ fedex_other = "OTHER"
480
+
481
+ """ Unified upload document type mapping """
482
+ certificate_of_origin = fedex_certificate_of_origin
483
+ commercial_invoice = fedex_commercial_invoice
484
+ pro_forma_invoice = fedex_pro_forma_invoice
485
+ packing_list = fedex_other
486
+ other = fedex_other
487
+
488
+
489
+ class DocumentUploadOption(lib.Enum):
490
+ fedex_carrier_code = lib.OptionEnum("carrierCode", str)
491
+ pre_shipment = lib.OptionEnum("pre_shipment", bool)
492
+
493
+
494
+ class TrackingStatus(lib.Enum):
495
+ on_hold = ["DE", "SE"]
496
+ delivered = ["DL"]
497
+ in_transit = ["IT", "IX"]
498
+ delivery_failed = ["CA", "RS"]
499
+ delivery_delayed = ["DY", "DD"]
500
+ out_for_delivery = ["AD", "ED", "OD"]
501
+ ready_for_pickup = ["HL"]