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,197 @@
1
+ import karrio.schemas.eshipper.rate_request as eshipper
2
+ import karrio.schemas.eshipper.rate_response as rating
3
+ import typing
4
+ import datetime
5
+ import karrio.lib as lib
6
+ import karrio.core.models as models
7
+ import karrio.providers.eshipper.error as error
8
+ import karrio.providers.eshipper.utils as provider_utils
9
+ import karrio.providers.eshipper.units as provider_units
10
+
11
+
12
+ def parse_rate_response(
13
+ _response: lib.Deserializable[dict],
14
+ settings: provider_utils.Settings,
15
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
16
+ response = _response.deserialize()
17
+
18
+ messages = error.parse_error_response(response, settings)
19
+ rates = [_extract_details(rate, settings) for rate in response.get("quotes", [])]
20
+
21
+ return rates, messages
22
+
23
+
24
+ def _extract_details(
25
+ data: dict,
26
+ settings: provider_utils.Settings,
27
+ ) -> models.RateDetails:
28
+ rate = lib.to_object(rating.QuoteType, data)
29
+ service = provider_units.ShippingService.find(
30
+ rate.serviceName,
31
+ service_id=rate.serviceId,
32
+ test_mode=settings.test_mode,
33
+ )
34
+ carrier_id = provider_units.ShippingService.carrier_id(
35
+ rate.carrierName,
36
+ test_mode=settings.test_mode,
37
+ service_search=service.name_or_key,
38
+ service_id=rate.serviceId,
39
+ )
40
+ rate_provider = provider_units.RateProvider.find(
41
+ rate.carrierName,
42
+ test_mode=settings.test_mode,
43
+ service_search=service.name_or_key,
44
+ service_id=rate.serviceId,
45
+ )
46
+
47
+ charges = [
48
+ ("baseCharge", rate.baseCharge),
49
+ ("fuelSurcharge", rate.fuelSurcharge),
50
+ ("carbonNeutralFees", rate.carbonNeutralFees),
51
+ ("processingFees", rate.processingFees),
52
+ ("totalChargedAmount", rate.totalChargedAmount),
53
+ *((_.name, _.amount) for _ in rate.surcharges),
54
+ *((_.name, _.amount) for _ in rate.taxes),
55
+ ]
56
+
57
+ return models.RateDetails(
58
+ carrier_id=settings.carrier_id,
59
+ carrier_name=settings.carrier_name,
60
+ service=service.name_or_key,
61
+ total_charge=lib.to_money(rate.totalCharge),
62
+ currency=rate.currency,
63
+ transit_days=lib.to_int(rate.transitDays),
64
+ extra_charges=[
65
+ models.ChargeDetails(
66
+ name=name,
67
+ currency=rate.currency,
68
+ amount=lib.to_money(amount),
69
+ )
70
+ for name, amount in charges
71
+ if amount
72
+ ],
73
+ meta=dict(
74
+ eshipper_carrier_id=carrier_id,
75
+ eshipper_service_id=rate.serviceId,
76
+ eshipper_carrier_name=rate.carrierName,
77
+ eshipper_service_name=rate.serviceName,
78
+ rate_provider=rate_provider.name_or_key,
79
+ service_name=service.name or rate.serviceName,
80
+ ),
81
+ )
82
+
83
+
84
+ def rate_request(
85
+ payload: models.RateRequest,
86
+ settings: provider_utils.Settings,
87
+ ) -> lib.Serializable:
88
+ shipper = lib.to_address(payload.shipper)
89
+ recipient = lib.to_address(payload.recipient)
90
+ packages = lib.to_packages(payload.parcels)
91
+ options = lib.to_shipping_options(
92
+ payload.options,
93
+ package_options=packages.options,
94
+ )
95
+ services = lib.to_services(payload.services, provider_units.ShippingService)
96
+ print(services, "<<<")
97
+ service_id = lib.identity(
98
+ options.eshipper_service_id.state
99
+ or provider_units.ShippingService.service_id(
100
+ services.first,
101
+ test_mode=settings.test_mode,
102
+ )
103
+ )
104
+
105
+ request = eshipper.RateRequestType(
106
+ scheduledShipDate=lib.fdatetime(
107
+ lib.to_next_business_datetime(
108
+ options.shipping_date.state or datetime.datetime.now(),
109
+ current_format="%Y-%m-%dT%H:%M",
110
+ ),
111
+ output_format="%Y-%m-%d %H:%M",
112
+ ),
113
+ raterequestfrom=eshipper.FromType(
114
+ attention=shipper.contact,
115
+ company=shipper.company_name,
116
+ address1=shipper.address_line1,
117
+ address2=shipper.address_line2,
118
+ city=shipper.city,
119
+ province=shipper.state_code,
120
+ country=shipper.country_code,
121
+ zip=shipper.postal_code,
122
+ email=shipper.email_address,
123
+ phone=shipper.phone_number,
124
+ instructions=None,
125
+ residential=shipper.is_residential,
126
+ tailgateRequired=None,
127
+ confirmDelivery=None,
128
+ notifyRecipient=None,
129
+ ),
130
+ to=eshipper.FromType(
131
+ attention=recipient.contact,
132
+ company=recipient.company_name,
133
+ address1=recipient.address_line1,
134
+ address2=recipient.address_line2,
135
+ city=recipient.city,
136
+ province=recipient.state_code,
137
+ country=recipient.country_code,
138
+ zip=recipient.postal_code,
139
+ email=recipient.email_address,
140
+ phone=recipient.phone_number,
141
+ instructions=None,
142
+ residential=recipient.is_residential,
143
+ tailgateRequired=None,
144
+ confirmDelivery=None,
145
+ notifyRecipient=None,
146
+ ),
147
+ packagingUnit="Metric" if packages.weight_unit.lower() == "kg" else "Imperial",
148
+ packages=eshipper.PackagesType(
149
+ type="Package",
150
+ packages=[
151
+ eshipper.PackageType(
152
+ height=lib.to_int(package.height.value),
153
+ length=lib.to_int(package.length.value),
154
+ width=lib.to_int(package.width.value),
155
+ weight=lib.to_int(package.weight.value),
156
+ dimensionUnit=package.dimension_unit.value,
157
+ weightUnit=package.weight_unit.value,
158
+ type=provider_units.PackagingType.map(package.packaging_type).value,
159
+ freightClass=None,
160
+ nmfcCode=None,
161
+ insuranceAmount=None,
162
+ codAmount=None,
163
+ description=package.description,
164
+ harmonizedCode=None,
165
+ skuCode=None,
166
+ )
167
+ for package in packages
168
+ ],
169
+ ),
170
+ reference1=payload.reference,
171
+ reference2=None,
172
+ reference3=None,
173
+ transactionId=None,
174
+ signatureRequired=options.eshipper_signature_required.state,
175
+ insuranceType=None,
176
+ dangerousGoodsType=None,
177
+ pickup=None,
178
+ customsInformation=None,
179
+ customsInBondFreight=None,
180
+ cod=None,
181
+ isSaturdayService=options.eshipper_is_saturday_service.state,
182
+ holdForPickupRequired=options.eshipper_hold_for_pickup_required.state,
183
+ specialEquipment=options.eshipper_special_equipment.state,
184
+ insideDelivery=options.eshipper_inside_delivery.state,
185
+ deliveryAppointment=options.eshipper_delivery_appointment.state,
186
+ insidePickup=options.eshipper_inside_pickup.state,
187
+ saturdayPickupRequired=options.eshipper_saturday_pickup_required.state,
188
+ stackable=options.eshipper_stackable.state,
189
+ serviceId=service_id,
190
+ thirdPartyBilling=None,
191
+ commodityType=None,
192
+ )
193
+
194
+ return lib.Serializable(
195
+ request,
196
+ lambda _: lib.to_dict(lib.to_json(_).replace("raterequestfrom", "from")),
197
+ )
@@ -0,0 +1,9 @@
1
+
2
+ from karrio.providers.eshipper.shipment.create import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.eshipper.shipment.cancel import (
7
+ parse_shipment_cancel_response,
8
+ shipment_cancel_request,
9
+ )
@@ -0,0 +1,45 @@
1
+ import karrio.schemas.eshipper.cancel_request as eshipper
2
+ import typing
3
+ import karrio.lib as lib
4
+ import karrio.core.models as models
5
+ import karrio.providers.eshipper.error as error
6
+ import karrio.providers.eshipper.utils as provider_utils
7
+ import karrio.providers.eshipper.units as provider_units
8
+
9
+
10
+ def parse_shipment_cancel_response(
11
+ _response: lib.Deserializable[dict],
12
+ settings: provider_utils.Settings,
13
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
14
+ response = _response.deserialize()
15
+ messages = error.parse_error_response(response, settings)
16
+ success = not any(messages)
17
+
18
+ confirmation = (
19
+ models.ConfirmationDetails(
20
+ carrier_id=settings.carrier_id,
21
+ carrier_name=settings.carrier_name,
22
+ operation="Cancel Shipment",
23
+ success=success,
24
+ )
25
+ if success
26
+ else None
27
+ )
28
+
29
+ return confirmation, messages
30
+
31
+
32
+ def shipment_cancel_request(
33
+ payload: models.ShipmentCancelRequest,
34
+ settings: provider_utils.Settings,
35
+ ) -> lib.Serializable:
36
+
37
+ request = eshipper.CancelRequestType(
38
+ order=eshipper.OrderType(
39
+ trackingId=None,
40
+ orderId=payload.options.get("orderId"),
41
+ message=None,
42
+ )
43
+ )
44
+
45
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,295 @@
1
+ import karrio.schemas.eshipper.shipping_request as eshipper
2
+ import karrio.schemas.eshipper.shipping_response as shipping
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.eshipper.error as error
9
+ import karrio.providers.eshipper.utils as provider_utils
10
+ import karrio.providers.eshipper.units as provider_units
11
+
12
+
13
+ def parse_shipment_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
+
19
+ messages = error.parse_error_response(response, settings)
20
+ shipment = _extract_details(response, settings) if "order" in response else None
21
+
22
+ return shipment, messages
23
+
24
+
25
+ def _extract_details(
26
+ data: dict,
27
+ settings: provider_utils.Settings,
28
+ ) -> models.ShipmentDetails:
29
+ shipment = lib.to_object(shipping.ShippingResponseType, data)
30
+ label_type = next((_.type for _ in shipment.labelData.label), "PDF").upper()
31
+ label = lib.bundle_base64([_.data for _ in shipment.labelData.label], label_type)
32
+ invoice = lib.failsafe(lambda: shipment.customsInvoice.data)
33
+ trackingNumbers = [_.trackingNumber for _ in shipment.packages]
34
+ service = provider_units.ShippingService.find(
35
+ shipment.carrier.serviceName,
36
+ test_mode=settings.test_mode,
37
+ )
38
+ rate_provider = provider_units.RateProvider.find(
39
+ shipment.carrier.carrierName,
40
+ test_mode=settings.test_mode,
41
+ )
42
+
43
+ return models.ShipmentDetails(
44
+ carrier_id=settings.carrier_id,
45
+ carrier_name=settings.carrier_name,
46
+ tracking_number=shipment.trackingNumber,
47
+ shipment_identifier=shipment.order.id,
48
+ label_type=label_type,
49
+ docs=models.Documents(label=label, invoice=invoice),
50
+ meta=lib.to_dict(
51
+ dict(
52
+ service_name=service.name_or_key,
53
+ rate_provider=rate_provider.name_or_key,
54
+ carrier_tracking_link=shipment.trackingUrl,
55
+ tracking_numbers=trackingNumbers,
56
+ orderId=shipment.order.id,
57
+ transactionId=shipment.transactionId,
58
+ billingReference=shipment.billingReference,
59
+ eshipper_carrier_name=shipment.carrier.carrierName,
60
+ )
61
+ ),
62
+ )
63
+
64
+
65
+ def shipment_request(
66
+ payload: models.ShipmentRequest,
67
+ settings: provider_utils.Settings,
68
+ ) -> lib.Serializable:
69
+ shipper = lib.to_address(payload.shipper)
70
+ recipient = lib.to_address(payload.recipient)
71
+ is_intl = shipper.country_code != recipient.country_code
72
+
73
+ payment = payload.payment or models.Payment()
74
+ payor = lib.to_address(
75
+ {
76
+ "sender": payload.shipper,
77
+ "recipient": payload.recipient,
78
+ "thid_party": payload.billing_address,
79
+ }.get(payment.paid_by)
80
+ )
81
+ packages = lib.to_packages(
82
+ payload.parcels,
83
+ package_option_type=provider_units.ShippingOption,
84
+ required=["weight", "height", "width", "length"],
85
+ )
86
+ options = lib.to_shipping_options(
87
+ payload.options,
88
+ package_options=packages.options,
89
+ initializer=provider_units.shipping_options_initializer,
90
+ )
91
+ service = provider_units.ShippingService.map(payload.service)
92
+ service_id = lib.identity(
93
+ options.eshipper_service_id.state
94
+ or provider_units.ShippingService.service_id(
95
+ service.name_or_key,
96
+ test_mode=settings.test_mode,
97
+ )
98
+ )
99
+ carrier_id = lib.identity(
100
+ options.eshipper_carrier_id.state
101
+ or provider_units.ShippingService.carrier_id(
102
+ service.name_or_key,
103
+ service_id=service_id,
104
+ test_mode=settings.test_mode,
105
+ service_search=service.name_or_key,
106
+ )
107
+ )
108
+
109
+ customs = lib.to_customs_info(
110
+ payload.customs,
111
+ shipper=payload.shipper,
112
+ recipient=payload.recipient,
113
+ weight_unit=packages.weight_unit,
114
+ default_to=lib.identity(
115
+ models.Customs(
116
+ commodities=(
117
+ packages.items
118
+ if any(packages.items)
119
+ else [
120
+ models.Commodity(
121
+ quantity=1,
122
+ sku=f"000{index}",
123
+ weight=pkg.weight.value,
124
+ weight_unit=pkg.weight_unit.value,
125
+ description=pkg.parcel.content,
126
+ )
127
+ for index, pkg in enumerate(packages, start=1)
128
+ ]
129
+ )
130
+ )
131
+ if is_intl
132
+ else None
133
+ ),
134
+ )
135
+
136
+ request = eshipper.ShippingRequestType(
137
+ scheduledShipDate=lib.fdatetime(
138
+ lib.to_next_business_datetime(
139
+ options.shipping_date.state or datetime.datetime.now(),
140
+ current_format="%Y-%m-%dT%H:%M",
141
+ ),
142
+ output_format="%Y-%m-%d %H:%M",
143
+ ),
144
+ shippingrequestfrom=eshipper.FromType(
145
+ attention=shipper.contact,
146
+ company=shipper.company_name,
147
+ address1=shipper.address_line1,
148
+ address2=shipper.address_line2,
149
+ city=shipper.city,
150
+ province=shipper.state_code,
151
+ country=shipper.country_code,
152
+ zip=shipper.postal_code,
153
+ email=shipper.email_address,
154
+ phone=shipper.phone_number,
155
+ instructions=None,
156
+ residential=shipper.is_residential,
157
+ tailgateRequired=None,
158
+ confirmDelivery=None,
159
+ notifyRecipient=None,
160
+ ),
161
+ to=eshipper.FromType(
162
+ attention=recipient.contact,
163
+ company=recipient.company_name,
164
+ address1=recipient.address_line1,
165
+ address2=recipient.address_line2,
166
+ city=recipient.city,
167
+ province=recipient.state_code,
168
+ country=recipient.country_code,
169
+ zip=recipient.postal_code,
170
+ email=recipient.email_address,
171
+ phone=recipient.phone_number,
172
+ instructions=None,
173
+ residential=recipient.is_residential,
174
+ tailgateRequired=None,
175
+ confirmDelivery=None,
176
+ notifyRecipient=None,
177
+ ),
178
+ packagingUnit="Metric" if packages.weight_unit.lower() == "kg" else "Imperial",
179
+ packages=eshipper.PackagesType(
180
+ type="Package",
181
+ packages=[
182
+ eshipper.PackageType(
183
+ height=lib.to_int(package.height.value),
184
+ length=lib.to_int(package.length.value),
185
+ width=lib.to_int(package.width.value),
186
+ weight=lib.to_int(package.weight.value),
187
+ dimensionUnit=package.dimension_unit.value,
188
+ weightUnit=package.weight_unit.value,
189
+ type=provider_units.PackagingType.map(package.packaging_type).value,
190
+ freightClass=package.parcel.freight_class,
191
+ nmfcCode=None,
192
+ insuranceAmount=package.options.insurance.state,
193
+ codAmount=None,
194
+ description=package.description,
195
+ harmonizedCode=None,
196
+ skuCode=None,
197
+ )
198
+ for package in packages
199
+ ],
200
+ ),
201
+ reference1=payload.reference,
202
+ reference2=None,
203
+ reference3=None,
204
+ transactionId=None,
205
+ signatureRequired=options.eshipper_signature_required.state,
206
+ insuranceType=None,
207
+ dangerousGoodsType=None,
208
+ pickup=None,
209
+ customsInformation=lib.identity(
210
+ eshipper.CustomsInformationType(
211
+ contact=eshipper.ContactType(
212
+ contactCompany=shipper.company_name,
213
+ contactName=shipper.contact,
214
+ brokerName=None,
215
+ brokerTaxId=None,
216
+ recipientTaxId=None,
217
+ ),
218
+ items=eshipper.ItemsType(
219
+ currency=(customs.duty.currency or options.currency.state),
220
+ items=[
221
+ eshipper.ItemType(
222
+ hsnCode=item.hs_code,
223
+ description=item.title or item.description,
224
+ originCountry=item.origin_country,
225
+ quantity=item.quantity,
226
+ unitPrice=lib.to_money(item.value_amount),
227
+ skuCode=item.sku,
228
+ )
229
+ for item in customs.commodities
230
+ ],
231
+ ),
232
+ dutiesTaxes=eshipper.DutiesTaxesType(
233
+ dutiable=not packages.is_document,
234
+ billTo=customs.duty_billing_address.contact,
235
+ accountNumber=customs.duty.account_number,
236
+ sedNumber=customs.options.sed_number.state,
237
+ ),
238
+ billingAddress=eshipper.BillingAddressType(
239
+ company=customs.duty_billing_address.company_name,
240
+ attention=customs.duty_billing_address.contact,
241
+ address1=customs.duty_billing_address.address_line1,
242
+ address2=customs.duty_billing_address.address_line2,
243
+ city=customs.duty_billing_address.city,
244
+ province=customs.duty_billing_address.state_code,
245
+ country=customs.duty_billing_address.country_code,
246
+ zip=customs.duty_billing_address.postal_code,
247
+ email=customs.duty_billing_address.email_address,
248
+ phone=customs.duty_billing_address.phone_number,
249
+ ),
250
+ remarks=None,
251
+ )
252
+ if payload.customs
253
+ else None
254
+ ),
255
+ cod=lib.identity(
256
+ eshipper.CodType(
257
+ codAddress=eshipper.CodAddressType(
258
+ company=recipient.company_name,
259
+ name=recipient.contact,
260
+ city=recipient.city,
261
+ province=recipient.state_code,
262
+ country=recipient.country_code,
263
+ zip=recipient.postal_code,
264
+ ),
265
+ paymentType="Cash",
266
+ )
267
+ if options.cash_on_delivery.state
268
+ else None
269
+ ),
270
+ isSaturdayService=options.eshipper_is_saturday_service.state,
271
+ holdForPickupRequired=options.eshipper_hold_for_pickup_required.state,
272
+ specialEquipment=options.eshipper_special_equipment.state,
273
+ insideDelivery=options.eshipper_inside_delivery.state,
274
+ deliveryAppointment=options.eshipper_delivery_appointment.state,
275
+ insidePickup=options.eshipper_inside_pickup.state,
276
+ saturdayPickupRequired=options.eshipper_saturday_pickup_required.state,
277
+ stackable=options.eshipper_stackable.state,
278
+ serviceId=service_id,
279
+ thirdPartyBilling=lib.identity(
280
+ eshipper.ThirdPartyBillingType(
281
+ carrier=carrier_id,
282
+ country=payor.country_code,
283
+ billToAccountNumber=payment.account_number,
284
+ billToPostalCode=payor.postal_code,
285
+ )
286
+ if payment.paid_by == "third_party"
287
+ else None
288
+ ),
289
+ commodityType=customs.content_type,
290
+ )
291
+
292
+ return lib.Serializable(
293
+ request,
294
+ lambda _: lib.to_dict(lib.to_json(_).replace("shippingrequestfrom", "from")),
295
+ )
@@ -0,0 +1,72 @@
1
+ import karrio.schemas.eshipper.tracking_request as eshipper
2
+ import karrio.schemas.eshipper.tracking_response as tracking
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.core.units as units
6
+ import karrio.core.models as models
7
+ import karrio.providers.eshipper.error as error
8
+ import karrio.providers.eshipper.utils as provider_utils
9
+ import karrio.providers.eshipper.units as provider_units
10
+
11
+
12
+ def parse_tracking_response(
13
+ _response: lib.Deserializable[typing.Union[dict, typing.List[dict]]],
14
+ settings: provider_utils.Settings,
15
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
16
+ response = _response.deserialize()
17
+ responses = response if isinstance(response, list) else [response]
18
+
19
+ messages: typing.List[models.Message] = sum(
20
+ [
21
+ error.parse_error_response(
22
+ _, settings, tracking_number=_.get("trackingNumber")
23
+ )
24
+ for _ in responses
25
+ ],
26
+ start=[],
27
+ )
28
+ tracking_details = [
29
+ _extract_details(_, settings)
30
+ for _ in responses
31
+ if _.get("trackingNumber") is not None
32
+ ]
33
+
34
+ return tracking_details, messages
35
+
36
+
37
+ def _extract_details(
38
+ data: dict,
39
+ settings: provider_utils.Settings,
40
+ ) -> models.TrackingDetails:
41
+ details = lib.to_object(tracking.TrackingResponseElementType, data)
42
+
43
+ return models.TrackingDetails(
44
+ carrier_id=settings.carrier_id,
45
+ carrier_name=settings.carrier_name,
46
+ tracking_number=details.trackingNumber,
47
+ events=[
48
+ models.TrackingEvent(
49
+ code=event.originalEvent.name,
50
+ location=event.location,
51
+ description=event.description,
52
+ date=lib.fdate(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
53
+ time=lib.flocaltime(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
54
+ )
55
+ for event in details.event
56
+ ],
57
+ estimated_delivery=lib.fdate(details.expectedDeliveryDate, "%Y-%m-%d %H:%M:%S"),
58
+ delivered=details.shipmentStatus.delivered,
59
+ )
60
+
61
+
62
+ def tracking_request(
63
+ payload: models.TrackingRequest,
64
+ settings: provider_utils.Settings,
65
+ ) -> lib.Serializable:
66
+ request = eshipper.TrackingRequestType(
67
+ trackingNumbers=payload.tracking_numbers,
68
+ includePublished=True,
69
+ pageable=lib.to_json({"page": 0, "size": 25, "sort": []}),
70
+ )
71
+
72
+ return lib.Serializable(request, lib.to_dict)