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,148 @@
1
+ import karrio.schemas.fedex.pickup_request as fedex
2
+ import karrio.schemas.fedex.pickup_response as pickup
3
+
4
+ import typing
5
+ import karrio.lib as lib
6
+ import karrio.core.units as units
7
+ import karrio.core.models as models
8
+ import karrio.providers.fedex.error as error
9
+ import karrio.providers.fedex.utils as provider_utils
10
+ import karrio.providers.fedex.units as provider_units
11
+
12
+
13
+ def parse_pickup_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[typing.List[models.PickupDetails], typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+
19
+ messages = error.parse_error_response(response, settings)
20
+ pickup = lib.identity(
21
+ _extract_details(response, settings, _response.ctx)
22
+ if (response.get("output") or {}).get("pickupConfirmationCode")
23
+ else None
24
+ )
25
+
26
+ return pickup, messages
27
+
28
+
29
+ def _extract_details(
30
+ data: dict,
31
+ settings: provider_utils.Settings,
32
+ ctx: dict,
33
+ ) -> models.PickupDetails:
34
+ details = lib.to_object(pickup.PickupResponseType, data)
35
+
36
+ return models.PickupDetails(
37
+ carrier_id=settings.carrier_id,
38
+ carrier_name=settings.carrier_name,
39
+ confirmation_number=details.output.pickupConfirmationCode,
40
+ pickup_date=lib.fdate(ctx["pickup_date"]),
41
+ )
42
+
43
+
44
+ def pickup_request(
45
+ payload: models.PickupRequest,
46
+ settings: provider_utils.Settings,
47
+ ) -> lib.Serializable:
48
+ address = lib.to_address(payload.address)
49
+ packages = lib.to_packages(payload.parcels)
50
+ options = lib.units.Options(
51
+ payload.options,
52
+ option_type=lib.units.create_enum(
53
+ "PickupOptions",
54
+ # fmt: off
55
+ {
56
+ "fedex_carrier_code": lib.OptionEnum("carrierCode"),
57
+ "fedex_pickup_location": lib.OptionEnum("location"),
58
+ "fedex_user_message": lib.OptionEnum("userMessage"),
59
+ "fedex_early_pickup": lib.OptionEnum("earlyPickup"),
60
+ "fedex_building_part": lib.OptionEnum("buildingPart"),
61
+ "fedex_pickup_date_type": lib.OptionEnum("pickupDateType"),
62
+ "fedex_supplies_requested": lib.OptionEnum("suppliesRequested"),
63
+ "fedex_pickup_address_type": lib.OptionEnum("pickupAddressType"),
64
+ "fedex_building_part_description": lib.OptionEnum("buildingPartDescription"),
65
+ "fedex_associated_account_number_type": lib.OptionEnum("associatedAccountNumberType"),
66
+ },
67
+ # fmt: on
68
+ ),
69
+ )
70
+
71
+ # map data to convert karrio model to fedex specific type
72
+ request = fedex.PickupRequestType(
73
+ associatedAccountNumber=fedex.AccountNumberType(
74
+ value=settings.account_number,
75
+ ),
76
+ originDetail=fedex.OriginDetailType(
77
+ pickupAddressType=options.fedex_pickup_address_type.state,
78
+ pickupLocation=fedex.PickupLocationType(
79
+ contact=fedex.ContactType(
80
+ companyName=address.company_name,
81
+ personName=address.person_name,
82
+ phoneNumber=address.phone_number,
83
+ phoneExtension=None,
84
+ ),
85
+ address=fedex.AccountAddressOfRecordType(
86
+ streetLines=address.address_lines,
87
+ city=address.city,
88
+ stateOrProvinceCode=address.state_code,
89
+ postalCode=address.postal_code,
90
+ countryCode=address.country_code,
91
+ residential=address.residential,
92
+ addressClassification=None,
93
+ urbanizationCode=None,
94
+ ),
95
+ accountNumber=fedex.AccountNumberType(
96
+ value=settings.account_number,
97
+ ),
98
+ deliveryInstructions=options.instructions.state,
99
+ ),
100
+ readyDateTimestamp=f"{payload.pickup_date}T{payload.closing_time}:00Z",
101
+ customerCloseTime=f"{payload.closing_time}:00",
102
+ pickupDateType=options.fedex_pickup_date_type.state,
103
+ packageLocation=payload.package_location,
104
+ buildingPart=options.fedex_building_part.state,
105
+ buildingPartDescription=options.fedex_building_part_description.state,
106
+ earlyPickup=options.fedex_early_pickup.state,
107
+ suppliesRequested=options.fedex_supplies_requested.state,
108
+ geographicalPostalCode=options.geographical_postal_code.state,
109
+ ),
110
+ associatedAccountNumberType=options.fedex_associated_account_number_type.state,
111
+ totalWeight=fedex.TotalWeightType(
112
+ units=packages.weight_unit,
113
+ value=packages.weight.value,
114
+ ),
115
+ packageCount=len(packages),
116
+ carrierCode=options.fedex_carrier_code.state or "FDXE",
117
+ accountAddressOfRecord=None,
118
+ remarks=None,
119
+ countryRelationships=None,
120
+ pickupType=None,
121
+ trackingNumber=None,
122
+ commodityDescription=lib.text(packages.description, max=100),
123
+ expressFreightDetail=None,
124
+ oversizePackageCount=None,
125
+ pickupNotificationDetail=lib.identity(
126
+ fedex.PickupNotificationDetailType(
127
+ emailDetails=[
128
+ fedex.EmailDetailType(
129
+ address=address.email,
130
+ locale="en_US",
131
+ )
132
+ ],
133
+ format="TEXT",
134
+ userMessage=options.fedex_user_message.state,
135
+ )
136
+ if address.email
137
+ else None
138
+ ),
139
+ )
140
+
141
+ return lib.Serializable(
142
+ request,
143
+ lib.to_dict,
144
+ dict(
145
+ pickup_date=payload.pickup_date,
146
+ address=payload.address,
147
+ ),
148
+ )
@@ -0,0 +1,162 @@
1
+ import karrio.schemas.fedex.pickup_request as fedex
2
+ import karrio.schemas.fedex.pickup_response as pickup
3
+
4
+ import typing
5
+ import karrio.lib as lib
6
+ import karrio.core.units as units
7
+ import karrio.core.models as models
8
+ import karrio.providers.fedex.error as error
9
+ import karrio.providers.fedex.utils as provider_utils
10
+ import karrio.providers.fedex.units as provider_units
11
+ import karrio.providers.fedex.pickup.cancel as cancel
12
+
13
+
14
+ def parse_pickup_update_response(
15
+ _response: lib.Deserializable[dict],
16
+ settings: provider_utils.Settings,
17
+ ) -> typing.Tuple[typing.List[models.PickupDetails], typing.List[models.Message]]:
18
+ response = _response.deserialize()
19
+
20
+ messages = error.parse_error_response(response, settings)
21
+ pickup = lib.identity(
22
+ _extract_details(response, settings, _response.ctx)
23
+ if (response.get("output") or {}).get("pickupConfirmationCode")
24
+ else None
25
+ )
26
+
27
+ return pickup, messages
28
+
29
+
30
+ def _extract_details(
31
+ data: dict,
32
+ settings: provider_utils.Settings,
33
+ ctx: dict,
34
+ ) -> models.PickupDetails:
35
+ details = lib.to_object(pickup.PickupResponseType, data)
36
+
37
+ return models.PickupDetails(
38
+ carrier_id=settings.carrier_id,
39
+ carrier_name=settings.carrier_name,
40
+ confirmation_number=details.output.pickupConfirmationCode,
41
+ pickup_date=lib.fdate(ctx["pickup_date"]),
42
+ )
43
+
44
+
45
+ def pickup_update_request(
46
+ payload: models.PickupUpdateRequest,
47
+ settings: provider_utils.Settings,
48
+ ) -> lib.Serializable:
49
+ address = lib.to_address(payload.address)
50
+ packages = lib.to_packages(payload.parcels)
51
+ options = lib.units.Options(
52
+ payload.options,
53
+ option_type=lib.units.create_enum(
54
+ "PickupOptions",
55
+ # fmt: off
56
+ {
57
+ "fedex_carrier_code": lib.OptionEnum("carrierCode"),
58
+ "fedex_pickup_location": lib.OptionEnum("location"),
59
+ "fedex_user_message": lib.OptionEnum("userMessage"),
60
+ "fedex_early_pickup": lib.OptionEnum("earlyPickup"),
61
+ "fedex_building_part": lib.OptionEnum("buildingPart"),
62
+ "fedex_pickup_date_type": lib.OptionEnum("pickupDateType"),
63
+ "fedex_supplies_requested": lib.OptionEnum("suppliesRequested"),
64
+ "fedex_pickup_address_type": lib.OptionEnum("pickupAddressType"),
65
+ "fedex_building_part_description": lib.OptionEnum("buildingPartDescription"),
66
+ "fedex_associated_account_number_type": lib.OptionEnum("associatedAccountNumberType"),
67
+ },
68
+ # fmt: on
69
+ ),
70
+ )
71
+
72
+ # map data to convert karrio model to fedex specific type
73
+ request = fedex.PickupRequestType(
74
+ associatedAccountNumber=fedex.AccountNumberType(
75
+ value=settings.account_number,
76
+ ),
77
+ originDetail=fedex.OriginDetailType(
78
+ pickupAddressType=options.fedex_pickup_address_type.state,
79
+ pickupLocation=fedex.PickupLocationType(
80
+ contact=fedex.ContactType(
81
+ companyName=address.company_name,
82
+ personName=address.person_name,
83
+ phoneNumber=address.phone_number,
84
+ phoneExtension=None,
85
+ ),
86
+ address=fedex.AccountAddressOfRecordType(
87
+ streetLines=address.address_lines,
88
+ city=address.city,
89
+ stateOrProvinceCode=address.state_code,
90
+ postalCode=address.postal_code,
91
+ countryCode=address.country_code,
92
+ residential=address.residential,
93
+ addressClassification=None,
94
+ urbanizationCode=None,
95
+ ),
96
+ accountNumber=fedex.AccountNumberType(
97
+ value=settings.account_number,
98
+ ),
99
+ deliveryInstructions=options.instructions.state,
100
+ ),
101
+ readyDateTimestamp=f"{payload.pickup_date}T{payload.closing_time}:00Z",
102
+ customerCloseTime=f"{payload.closing_time}:00",
103
+ pickupDateType=options.fedex_pickup_date_type.state,
104
+ packageLocation=payload.package_location,
105
+ buildingPart=options.fedex_building_part.state,
106
+ buildingPartDescription=options.fedex_building_part_description.state,
107
+ earlyPickup=options.fedex_early_pickup.state,
108
+ suppliesRequested=options.fedex_supplies_requested.state,
109
+ geographicalPostalCode=options.geographical_postal_code.state,
110
+ ),
111
+ associatedAccountNumberType=options.fedex_associated_account_number_type.state,
112
+ totalWeight=fedex.TotalWeightType(
113
+ units=packages.weight_unit,
114
+ value=packages.weight.value,
115
+ ),
116
+ packageCount=len(packages),
117
+ carrierCode=options.fedex_carrier_code.state or "FDXE",
118
+ accountAddressOfRecord=None,
119
+ remarks=None,
120
+ countryRelationships=None,
121
+ pickupType=None,
122
+ trackingNumber=None,
123
+ commodityDescription=lib.text(packages.description, max=100),
124
+ expressFreightDetail=None,
125
+ oversizePackageCount=None,
126
+ pickupNotificationDetail=lib.identity(
127
+ fedex.PickupNotificationDetailType(
128
+ emailDetails=[
129
+ fedex.EmailDetailType(
130
+ address=address.email,
131
+ locale="en_US",
132
+ )
133
+ ],
134
+ format="TEXT",
135
+ userMessage=options.fedex_user_message.state,
136
+ )
137
+ if address.email
138
+ else None
139
+ ),
140
+ )
141
+
142
+ return lib.Serializable(
143
+ request,
144
+ lib.to_dict,
145
+ dict(
146
+ confirmation_number=payload.confirmation_number,
147
+ pickup_date=payload.pickup_date,
148
+ address=payload.address,
149
+ cancel=cancel.pickup_cancel_request(
150
+ lib.to_object(
151
+ models.PickupCancelRequest,
152
+ dict(
153
+ confirmation_number=payload.confirmation_number,
154
+ pickup_date=payload.pickup_date,
155
+ address=payload.address,
156
+ reason="update",
157
+ ),
158
+ ),
159
+ settings,
160
+ ),
161
+ ),
162
+ )
@@ -0,0 +1,357 @@
1
+ import karrio.schemas.fedex.rating_request as fedex
2
+ import karrio.schemas.fedex.rating_responses as rating
3
+ import typing
4
+ import datetime
5
+ import karrio.lib as lib
6
+ import karrio.core.units as units
7
+ import karrio.core.models as models
8
+ import karrio.providers.fedex.error as error
9
+ import karrio.providers.fedex.utils as provider_utils
10
+ import karrio.providers.fedex.units as provider_units
11
+
12
+
13
+ def parse_rate_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+ messages = error.parse_error_response(response, settings)
19
+ rates = [
20
+ _extract_details(rate, settings, ctx=_response.ctx)
21
+ for rate in (response.get("output", {}).get("rateReplyDetails") or [])
22
+ ]
23
+
24
+ return rates, messages
25
+
26
+
27
+ def _extract_details(
28
+ data: dict,
29
+ settings: provider_utils.Settings,
30
+ ctx: dict = {},
31
+ ) -> models.RateDetails:
32
+ # fmt: off
33
+ rate = lib.to_object(rating.RateReplyDetailType, data)
34
+ service = provider_units.ShippingService.map(rate.serviceType)
35
+ details: rating.RatedShipmentDetailType = lib.identity(
36
+ next((_ for _ in rate.ratedShipmentDetails if _.rateType == "PREFERRED_CURRENCY"), None) or
37
+ next((_ for _ in rate.ratedShipmentDetails if _.rateType == "ACCOUNT"), None) or
38
+ rate.ratedShipmentDetails[0]
39
+ )
40
+
41
+ charges = [
42
+ ("Base Charge", lib.to_money(details.totalBaseCharge)),
43
+ ("Discounts", lib.to_money(details.totalDiscounts)),
44
+ *[(_.type, lib.to_money(_.amount)) for _ in details.shipmentRateDetail.taxes or []],
45
+ *[(_.description, lib.to_money(_.amount)) for _ in details.shipmentRateDetail.surCharges or []],
46
+ ]
47
+ total_charge = lib.to_money(
48
+ details.totalNetChargeWithDutiesAndTaxes
49
+ or details.totalNetCharge
50
+ )
51
+ estimated_delivery = lib.to_date(getattr(rate.operationalDetail, "commitDate", None), "%Y-%m-%dT%H:%M:%S")
52
+ shipping_date = lib.to_date(ctx.get("shipment_date") or datetime.datetime.now())
53
+ transit_day_list = lib.identity(
54
+ (shipping_date + datetime.timedelta(x + 1) for x in range((estimated_delivery.date() - shipping_date.date()).days))
55
+ if estimated_delivery is not None
56
+ else None
57
+ )
58
+ transit_days = lib.identity(
59
+ sum(1 for day in transit_day_list if day.weekday() < 5)
60
+ if transit_day_list is not None
61
+ else None
62
+ )
63
+ # fmt: on
64
+
65
+ return models.RateDetails(
66
+ carrier_id=settings.carrier_id,
67
+ carrier_name=settings.carrier_name,
68
+ service=service.name_or_key,
69
+ total_charge=total_charge,
70
+ currency=details.currency,
71
+ transit_days=transit_days,
72
+ estimated_delivery=lib.fdate(estimated_delivery),
73
+ extra_charges=[
74
+ models.ChargeDetails(
75
+ name=name,
76
+ amount=amount,
77
+ currency=details.currency,
78
+ )
79
+ for name, amount in charges
80
+ if amount is not None
81
+ ],
82
+ meta=dict(
83
+ service_name=service.name or rate.serviceName,
84
+ transit_time=getattr(rate.operationalDetail, "transitTime", None),
85
+ rate_zone=lib.failsafe(lambda: details.shipmentRateDetail.rateZone),
86
+ ),
87
+ )
88
+
89
+
90
+ def rate_request(
91
+ payload: models.RateRequest,
92
+ settings: provider_utils.Settings,
93
+ ) -> lib.Serializable:
94
+ shipper = lib.to_address(payload.shipper)
95
+ recipient = lib.to_address(payload.recipient)
96
+ service = lib.to_services(payload.services, provider_units.ShippingService).first
97
+ options = lib.to_shipping_options(
98
+ payload.options,
99
+ initializer=provider_units.shipping_options_initializer,
100
+ )
101
+ packages = lib.to_packages(
102
+ payload.parcels,
103
+ required=["weight"],
104
+ options=options,
105
+ presets=provider_units.PackagePresets,
106
+ shipping_options_initializer=provider_units.shipping_options_initializer,
107
+ )
108
+
109
+ is_intl = shipper.country_code != recipient.country_code
110
+ default_currency = lib.identity(
111
+ options.currency.state
112
+ or settings.default_currency
113
+ or units.CountryCurrency.map(payload.shipper.country_code).value
114
+ or "USD"
115
+ )
116
+ weight_unit, dim_unit = lib.identity(
117
+ provider_units.COUNTRY_PREFERED_UNITS.get(payload.shipper.country_code)
118
+ or packages.compatible_units
119
+ )
120
+ payment = payload.payment or models.Payment(
121
+ paid_by="sender", account_number=settings.account_number
122
+ )
123
+ request_types = lib.identity(
124
+ settings.connection_config.rate_request_types.state
125
+ if any(settings.connection_config.rate_request_types.state or [])
126
+ else ["LIST", "ACCOUNT", "PREFERRED"]
127
+ )
128
+ shipment_date = lib.to_date(options.shipment_date.state or datetime.datetime.now())
129
+ hub_id = lib.identity(
130
+ lib.text(options.fedex_smart_post_hub_id.state)
131
+ or lib.text(settings.connection_config.smart_post_hub_id.state)
132
+ )
133
+ rate_options = lambda _options: [
134
+ option
135
+ for _, option in _options.items()
136
+ if option.state is not False and option.code in provider_units.RATING_OPTIONS
137
+ ]
138
+ shipment_options = lambda _options: [
139
+ option
140
+ for _, option in _options.items()
141
+ if option.state is not False and option.code in provider_units.SHIPMENT_OPTIONS
142
+ ]
143
+
144
+ customs = lib.to_customs_info(
145
+ payload.customs,
146
+ shipper=payload.shipper,
147
+ recipient=payload.recipient,
148
+ weight_unit=weight_unit.value,
149
+ )
150
+ commodities = lib.identity(
151
+ (customs.commodities if any(customs.commodities) else packages.items)
152
+ if any(packages.items) or any(customs.commodities)
153
+ else units.Products(
154
+ [
155
+ models.Commodity(
156
+ sku="0000",
157
+ quantity=1,
158
+ weight=packages.weight.value,
159
+ weight_unit=packages.weight_unit,
160
+ value_amount=options.declared_value.state or 1.0,
161
+ value_currency=default_currency,
162
+ )
163
+ ]
164
+ )
165
+ )
166
+
167
+ request = fedex.RatingRequestType(
168
+ accountNumber=fedex.RatingRequestAccountNumberType(
169
+ value=settings.account_number,
170
+ ),
171
+ rateRequestControlParameters=fedex.RateRequestControlParametersType(
172
+ returnTransitTimes=True,
173
+ servicesNeededOnRateFailure=True,
174
+ variableOptions=lib.identity(
175
+ ",".join([option.code for option in rate_options(options)])
176
+ if any(rate_options(options))
177
+ else None
178
+ ),
179
+ rateSortOrder=(options.fedex_rate_sort_order.state or "COMMITASCENDING"),
180
+ ),
181
+ requestedShipment=fedex.RequestedShipmentType(
182
+ shipper=fedex.ShipperClassType(
183
+ address=fedex.ResponsiblePartyAddressType(
184
+ streetLines=shipper.address_lines,
185
+ city=shipper.city,
186
+ stateOrProvinceCode=provider_utils.state_code(shipper),
187
+ postalCode=shipper.postal_code,
188
+ countryCode=shipper.country_code,
189
+ residential=shipper.residential,
190
+ )
191
+ ),
192
+ recipient=fedex.ShipperClassType(
193
+ address=fedex.ResponsiblePartyAddressType(
194
+ streetLines=recipient.address_lines,
195
+ city=recipient.city,
196
+ stateOrProvinceCode=provider_utils.state_code(recipient),
197
+ postalCode=recipient.postal_code,
198
+ countryCode=recipient.country_code,
199
+ residential=recipient.residential,
200
+ )
201
+ ),
202
+ serviceType=getattr(service, "value", None),
203
+ emailNotificationDetail=None,
204
+ preferredCurrency=default_currency,
205
+ rateRequestType=request_types,
206
+ shipDateStamp=lib.fdate(shipment_date, "%Y-%m-%d"),
207
+ pickupType="DROPOFF_AT_FEDEX_LOCATION",
208
+ requestedPackageLineItems=[
209
+ fedex.RequestedPackageLineItemType(
210
+ subPackagingType=lib.identity(
211
+ provider_units.SubPackageType.map(package.packaging_type).value
212
+ or "OTHER"
213
+ ),
214
+ groupPackageCount=1,
215
+ contentRecord=[],
216
+ declaredValue=package.options.declared_value.state,
217
+ weight=fedex.WeightType(
218
+ units=package.weight.unit,
219
+ value=package.weight.value,
220
+ ),
221
+ dimensions=(
222
+ fedex.DimensionsType(
223
+ length=package.length.map(
224
+ provider_units.MeasurementOptions
225
+ ).value,
226
+ width=package.width.map(
227
+ provider_units.MeasurementOptions
228
+ ).value,
229
+ height=package.height.map(
230
+ provider_units.MeasurementOptions
231
+ ).value,
232
+ units=package.dimension_unit.value,
233
+ )
234
+ if (
235
+ # only set dimensions if the packaging type is set to your_packaging
236
+ package.has_dimensions
237
+ and provider_units.PackagingType.map(
238
+ package.packaging_type or "your_packaging"
239
+ ).value
240
+ == provider_units.PackagingType.your_packaging.value
241
+ )
242
+ else None
243
+ ),
244
+ variableHandlingChargeDetail=None,
245
+ packageSpecialServices=None,
246
+ )
247
+ for package in packages
248
+ ],
249
+ documentShipment=packages.is_document,
250
+ variableHandlingChargeDetail=None,
251
+ packagingType=provider_units.PackagingType.map(
252
+ packages.package_type or "your_packaging"
253
+ ).value,
254
+ totalWeight=packages.weight.LB,
255
+ shipmentSpecialServices=lib.identity(
256
+ fedex.ShipmentSpecialServicesType(
257
+ returnShipmentDetail=None,
258
+ deliveryOnInvoiceAcceptanceDetail=None,
259
+ internationalTrafficInArmsRegulationsDetail=None,
260
+ pendingShipmentDetail=None,
261
+ holdAtLocationDetail=None,
262
+ shipmentCODDetail=None,
263
+ shipmentDryIceDetail=None,
264
+ internationalControlledExportDetail=None,
265
+ homeDeliveryPremiumDetail=None,
266
+ specialServiceTypes=(
267
+ [option.code for option in shipment_options(options)]
268
+ if any(shipment_options(options))
269
+ else []
270
+ ),
271
+ )
272
+ if any(shipment_options(options))
273
+ else None
274
+ ),
275
+ customsClearanceDetail=lib.identity(
276
+ fedex.CustomsClearanceDetailType(
277
+ brokers=[],
278
+ commercialInvoice=None,
279
+ freightOnValue=None,
280
+ dutiesPayment=fedex.DutiesPaymentType(
281
+ payor=fedex.PayorType(
282
+ responsibleParty=fedex.ResponsiblePartyType(
283
+ address=None,
284
+ contact=None,
285
+ accountNumber=fedex.RatingRequestAccountNumberType(
286
+ value=settings.account_number,
287
+ ),
288
+ ),
289
+ ),
290
+ paymentType=provider_units.PaymentType.map("sender").value,
291
+ ),
292
+ commodities=[
293
+ fedex.CommodityType(
294
+ description=lib.text(
295
+ item.description or item.title or "N/A", max=35
296
+ ),
297
+ weight=fedex.WeightType(
298
+ units=packages.weight_unit,
299
+ value=item.weight,
300
+ ),
301
+ unitPrice=lib.identity(
302
+ fedex.FixedValueType(
303
+ amount=lib.to_money(item.value_amount),
304
+ currency=(item.value_currency or default_currency),
305
+ )
306
+ if item.value_amount
307
+ else None
308
+ ),
309
+ customsValue=fedex.FixedValueType(
310
+ amount=lib.identity(
311
+ lib.to_money(
312
+ item.value_amount or 1.0 * item.quantity
313
+ )
314
+ ),
315
+ currency=lib.identity(
316
+ item.value_currency or default_currency
317
+ ),
318
+ ),
319
+ quantity=item.quantity,
320
+ numberOfPieces=item.quantity,
321
+ quantityUnits="PCS",
322
+ harmonizedCode=item.hs_code,
323
+ name=item.title,
324
+ partNumber=item.sku,
325
+ )
326
+ for item in commodities
327
+ ],
328
+ )
329
+ if is_intl
330
+ else None
331
+ ),
332
+ groupShipment=None,
333
+ serviceTypeDetail=None,
334
+ smartPostInfoDetail=lib.identity(
335
+ fedex.SmartPostInfoDetailType(
336
+ ancillaryEndorsement=None,
337
+ hubId=hub_id,
338
+ indicia=(
339
+ lib.text(options.fedex_smart_post_allowed_indicia.state)
340
+ or "PARCEL_SELECT"
341
+ ),
342
+ specialServices=None,
343
+ )
344
+ if hub_id is not None
345
+ else None
346
+ ),
347
+ expressFreightDetail=None,
348
+ groundShipment=None,
349
+ ),
350
+ carrierCodes=options.fedex_carrier_codes.state,
351
+ )
352
+
353
+ return lib.Serializable(
354
+ request,
355
+ lib.to_dict,
356
+ dict(shipment_date=shipment_date),
357
+ )
@@ -0,0 +1,9 @@
1
+
2
+ from karrio.providers.fedex.shipment.create import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.fedex.shipment.cancel import (
7
+ parse_shipment_cancel_response,
8
+ shipment_cancel_request,
9
+ )