karrio-teleship 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 (49) hide show
  1. karrio/mappers/teleship/__init__.py +4 -0
  2. karrio/mappers/teleship/hooks.py +27 -0
  3. karrio/mappers/teleship/mapper.py +114 -0
  4. karrio/mappers/teleship/proxy.py +239 -0
  5. karrio/mappers/teleship/settings.py +21 -0
  6. karrio/plugins/teleship/__init__.py +32 -0
  7. karrio/providers/teleship/__init__.py +41 -0
  8. karrio/providers/teleship/duties.py +115 -0
  9. karrio/providers/teleship/error.py +44 -0
  10. karrio/providers/teleship/hooks/__init__.py +5 -0
  11. karrio/providers/teleship/hooks/event.py +163 -0
  12. karrio/providers/teleship/hooks/oauth.py +103 -0
  13. karrio/providers/teleship/manifest.py +68 -0
  14. karrio/providers/teleship/pickup/__init__.py +8 -0
  15. karrio/providers/teleship/pickup/cancel.py +43 -0
  16. karrio/providers/teleship/pickup/schedule.py +66 -0
  17. karrio/providers/teleship/rate.py +287 -0
  18. karrio/providers/teleship/shipment/__init__.py +9 -0
  19. karrio/providers/teleship/shipment/cancel.py +48 -0
  20. karrio/providers/teleship/shipment/create.py +322 -0
  21. karrio/providers/teleship/tracking.py +100 -0
  22. karrio/providers/teleship/units.py +154 -0
  23. karrio/providers/teleship/utils.py +57 -0
  24. karrio/providers/teleship/webhook/__init__.py +8 -0
  25. karrio/providers/teleship/webhook/deregister.py +47 -0
  26. karrio/providers/teleship/webhook/register.py +48 -0
  27. karrio/schemas/teleship/__init__.py +0 -0
  28. karrio/schemas/teleship/duties_taxes_request.py +82 -0
  29. karrio/schemas/teleship/duties_taxes_response.py +28 -0
  30. karrio/schemas/teleship/error_response.py +17 -0
  31. karrio/schemas/teleship/manifest_request.py +39 -0
  32. karrio/schemas/teleship/manifest_response.py +31 -0
  33. karrio/schemas/teleship/pickup_request.py +31 -0
  34. karrio/schemas/teleship/pickup_response.py +48 -0
  35. karrio/schemas/teleship/rate_request.py +128 -0
  36. karrio/schemas/teleship/rate_response.py +66 -0
  37. karrio/schemas/teleship/shipment_cancel_request.py +8 -0
  38. karrio/schemas/teleship/shipment_cancel_response.py +17 -0
  39. karrio/schemas/teleship/shipment_request.py +137 -0
  40. karrio/schemas/teleship/shipment_response.py +247 -0
  41. karrio/schemas/teleship/tracking_request.py +8 -0
  42. karrio/schemas/teleship/tracking_response.py +52 -0
  43. karrio/schemas/teleship/webhook_request.py +18 -0
  44. karrio/schemas/teleship/webhook_response.py +20 -0
  45. karrio_teleship-2025.5.dist-info/METADATA +44 -0
  46. karrio_teleship-2025.5.dist-info/RECORD +49 -0
  47. karrio_teleship-2025.5.dist-info/WHEEL +5 -0
  48. karrio_teleship-2025.5.dist-info/entry_points.txt +2 -0
  49. karrio_teleship-2025.5.dist-info/top_level.txt +4 -0
@@ -0,0 +1,287 @@
1
+ """Karrio Teleship rate API implementation."""
2
+
3
+ import karrio.schemas.teleship.rate_request as teleship_req
4
+ import karrio.schemas.teleship.rate_response as teleship_res
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.units as units
9
+ import karrio.core.models as models
10
+ import karrio.providers.teleship.error as error
11
+ import karrio.providers.teleship.utils as provider_utils
12
+ import karrio.providers.teleship.units as provider_units
13
+
14
+
15
+ def parse_rate_response(
16
+ _response: lib.Deserializable[typing.List[dict]],
17
+ settings: provider_utils.Settings,
18
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
19
+ responses = _response.deserialize()
20
+
21
+ messages: typing.List[models.Message] = sum(
22
+ [error.parse_error_response(response, settings) for response in responses],
23
+ start=[],
24
+ )
25
+ package_rates: typing.List[typing.Tuple[str, typing.List[models.RateDetails]]] = [
26
+ (
27
+ f"{_}",
28
+ [_extract_details(rate, settings) for rate in response.get("rates") or []],
29
+ )
30
+ for _, response in enumerate(responses, start=1)
31
+ ]
32
+ rates = lib.to_multi_piece_rates(package_rates)
33
+
34
+ return rates, messages
35
+
36
+
37
+ def _extract_details(
38
+ data: dict,
39
+ settings: provider_utils.Settings,
40
+ ) -> models.RateDetails:
41
+ """Extract rate details from carrier response data"""
42
+ rate = lib.to_object(teleship_res.RateType, data)
43
+ service = provider_units.ShippingService.map(
44
+ rate.service.code if rate.service else ""
45
+ )
46
+
47
+ return models.RateDetails(
48
+ carrier_id=settings.carrier_id,
49
+ carrier_name=settings.carrier_name,
50
+ service=service.name_or_key,
51
+ total_charge=lib.to_money(rate.price),
52
+ currency=rate.currency,
53
+ transit_days=rate.transit,
54
+ extra_charges=[
55
+ models.ChargeDetails(
56
+ name=charge.name or "",
57
+ amount=lib.to_money(charge.amount),
58
+ currency=charge.currency,
59
+ )
60
+ for charge in (rate.charges or [])
61
+ ],
62
+ meta=dict(
63
+ service_name=service.name_or_key,
64
+ estimated_delivery=lib.fdate(
65
+ rate.estimatedDelivery,
66
+ try_formats=["%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%SZ"],
67
+ ),
68
+ ),
69
+ )
70
+
71
+
72
+ def rate_request(
73
+ payload: models.RateRequest,
74
+ settings: provider_utils.Settings,
75
+ ) -> lib.Serializable:
76
+ """Create a rate request for the carrier API"""
77
+ # Convert karrio models using functional lib utilities
78
+ shipper = lib.to_address(payload.shipper)
79
+ recipient = lib.to_address(payload.recipient)
80
+ billing_address = lib.to_address(payload.billing_address)
81
+ return_address = lib.to_address(payload.return_address)
82
+ is_intl = payload.recipient.country_code != payload.shipper.country_code
83
+
84
+ options = lib.to_shipping_options(
85
+ payload.options,
86
+ initializer=provider_units.shipping_options_initializer,
87
+ )
88
+ packages = lib.to_packages(
89
+ payload.parcels,
90
+ options=options,
91
+ shipping_options_initializer=provider_units.shipping_options_initializer,
92
+ )
93
+ customs = lib.to_customs_info(
94
+ payload.customs,
95
+ shipper=payload.shipper,
96
+ recipient=payload.recipient,
97
+ weight_unit=units.WeightUnit.KG.name,
98
+ )
99
+
100
+ # Build request using typed schema classes
101
+ request = [
102
+ teleship_req.RateRequestType(
103
+ customerReference=payload.reference,
104
+ description=package.parcel.description,
105
+ shipDate=options.shipping_date.state,
106
+ orderTrackingReference=options.teleship_order_tracking_reference.state,
107
+ commercialInvoiceReference=customs.invoice,
108
+ packageType=provider_units.PackagingType.map(
109
+ package.packaging_type or "your_packaging"
110
+ ).value_or_key,
111
+ shipTo=teleship_req.BillToType(
112
+ name=recipient.contact or "N/A",
113
+ company=recipient.company_name,
114
+ email=recipient.email,
115
+ phone=recipient.phone_number,
116
+ address=teleship_req.AddressType(
117
+ line1=recipient.address_line1,
118
+ line2=recipient.address_line2,
119
+ city=recipient.city,
120
+ state=recipient.state_code,
121
+ postcode=recipient.postal_code,
122
+ country=recipient.country_code,
123
+ ),
124
+ ),
125
+ shipFrom=teleship_req.BillToType(
126
+ name=shipper.contact or "N/A",
127
+ company=shipper.company_name,
128
+ email=shipper.email,
129
+ phone=shipper.phone_number,
130
+ address=teleship_req.AddressType(
131
+ line1=shipper.address_line1,
132
+ line2=shipper.address_line2,
133
+ city=shipper.city,
134
+ state=shipper.state_code,
135
+ postcode=shipper.postal_code,
136
+ country=shipper.country_code,
137
+ ),
138
+ ),
139
+ returnTo=lib.identity(
140
+ teleship_req.BillToType(
141
+ name=return_address.contact or "N/A",
142
+ company=return_address.company_name,
143
+ email=return_address.email,
144
+ phone=return_address.phone_number,
145
+ address=teleship_req.AddressType(
146
+ line1=return_address.address_line1,
147
+ line2=return_address.address_line2,
148
+ city=return_address.city,
149
+ state=return_address.state_code,
150
+ postcode=return_address.postal_code,
151
+ country=return_address.country_code,
152
+ ),
153
+ )
154
+ if payload.return_address
155
+ else None
156
+ ),
157
+ billTo=lib.identity(
158
+ teleship_req.BillToType(
159
+ name=billing_address.contact or "N/A",
160
+ company=billing_address.company_name,
161
+ email=billing_address.email,
162
+ phone=billing_address.phone_number,
163
+ address=teleship_req.AddressType(
164
+ line1=billing_address.address_line1,
165
+ line2=billing_address.address_line2,
166
+ city=billing_address.city,
167
+ state=billing_address.state_code,
168
+ postcode=billing_address.postal_code,
169
+ country=billing_address.country_code,
170
+ ),
171
+ stateTaxId=billing_address.state_tax_id,
172
+ countryTaxId=billing_address.federal_tax_id,
173
+ )
174
+ if payload.billing_address
175
+ else None
176
+ ),
177
+ weight=teleship_req.WeightType(
178
+ value=package.weight.value,
179
+ unit=package.weight.unit.lower(),
180
+ ),
181
+ dimensions=lib.identity(
182
+ teleship_req.DimensionsType(
183
+ unit=package.dimension_unit.lower(),
184
+ length=package.length.value,
185
+ width=package.width.value,
186
+ height=package.height.value,
187
+ )
188
+ if all(
189
+ [
190
+ package.length.value,
191
+ package.width.value,
192
+ package.height.value,
193
+ ]
194
+ )
195
+ else None
196
+ ),
197
+ additionalServices=lib.identity(
198
+ teleship_req.AdditionalServicesType(
199
+ signatureRequired=options.teleship_signature_required.state,
200
+ deliveryWarranty=options.teleship_delivery_warranty.state,
201
+ deliveryPUDO=options.teleship_delivery_pudo.state,
202
+ lowCarbon=options.teleship_low_carbon.state,
203
+ dutyTaxCalculation=options.teleship_duty_tax_calculation.state,
204
+ )
205
+ if any(
206
+ [
207
+ options.teleship_signature_required.state,
208
+ options.teleship_delivery_warranty.state,
209
+ options.teleship_delivery_pudo.state,
210
+ options.teleship_low_carbon.state,
211
+ options.teleship_duty_tax_calculation.state,
212
+ ]
213
+ )
214
+ else None
215
+ ),
216
+ commodities=[
217
+ teleship_req.CommodityType(
218
+ sku=commodity.sku,
219
+ hsCode=commodity.hs_code,
220
+ title=lib.text(commodity.title or commodity.description, max=200),
221
+ description=lib.text(
222
+ commodity.description if commodity.title else None, max=200
223
+ ),
224
+ category=commodity.category,
225
+ value=teleship_req.ValueType(
226
+ amount=commodity.value_amount,
227
+ currency=commodity.value_currency,
228
+ ),
229
+ quantity=commodity.quantity,
230
+ unitWeight=teleship_req.WeightType(
231
+ value=commodity.weight,
232
+ unit=commodity.weight_unit.lower(),
233
+ ),
234
+ countryOfOrigin=commodity.origin_country,
235
+ imageUrl=commodity.image_url,
236
+ productUrl=commodity.product_url,
237
+ compliance=None,
238
+ )
239
+ for commodity in (
240
+ package.items if any(package.items) else customs.commodities or []
241
+ )
242
+ ],
243
+ customs=lib.identity(
244
+ teleship_req.CustomsType(
245
+ EORI=customs.options.eori_number.state,
246
+ IOSS=customs.options.ioss.state,
247
+ VAT=customs.options.vat.state,
248
+ EIN=customs.options.ein.state,
249
+ VOECNUMBER=customs.options.voec_number.state,
250
+ importerGST=customs.options.importer_gst.state,
251
+ exporterGST=customs.options.exporter_gst.state,
252
+ consigneeGST=customs.options.consignee_gst.state,
253
+ contentType=provider_units.CustomsContentType.map(
254
+ customs.content_type or "other"
255
+ ).value,
256
+ invoiceDate=customs.invoice_date,
257
+ invoiceNumber=customs.invoice,
258
+ GPSRContactInfo=options.teleship_gpsr_contact_info.state,
259
+ importerOfRecord=lib.identity(
260
+ teleship_req.BillToType(
261
+ name=customs.duty_billing_address.contact or "N/A",
262
+ company=customs.duty_billing_address.company_name,
263
+ email=customs.duty_billing_address.email,
264
+ phone=customs.duty_billing_address.phone_number,
265
+ address=teleship_req.AddressType(
266
+ line1=customs.duty_billing_address.address_line1,
267
+ line2=customs.duty_billing_address.address_line2,
268
+ city=customs.duty_billing_address.city,
269
+ state=customs.duty_billing_address.state_code,
270
+ country=customs.duty_billing_address.country_code,
271
+ postcode=customs.duty_billing_address.postal_code,
272
+ ),
273
+ stateTaxId=customs.duty_billing_address.state_tax_id,
274
+ countryTaxId=customs.duty_billing_address.federal_tax_id,
275
+ )
276
+ if payload.customs.duty_billing_address
277
+ else None
278
+ ),
279
+ )
280
+ if payload.customs and is_intl
281
+ else None
282
+ ),
283
+ )
284
+ for package in packages
285
+ ]
286
+
287
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,9 @@
1
+
2
+ from karrio.providers.teleship.shipment.create import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.teleship.shipment.cancel import (
7
+ parse_shipment_cancel_response,
8
+ shipment_cancel_request,
9
+ )
@@ -0,0 +1,48 @@
1
+ """Karrio Teleship shipment cancellation API implementation."""
2
+
3
+ import typing
4
+ import karrio.schemas.teleship.shipment_cancel_request as teleship_req
5
+ import karrio.schemas.teleship.shipment_cancel_response as teleship_res
6
+ import karrio.lib as lib
7
+ import karrio.core.models as models
8
+ import karrio.providers.teleship.error as error
9
+ import karrio.providers.teleship.utils as provider_utils
10
+
11
+
12
+ def parse_shipment_cancel_response(
13
+ _response: lib.Deserializable[dict],
14
+ settings: provider_utils.Settings,
15
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
16
+ """Parse shipment cancellation response from carrier API"""
17
+ response = _response.deserialize()
18
+ messages = error.parse_error_response(response, settings)
19
+ details = lib.to_object(teleship_res.ShipmentCancelResponseType, response)
20
+
21
+ # Check if cancellation was successful based on status
22
+ success = details.status in ["cancelled", "voided"] if details else False
23
+
24
+ confirmation = lib.identity(
25
+ models.ConfirmationDetails(
26
+ carrier_id=settings.carrier_id,
27
+ carrier_name=settings.carrier_name,
28
+ operation="Cancel Shipment",
29
+ success=success,
30
+ )
31
+ if details
32
+ else None
33
+ )
34
+
35
+ return confirmation, messages
36
+
37
+
38
+ def shipment_cancel_request(
39
+ payload: models.ShipmentCancelRequest,
40
+ settings: provider_utils.Settings,
41
+ ) -> lib.Serializable:
42
+ """Create a shipment cancellation request for the carrier API"""
43
+
44
+ request = teleship_req.ShipmentCancelRequestType(
45
+ shipmentId=payload.shipment_identifier,
46
+ )
47
+
48
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,322 @@
1
+ """Karrio Teleship shipment API implementation."""
2
+
3
+ import karrio.schemas.teleship.shipment_request as teleship_req
4
+ import karrio.schemas.teleship.shipment_response as teleship_res
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.units as units
9
+ import karrio.core.models as models
10
+ import karrio.providers.teleship.error as error
11
+ import karrio.providers.teleship.utils as provider_utils
12
+ import karrio.providers.teleship.units as provider_units
13
+
14
+
15
+ def parse_shipment_response(
16
+ _responses: lib.Deserializable[typing.List[dict]],
17
+ settings: provider_utils.Settings,
18
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
19
+ responses = _responses.deserialize()
20
+
21
+ shipment = lib.to_multi_piece_shipment(
22
+ [
23
+ (
24
+ f"{_}",
25
+ (
26
+ _extract_details(response.get("shipment"), settings)
27
+ if response.get("shipment")
28
+ else None
29
+ ),
30
+ )
31
+ for _, response in enumerate(responses, start=1)
32
+ ]
33
+ )
34
+ messages: typing.List[models.Message] = sum(
35
+ [error.parse_error_response(response, settings) for response in responses],
36
+ start=[],
37
+ )
38
+
39
+ return shipment, messages
40
+
41
+
42
+ def _extract_details(
43
+ data: dict,
44
+ settings: provider_utils.Settings,
45
+ ) -> models.ShipmentDetails:
46
+ """Extract shipment details from carrier response data"""
47
+ shipment = lib.to_object(teleship_res.ShipmentType, data)
48
+ service = provider_units.ShippingService.map(shipment.rate.service.code)
49
+
50
+ # Extract label document from documents array
51
+ label = lib.identity(
52
+ next(
53
+ (doc for doc in (shipment.documents or []) if doc.type == "LABEL"),
54
+ None,
55
+ )
56
+ )
57
+ invoice = lib.identity(
58
+ next(
59
+ (doc for doc in (shipment.documents or []) if doc.type == "INVOICE"),
60
+ None,
61
+ )
62
+ )
63
+
64
+ return models.ShipmentDetails(
65
+ carrier_id=settings.carrier_id,
66
+ carrier_name=settings.carrier_name,
67
+ tracking_number=shipment.trackingNumber,
68
+ shipment_identifier=shipment.shipmentId,
69
+ label_type=label.format or "PDF",
70
+ docs=models.Documents(
71
+ label=getattr(label, "base64String", None),
72
+ invoice=getattr(invoice, "base64String", None),
73
+ ),
74
+ selected_rate=models.RateDetails(
75
+ carrier_id=settings.carrier_id,
76
+ carrier_name=settings.carrier_name,
77
+ service=service.name_or_key,
78
+ total_charge=lib.to_money(shipment.rate.price),
79
+ currency=shipment.rate.currency,
80
+ transit_days=shipment.rate.transit,
81
+ estimated_delivery=lib.fdate(
82
+ shipment.rate.estimatedDelivery,
83
+ try_formats=["%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%SZ"],
84
+ ),
85
+ extra_charges=[
86
+ models.ChargeDetails(
87
+ name=charge.name,
88
+ amount=lib.to_money(charge.amount),
89
+ currency=charge.currency,
90
+ )
91
+ for charge in shipment.rate.charges
92
+ ],
93
+ meta=dict(
94
+ service_name=service.name_or_key,
95
+ ),
96
+ ),
97
+ meta=dict(
98
+ customer_reference=shipment.customerReference,
99
+ service_name=service.name_or_key,
100
+ ship_date=shipment.shipDate,
101
+ ),
102
+ )
103
+
104
+
105
+ def shipment_request(
106
+ payload: models.ShipmentRequest,
107
+ settings: provider_utils.Settings,
108
+ ) -> lib.Serializable:
109
+ """Create a shipment request for the carrier API"""
110
+ # Convert karrio models using functional lib utilities
111
+ shipper = lib.to_address(payload.shipper)
112
+ recipient = lib.to_address(payload.recipient)
113
+ billing_address = lib.to_address(payload.billing_address)
114
+ return_address = lib.to_address(payload.return_address)
115
+ is_intl = payload.recipient.country_code != payload.shipper.country_code
116
+
117
+ service = provider_units.ShippingService.map(payload.service).value_or_key
118
+ options = lib.to_shipping_options(
119
+ payload.options,
120
+ initializer=provider_units.shipping_options_initializer,
121
+ )
122
+ packages = lib.to_packages(
123
+ payload.parcels,
124
+ options=options,
125
+ shipping_options_initializer=provider_units.shipping_options_initializer,
126
+ )
127
+ customs = lib.to_customs_info(
128
+ payload.customs,
129
+ shipper=payload.shipper,
130
+ recipient=payload.recipient,
131
+ weight_unit=units.WeightUnit.KG.name,
132
+ )
133
+
134
+ # Build request using typed schema classes
135
+ request = [
136
+ teleship_req.ShipmentRequestType(
137
+ serviceCode=service,
138
+ customerReference=payload.reference,
139
+ description=package.parcel.description,
140
+ shipDate=options.shipping_date.state,
141
+ orderTrackingReference=options.teleship_order_tracking_reference.state,
142
+ commercialInvoiceReference=customs.invoice,
143
+ packageType=provider_units.PackagingType.map(
144
+ package.packaging_type or "your_packaging"
145
+ ).value_or_key,
146
+ shipTo=teleship_req.BillToType(
147
+ name=recipient.contact or "N/A",
148
+ company=recipient.company_name,
149
+ email=recipient.email,
150
+ phone=recipient.phone_number,
151
+ address=teleship_req.AddressType(
152
+ line1=recipient.address_line1,
153
+ line2=recipient.address_line2,
154
+ city=recipient.city,
155
+ state=recipient.state_code,
156
+ postcode=recipient.postal_code,
157
+ country=recipient.country_code,
158
+ ),
159
+ ),
160
+ shipFrom=teleship_req.BillToType(
161
+ name=shipper.contact or "N/A",
162
+ company=shipper.company_name,
163
+ email=shipper.email,
164
+ phone=shipper.phone_number,
165
+ address=teleship_req.AddressType(
166
+ line1=shipper.address_line1,
167
+ line2=shipper.address_line2,
168
+ city=shipper.city,
169
+ state=shipper.state_code,
170
+ postcode=shipper.postal_code,
171
+ country=shipper.country_code,
172
+ ),
173
+ ),
174
+ returnTo=lib.identity(
175
+ teleship_req.BillToType(
176
+ name=return_address.contact or "N/A",
177
+ company=return_address.company_name,
178
+ email=return_address.email,
179
+ phone=return_address.phone_number,
180
+ address=teleship_req.AddressType(
181
+ line1=return_address.address_line1,
182
+ line2=return_address.address_line2,
183
+ city=return_address.city,
184
+ state=return_address.state_code,
185
+ postcode=return_address.postal_code,
186
+ country=return_address.country_code,
187
+ ),
188
+ )
189
+ if payload.return_address
190
+ else None
191
+ ),
192
+ billTo=lib.identity(
193
+ teleship_req.BillToType(
194
+ name=billing_address.contact or "N/A",
195
+ company=billing_address.company_name,
196
+ email=billing_address.email,
197
+ phone=billing_address.phone_number,
198
+ address=teleship_req.AddressType(
199
+ line1=billing_address.address_line1,
200
+ line2=billing_address.address_line2,
201
+ city=billing_address.city,
202
+ state=billing_address.state_code,
203
+ postcode=billing_address.postal_code,
204
+ country=billing_address.country_code,
205
+ ),
206
+ stateTaxId=billing_address.state_tax_id,
207
+ countryTaxId=billing_address.federal_tax_id,
208
+ )
209
+ if payload.billing_address
210
+ else None
211
+ ),
212
+ weight=teleship_req.WeightType(
213
+ value=package.weight.value,
214
+ unit=package.weight.unit.lower(),
215
+ ),
216
+ dimensions=lib.identity(
217
+ teleship_req.DimensionsType(
218
+ unit=package.dimension_unit.lower(),
219
+ length=package.length.value,
220
+ width=package.width.value,
221
+ height=package.height.value,
222
+ )
223
+ if all(
224
+ [
225
+ package.length.value,
226
+ package.width.value,
227
+ package.height.value,
228
+ ]
229
+ )
230
+ else None
231
+ ),
232
+ additionalServices=lib.identity(
233
+ teleship_req.AdditionalServicesType(
234
+ signatureRequired=options.teleship_signature_required.state,
235
+ deliveryWarranty=options.teleship_delivery_warranty.state,
236
+ deliveryPUDO=options.teleship_delivery_pudo.state,
237
+ lowCarbon=options.teleship_low_carbon.state,
238
+ dutyTaxCalculation=options.teleship_duty_tax_calculation.state,
239
+ )
240
+ if any(
241
+ [
242
+ options.teleship_signature_required.state,
243
+ options.teleship_delivery_warranty.state,
244
+ options.teleship_delivery_pudo.state,
245
+ options.teleship_low_carbon.state,
246
+ options.teleship_duty_tax_calculation.state,
247
+ ]
248
+ )
249
+ else None
250
+ ),
251
+ commodities=[
252
+ teleship_req.CommodityType(
253
+ sku=commodity.sku,
254
+ hsCode=commodity.hs_code,
255
+ title=lib.text(commodity.title or commodity.description, max=200),
256
+ description=lib.text(
257
+ commodity.description if commodity.title else None, max=200
258
+ ),
259
+ category=commodity.category,
260
+ value=teleship_req.ValueType(
261
+ amount=commodity.value_amount,
262
+ currency=commodity.value_currency,
263
+ ),
264
+ quantity=commodity.quantity,
265
+ unitWeight=teleship_req.WeightType(
266
+ value=commodity.weight,
267
+ unit=commodity.weight_unit.lower(),
268
+ ),
269
+ countryOfOrigin=commodity.origin_country,
270
+ imageUrl=commodity.image_url,
271
+ productUrl=commodity.product_url,
272
+ compliance=None,
273
+ )
274
+ for commodity in (
275
+ package.items if any(package.items) else customs.commodities or []
276
+ )
277
+ ],
278
+ customs=lib.identity(
279
+ teleship_req.CustomsType(
280
+ EORI=customs.options.eori_number.state,
281
+ IOSS=customs.options.ioss.state,
282
+ VAT=customs.options.vat.state,
283
+ EIN=customs.options.ein.state,
284
+ VOECNUMBER=customs.options.voec_number.state,
285
+ importerGST=customs.options.importer_gst.state,
286
+ exporterGST=customs.options.exporter_gst.state,
287
+ consigneeGST=customs.options.consignee_gst.state,
288
+ contentType=provider_units.CustomsContentType.map(
289
+ customs.content_type or "other"
290
+ ).value,
291
+ invoiceDate=customs.invoice_date,
292
+ invoiceNumber=customs.invoice,
293
+ GPSRContactInfo=options.teleship_gpsr_contact_info.state,
294
+ importerOfRecord=lib.identity(
295
+ teleship_req.BillToType(
296
+ name=customs.duty_billing_address.contact or "N/A",
297
+ company=customs.duty_billing_address.company_name,
298
+ email=customs.duty_billing_address.email,
299
+ phone=customs.duty_billing_address.phone_number,
300
+ address=teleship_req.AddressType(
301
+ line1=customs.duty_billing_address.address_line1,
302
+ line2=customs.duty_billing_address.address_line2,
303
+ city=customs.duty_billing_address.city,
304
+ state=customs.duty_billing_address.state_code,
305
+ country=customs.duty_billing_address.country_code,
306
+ postcode=customs.duty_billing_address.postal_code,
307
+ ),
308
+ stateTaxId=customs.duty_billing_address.state_tax_id,
309
+ countryTaxId=customs.duty_billing_address.federal_tax_id,
310
+ )
311
+ if payload.customs.duty_billing_address
312
+ else None
313
+ ),
314
+ )
315
+ if payload.customs and is_intl
316
+ else None
317
+ ),
318
+ )
319
+ for package in packages
320
+ ]
321
+
322
+ return lib.Serializable(request, lib.to_dict)