karrio-hermes 2026.1__py3-none-any.whl → 2026.1.3__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.
- karrio/mappers/hermes/mapper.py +11 -0
- karrio/mappers/hermes/proxy.py +60 -37
- karrio/providers/hermes/__init__.py +4 -0
- karrio/providers/hermes/pickup/create.py +8 -0
- karrio/providers/hermes/shipment/create.py +278 -245
- karrio/providers/hermes/tracking.py +156 -0
- karrio/providers/hermes/units.py +261 -29
- karrio/providers/hermes/utils.py +26 -0
- karrio/schemas/hermes/__init__.py +10 -0
- karrio/schemas/hermes/tracking_response.py +64 -0
- {karrio_hermes-2026.1.dist-info → karrio_hermes-2026.1.3.dist-info}/METADATA +1 -1
- karrio_hermes-2026.1.3.dist-info/RECORD +29 -0
- {karrio_hermes-2026.1.dist-info → karrio_hermes-2026.1.3.dist-info}/WHEEL +1 -1
- karrio_hermes-2026.1.dist-info/RECORD +0 -27
- {karrio_hermes-2026.1.dist-info → karrio_hermes-2026.1.3.dist-info}/entry_points.txt +0 -0
- {karrio_hermes-2026.1.dist-info → karrio_hermes-2026.1.3.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import typing
|
|
4
4
|
import karrio.lib as lib
|
|
5
|
-
import karrio.core.units as units
|
|
6
5
|
import karrio.core.models as models
|
|
7
6
|
import karrio.providers.hermes.error as error
|
|
8
7
|
import karrio.providers.hermes.utils as provider_utils
|
|
@@ -22,18 +21,31 @@ def _split_name(name: typing.Optional[str]) -> typing.Tuple[str, str]:
|
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
def parse_shipment_response(
|
|
25
|
-
_response: lib.Deserializable[dict],
|
|
24
|
+
_response: lib.Deserializable[typing.List[dict]],
|
|
26
25
|
settings: provider_utils.Settings,
|
|
27
26
|
) -> typing.Tuple[typing.Optional[models.ShipmentDetails], typing.List[models.Message]]:
|
|
28
|
-
"""Parse Hermes shipment response."""
|
|
29
|
-
|
|
30
|
-
messages = error.parse_error_response(response, settings)
|
|
27
|
+
"""Parse Hermes shipment response for single or multi-piece shipments."""
|
|
28
|
+
responses = _response.deserialize()
|
|
31
29
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
# Collect all messages from all responses
|
|
31
|
+
messages: typing.List[models.Message] = sum(
|
|
32
|
+
[error.parse_error_response(response, settings) for response in responses],
|
|
33
|
+
start=[],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Extract shipment details from each valid response
|
|
37
|
+
shipment_details = [
|
|
38
|
+
(
|
|
39
|
+
f"{idx}",
|
|
40
|
+
_extract_details(response, settings),
|
|
41
|
+
)
|
|
42
|
+
for idx, response in enumerate(responses, start=1)
|
|
43
|
+
if isinstance(response, dict)
|
|
44
|
+
and (response.get("shipmentID") or response.get("shipmentOrderID"))
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# Use lib.to_multi_piece_shipment() to aggregate multi-piece shipments
|
|
48
|
+
shipment = lib.to_multi_piece_shipment(shipment_details) if shipment_details else None
|
|
37
49
|
|
|
38
50
|
return shipment, messages
|
|
39
51
|
|
|
@@ -55,8 +67,9 @@ def _extract_details(
|
|
|
55
67
|
# Commercial invoice for international shipments
|
|
56
68
|
invoice_image = response.commInvoiceImage or ""
|
|
57
69
|
|
|
58
|
-
# Label media type
|
|
59
|
-
|
|
70
|
+
# Label media type - convert MIME type to format (e.g., "application/pdf" -> "PDF")
|
|
71
|
+
label_mediatype = response.labelMediatype or "application/pdf"
|
|
72
|
+
label_type = label_mediatype.split("/")[-1].upper() if "/" in label_mediatype else label_mediatype
|
|
60
73
|
|
|
61
74
|
documents = models.Documents(label=label_image)
|
|
62
75
|
if invoice_image:
|
|
@@ -80,257 +93,277 @@ def shipment_request(
|
|
|
80
93
|
payload: models.ShipmentRequest,
|
|
81
94
|
settings: provider_utils.Settings,
|
|
82
95
|
) -> lib.Serializable:
|
|
83
|
-
"""Create
|
|
96
|
+
"""Create Hermes shipment request(s) for single or multi-piece shipments.
|
|
97
|
+
|
|
98
|
+
For multi-piece shipments (Pattern B - Per-Package Request):
|
|
99
|
+
- First package: partNumber=1, numberOfParts=N, no parentShipmentOrderID
|
|
100
|
+
- Subsequent packages: partNumber=2,3,..., numberOfParts=N, parentShipmentOrderID=<first shipmentOrderID>
|
|
101
|
+
|
|
102
|
+
The proxy handles the sequential API calls and injects parentShipmentOrderID
|
|
103
|
+
from the first response into subsequent requests.
|
|
104
|
+
"""
|
|
84
105
|
shipper = lib.to_address(payload.shipper)
|
|
85
106
|
recipient = lib.to_address(payload.recipient)
|
|
86
107
|
packages = lib.to_packages(payload.parcels)
|
|
87
|
-
package = packages.single # Hermes handles one parcel per request
|
|
88
108
|
options = lib.to_shipping_options(
|
|
89
109
|
payload.options,
|
|
90
110
|
package_options=packages.options,
|
|
91
111
|
initializer=provider_units.shipping_options_initializer,
|
|
92
112
|
)
|
|
93
|
-
|
|
94
|
-
# Determine product type
|
|
95
|
-
product_type = provider_units.PackagingType.map(
|
|
96
|
-
package.packaging_type or "your_packaging"
|
|
97
|
-
).value
|
|
98
|
-
|
|
99
|
-
# Build services object based on options
|
|
100
|
-
service = _build_service(options)
|
|
101
|
-
|
|
102
|
-
# Build customs for international shipments
|
|
103
|
-
customs = None
|
|
104
|
-
if payload.customs:
|
|
105
|
-
customs = _build_customs(payload.customs, shipper)
|
|
113
|
+
customs = payload.customs
|
|
106
114
|
|
|
107
115
|
# Split names for Hermes API
|
|
108
116
|
recipient_firstname, recipient_lastname = _split_name(recipient.person_name)
|
|
109
117
|
shipper_firstname, shipper_lastname = _split_name(shipper.person_name)
|
|
110
118
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
gender=None,
|
|
123
|
-
firstname=recipient_firstname,
|
|
124
|
-
middlename=None,
|
|
125
|
-
lastname=recipient_lastname,
|
|
126
|
-
),
|
|
127
|
-
# Receiver address
|
|
128
|
-
receiverAddress=hermes_req.ErAddressType(
|
|
129
|
-
street=lib.text(recipient.street_name, max=50),
|
|
130
|
-
houseNumber=lib.text(recipient.street_number, max=5) or "",
|
|
131
|
-
zipCode=recipient.postal_code,
|
|
132
|
-
town=lib.text(recipient.city, max=30),
|
|
133
|
-
countryCode=recipient.country_code,
|
|
134
|
-
addressAddition=lib.text(recipient.address_line2, max=50),
|
|
135
|
-
addressAddition2=None,
|
|
136
|
-
addressAddition3=lib.text(recipient.company_name, max=20),
|
|
137
|
-
),
|
|
138
|
-
# Receiver contact
|
|
139
|
-
receiverContact=lib.identity(
|
|
140
|
-
hermes_req.ReceiverContactType(
|
|
141
|
-
phone=lib.text(recipient.phone_number, max=20),
|
|
142
|
-
mobile=None,
|
|
143
|
-
mail=recipient.email,
|
|
144
|
-
)
|
|
145
|
-
if recipient.phone_number or recipient.email
|
|
146
|
-
else None
|
|
147
|
-
),
|
|
148
|
-
# Sender (divergent sender if different from account default)
|
|
149
|
-
senderName=lib.identity(
|
|
150
|
-
hermes_req.ErNameType(
|
|
119
|
+
# Determine if this is a multi-piece shipment
|
|
120
|
+
is_multi_piece = len(packages) > 1
|
|
121
|
+
number_of_parts = len(packages) if is_multi_piece else None
|
|
122
|
+
|
|
123
|
+
# Create a request for each package - single tree instantiation
|
|
124
|
+
requests = [
|
|
125
|
+
hermes_req.ShipmentRequestType(
|
|
126
|
+
clientReference=lib.text(payload.reference, max=20) or "",
|
|
127
|
+
clientReference2=lib.text((payload.options or {}).get("clientReference2"), max=20),
|
|
128
|
+
# Receiver name
|
|
129
|
+
receiverName=hermes_req.ErNameType(
|
|
151
130
|
title=None,
|
|
152
131
|
gender=None,
|
|
153
|
-
firstname=
|
|
132
|
+
firstname=recipient_firstname,
|
|
154
133
|
middlename=None,
|
|
155
|
-
lastname=
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
town=lib.text(shipper.city, max=30),
|
|
166
|
-
countryCode=shipper.country_code,
|
|
167
|
-
addressAddition=lib.text(shipper.address_line2, max=50),
|
|
134
|
+
lastname=recipient_lastname,
|
|
135
|
+
),
|
|
136
|
+
# Receiver address
|
|
137
|
+
receiverAddress=hermes_req.ErAddressType(
|
|
138
|
+
street=lib.text(recipient.street_name, max=50),
|
|
139
|
+
houseNumber=lib.text(recipient.street_number, max=5) or "",
|
|
140
|
+
zipCode=recipient.postal_code,
|
|
141
|
+
town=lib.text(recipient.city, max=30),
|
|
142
|
+
countryCode=recipient.country_code,
|
|
143
|
+
addressAddition=lib.text(recipient.address_line2, max=50),
|
|
168
144
|
addressAddition2=None,
|
|
169
|
-
addressAddition3=lib.text(
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
145
|
+
addressAddition3=lib.text(recipient.company_name, max=20),
|
|
146
|
+
),
|
|
147
|
+
# Receiver contact
|
|
148
|
+
receiverContact=lib.identity(
|
|
149
|
+
hermes_req.ReceiverContactType(
|
|
150
|
+
phone=lib.text(recipient.phone_number, max=20),
|
|
151
|
+
mobile=None,
|
|
152
|
+
mail=recipient.email,
|
|
153
|
+
)
|
|
154
|
+
if recipient.phone_number or recipient.email
|
|
155
|
+
else None
|
|
156
|
+
),
|
|
157
|
+
# Sender (divergent sender if different from account default)
|
|
158
|
+
senderName=lib.identity(
|
|
159
|
+
hermes_req.ErNameType(
|
|
160
|
+
title=None,
|
|
161
|
+
gender=None,
|
|
162
|
+
firstname=shipper_firstname,
|
|
163
|
+
middlename=None,
|
|
164
|
+
lastname=shipper_lastname,
|
|
165
|
+
)
|
|
166
|
+
if shipper.person_name
|
|
167
|
+
else None
|
|
168
|
+
),
|
|
169
|
+
senderAddress=lib.identity(
|
|
170
|
+
hermes_req.ErAddressType(
|
|
171
|
+
street=lib.text(shipper.street_name, max=50),
|
|
172
|
+
houseNumber=lib.text(shipper.street_number, max=5) or "",
|
|
173
|
+
zipCode=shipper.postal_code,
|
|
174
|
+
town=lib.text(shipper.city, max=30),
|
|
175
|
+
countryCode=shipper.country_code,
|
|
176
|
+
addressAddition=lib.text(shipper.address_line2, max=50),
|
|
177
|
+
addressAddition2=None,
|
|
178
|
+
addressAddition3=lib.text(shipper.company_name, max=20),
|
|
179
|
+
)
|
|
180
|
+
if shipper.street
|
|
181
|
+
else None
|
|
182
|
+
),
|
|
183
|
+
# Parcel details (weight in grams)
|
|
184
|
+
parcel=hermes_req.ParcelType(
|
|
185
|
+
parcelClass=None,
|
|
186
|
+
parcelHeight=lib.to_int(package.height.MM),
|
|
187
|
+
parcelWidth=lib.to_int(package.width.MM),
|
|
188
|
+
parcelDepth=lib.to_int(package.length.MM),
|
|
189
|
+
parcelWeight=lib.to_int(package.weight.G),
|
|
190
|
+
parcelVolume=None,
|
|
191
|
+
productType=provider_units.PackagingType.map(
|
|
192
|
+
package.packaging_type or "your_packaging"
|
|
193
|
+
).value,
|
|
194
|
+
),
|
|
195
|
+
# Services - single tree instantiation with all options inline
|
|
196
|
+
service=lib.identity(
|
|
197
|
+
hermes_req.ServiceType(
|
|
198
|
+
tanService=options.hermes_tan_service.state,
|
|
199
|
+
# Multipart service for multi-piece shipments
|
|
200
|
+
multipartService=(
|
|
201
|
+
hermes_req.MultipartServiceType(
|
|
202
|
+
partNumber=index,
|
|
203
|
+
numberOfParts=number_of_parts,
|
|
204
|
+
parentShipmentOrderID=None, # Injected by proxy for parts 2+
|
|
205
|
+
)
|
|
206
|
+
if is_multi_piece
|
|
207
|
+
else (
|
|
208
|
+
hermes_req.MultipartServiceType(
|
|
209
|
+
partNumber=options.hermes_part_number.state or 1,
|
|
210
|
+
numberOfParts=options.hermes_number_of_parts.state,
|
|
211
|
+
parentShipmentOrderID=options.hermes_parent_shipment_order_id.state,
|
|
212
|
+
)
|
|
213
|
+
if options.hermes_number_of_parts.state
|
|
214
|
+
else None
|
|
215
|
+
)
|
|
216
|
+
),
|
|
217
|
+
limitedQuantitiesService=options.hermes_limited_quantities.state,
|
|
218
|
+
# Cash on delivery service
|
|
219
|
+
cashOnDeliveryService=(
|
|
220
|
+
hermes_req.CashOnDeliveryServiceType(
|
|
221
|
+
amount=options.hermes_cod_amount.state,
|
|
222
|
+
currency=options.hermes_cod_currency.state or "EUR",
|
|
223
|
+
bankTransferAmount=options.hermes_cod_amount.state,
|
|
224
|
+
bankTransferCurrency=options.hermes_cod_currency.state or "EUR",
|
|
225
|
+
)
|
|
226
|
+
if options.hermes_cod_amount.state
|
|
227
|
+
else None
|
|
228
|
+
),
|
|
229
|
+
bulkGoodService=options.hermes_bulk_goods.state,
|
|
230
|
+
# Stated time service
|
|
231
|
+
statedTimeService=(
|
|
232
|
+
hermes_req.StatedTimeServiceType(
|
|
233
|
+
timeSlot=options.hermes_time_slot.state,
|
|
234
|
+
)
|
|
235
|
+
if options.hermes_time_slot.state
|
|
236
|
+
else None
|
|
237
|
+
),
|
|
238
|
+
householdSignatureService=options.hermes_household_signature.state,
|
|
239
|
+
# Customer alert service
|
|
240
|
+
customerAlertService=(
|
|
241
|
+
hermes_req.CustomerAlertServiceType(
|
|
242
|
+
notificationType=options.hermes_notification_type.state or "EMAIL",
|
|
243
|
+
notificationEmail=options.hermes_notification_email.state,
|
|
244
|
+
notificationNumber=None,
|
|
245
|
+
)
|
|
246
|
+
if options.hermes_notification_email.state
|
|
247
|
+
else None
|
|
248
|
+
),
|
|
249
|
+
# Parcel shop delivery service
|
|
250
|
+
parcelShopDeliveryService=(
|
|
251
|
+
hermes_req.ParcelShopDeliveryServiceType(
|
|
252
|
+
psCustomerFirstName=options.hermes_parcel_shop_customer_firstname.state,
|
|
253
|
+
psCustomerLastName=options.hermes_parcel_shop_customer_lastname.state,
|
|
254
|
+
psID=options.hermes_parcel_shop_id.state,
|
|
255
|
+
psSelectionRule=options.hermes_parcel_shop_selection_rule.state or "SELECT_BY_ID",
|
|
256
|
+
)
|
|
257
|
+
if options.hermes_parcel_shop_id.state
|
|
258
|
+
else None
|
|
259
|
+
),
|
|
260
|
+
compactParcelService=options.hermes_compact_parcel.state,
|
|
261
|
+
# Ident service
|
|
262
|
+
identService=(
|
|
263
|
+
hermes_req.IdentServiceType(
|
|
264
|
+
identID=options.hermes_ident_id.state,
|
|
265
|
+
identType=options.hermes_ident_type.state,
|
|
266
|
+
identVerifyFsk=options.hermes_ident_fsk.state,
|
|
267
|
+
identVerifyBirthday=options.hermes_ident_birthday.state,
|
|
268
|
+
)
|
|
269
|
+
if options.hermes_ident_fsk.state or options.hermes_ident_id.state
|
|
270
|
+
else None
|
|
271
|
+
),
|
|
272
|
+
# Stated day service
|
|
273
|
+
statedDayService=(
|
|
274
|
+
hermes_req.StatedDayServiceType(
|
|
275
|
+
statedDay=options.hermes_stated_day.state,
|
|
276
|
+
)
|
|
277
|
+
if options.hermes_stated_day.state
|
|
278
|
+
else None
|
|
279
|
+
),
|
|
280
|
+
nextDayService=options.hermes_next_day.state,
|
|
281
|
+
signatureService=options.hermes_signature.state,
|
|
282
|
+
redirectionProhibitedService=options.hermes_redirection_prohibited.state,
|
|
283
|
+
excludeParcelShopAuthorization=options.hermes_exclude_parcel_shop_auth.state,
|
|
284
|
+
lateInjectionService=options.hermes_late_injection.state,
|
|
285
|
+
)
|
|
286
|
+
if any([
|
|
287
|
+
options.hermes_tan_service.state,
|
|
288
|
+
is_multi_piece,
|
|
289
|
+
options.hermes_number_of_parts.state,
|
|
290
|
+
options.hermes_limited_quantities.state,
|
|
291
|
+
options.hermes_cod_amount.state,
|
|
292
|
+
options.hermes_bulk_goods.state,
|
|
293
|
+
options.hermes_time_slot.state,
|
|
294
|
+
options.hermes_household_signature.state,
|
|
295
|
+
options.hermes_notification_email.state,
|
|
296
|
+
options.hermes_parcel_shop_id.state,
|
|
297
|
+
options.hermes_compact_parcel.state,
|
|
298
|
+
options.hermes_ident_fsk.state,
|
|
299
|
+
options.hermes_ident_id.state,
|
|
300
|
+
options.hermes_stated_day.state,
|
|
301
|
+
options.hermes_next_day.state,
|
|
302
|
+
options.hermes_signature.state,
|
|
303
|
+
options.hermes_redirection_prohibited.state,
|
|
304
|
+
options.hermes_exclude_parcel_shop_auth.state,
|
|
305
|
+
options.hermes_late_injection.state,
|
|
306
|
+
])
|
|
307
|
+
else None
|
|
308
|
+
),
|
|
309
|
+
# Customs for international shipments - inline
|
|
310
|
+
customsAndTaxes=(
|
|
311
|
+
hermes_req.CustomsAndTaxesType(
|
|
312
|
+
currency=lib.identity(customs.duty.currency if customs.duty else "EUR"),
|
|
313
|
+
shipmentCost=None,
|
|
314
|
+
items=[
|
|
315
|
+
hermes_req.ItemType(
|
|
316
|
+
sku=item.sku,
|
|
317
|
+
category=None,
|
|
318
|
+
countryCodeOfManufacture=item.origin_country,
|
|
319
|
+
value=lib.to_int(item.value_amount * 100) if item.value_amount else None,
|
|
320
|
+
weight=lib.to_int(item.weight * 1000) if item.weight else None,
|
|
321
|
+
quantity=item.quantity or 1,
|
|
322
|
+
description=item.description or item.title,
|
|
323
|
+
exportDescription=None,
|
|
324
|
+
exportHsCode=None,
|
|
325
|
+
hsCode=item.hs_code,
|
|
326
|
+
url=None,
|
|
327
|
+
)
|
|
328
|
+
for item in (customs.commodities or [])
|
|
329
|
+
] or None,
|
|
330
|
+
invoiceReferences=None,
|
|
331
|
+
value=None,
|
|
332
|
+
exportCustomsClearance=None,
|
|
333
|
+
client=None,
|
|
334
|
+
shipmentOriginAddress=lib.identity(
|
|
335
|
+
hermes_req.ShipmentOriginAddressType(
|
|
336
|
+
title=None,
|
|
337
|
+
firstname=shipper_firstname,
|
|
338
|
+
lastname=shipper_lastname,
|
|
339
|
+
company=shipper.company_name,
|
|
340
|
+
street=shipper.street_name,
|
|
341
|
+
houseNumber=shipper.street_number or "",
|
|
342
|
+
zipCode=shipper.postal_code,
|
|
343
|
+
town=shipper.city,
|
|
344
|
+
state=shipper.state_code,
|
|
345
|
+
countryCode=shipper.country_code,
|
|
346
|
+
addressAddition=shipper.address_line2,
|
|
347
|
+
addressAddition2=None,
|
|
348
|
+
addressAddition3=None,
|
|
349
|
+
phone=shipper.phone_number,
|
|
350
|
+
fax=None,
|
|
351
|
+
mobile=None,
|
|
352
|
+
mail=shipper.email,
|
|
353
|
+
)
|
|
354
|
+
if shipper
|
|
355
|
+
else None
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
if customs
|
|
359
|
+
else None
|
|
360
|
+
),
|
|
206
361
|
)
|
|
207
|
-
|
|
208
|
-
# Customer alert service
|
|
209
|
-
alert_service = None
|
|
210
|
-
if options.hermes_notification_email.state:
|
|
211
|
-
alert_service = hermes_req.CustomerAlertServiceType(
|
|
212
|
-
notificationType=options.hermes_notification_type.state or "EMAIL",
|
|
213
|
-
notificationEmail=options.hermes_notification_email.state,
|
|
214
|
-
notificationNumber=None,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
# Ident service
|
|
218
|
-
ident_service = None
|
|
219
|
-
if options.hermes_ident_fsk.state or options.hermes_ident_id.state:
|
|
220
|
-
ident_service = hermes_req.IdentServiceType(
|
|
221
|
-
identID=options.hermes_ident_id.state,
|
|
222
|
-
identType=options.hermes_ident_type.state,
|
|
223
|
-
identVerifyFsk=options.hermes_ident_fsk.state,
|
|
224
|
-
identVerifyBirthday=options.hermes_ident_birthday.state,
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
# Parcel shop delivery
|
|
228
|
-
parcel_shop_service = None
|
|
229
|
-
if options.hermes_parcel_shop_id.state:
|
|
230
|
-
parcel_shop_service = hermes_req.ParcelShopDeliveryServiceType(
|
|
231
|
-
psCustomerFirstName=options.hermes_parcel_shop_customer_firstname.state,
|
|
232
|
-
psCustomerLastName=options.hermes_parcel_shop_customer_lastname.state,
|
|
233
|
-
psID=options.hermes_parcel_shop_id.state,
|
|
234
|
-
psSelectionRule=options.hermes_parcel_shop_selection_rule.state or "SELECT_BY_ID",
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
# Stated day service
|
|
238
|
-
stated_day_service = None
|
|
239
|
-
if options.hermes_stated_day.state:
|
|
240
|
-
stated_day_service = hermes_req.StatedDayServiceType(
|
|
241
|
-
statedDay=options.hermes_stated_day.state,
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
# Stated time service
|
|
245
|
-
stated_time_service = None
|
|
246
|
-
if options.hermes_time_slot.state:
|
|
247
|
-
stated_time_service = hermes_req.StatedTimeServiceType(
|
|
248
|
-
timeSlot=options.hermes_time_slot.state,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
# Multipart service
|
|
252
|
-
multipart_service = None
|
|
253
|
-
if options.hermes_number_of_parts.state:
|
|
254
|
-
multipart_service = hermes_req.MultipartServiceType(
|
|
255
|
-
partNumber=options.hermes_part_number.state or 1,
|
|
256
|
-
numberOfParts=options.hermes_number_of_parts.state,
|
|
257
|
-
parentShipmentOrderID=options.hermes_parent_shipment_order_id.state,
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
return hermes_req.ServiceType(
|
|
261
|
-
tanService=options.hermes_tan_service.state,
|
|
262
|
-
multipartService=multipart_service,
|
|
263
|
-
limitedQuantitiesService=options.hermes_limited_quantities.state,
|
|
264
|
-
cashOnDeliveryService=cod_service,
|
|
265
|
-
bulkGoodService=options.hermes_bulk_goods.state,
|
|
266
|
-
statedTimeService=stated_time_service,
|
|
267
|
-
householdSignatureService=options.hermes_household_signature.state,
|
|
268
|
-
customerAlertService=alert_service,
|
|
269
|
-
parcelShopDeliveryService=parcel_shop_service,
|
|
270
|
-
compactParcelService=options.hermes_compact_parcel.state,
|
|
271
|
-
identService=ident_service,
|
|
272
|
-
statedDayService=stated_day_service,
|
|
273
|
-
nextDayService=options.hermes_next_day.state,
|
|
274
|
-
signatureService=options.hermes_signature.state,
|
|
275
|
-
redirectionProhibitedService=options.hermes_redirection_prohibited.state,
|
|
276
|
-
excludeParcelShopAuthorization=options.hermes_exclude_parcel_shop_auth.state,
|
|
277
|
-
lateInjectionService=options.hermes_late_injection.state,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
def _build_customs(
|
|
282
|
-
customs: models.Customs,
|
|
283
|
-
shipper,
|
|
284
|
-
) -> hermes_req.CustomsAndTaxesType:
|
|
285
|
-
"""Build customs and taxes for international shipments."""
|
|
286
|
-
items = [
|
|
287
|
-
hermes_req.ItemType(
|
|
288
|
-
sku=item.sku,
|
|
289
|
-
category=None,
|
|
290
|
-
countryCodeOfManufacture=item.origin_country,
|
|
291
|
-
value=lib.to_int(item.value_amount * 100) if item.value_amount else None, # In cents
|
|
292
|
-
weight=lib.to_int(item.weight * 1000) if item.weight else None, # In grams
|
|
293
|
-
quantity=item.quantity or 1,
|
|
294
|
-
description=item.description or item.title,
|
|
295
|
-
exportDescription=None,
|
|
296
|
-
exportHsCode=None,
|
|
297
|
-
hsCode=item.hs_code,
|
|
298
|
-
url=None,
|
|
299
|
-
)
|
|
300
|
-
for item in customs.commodities or []
|
|
362
|
+
for index, package in enumerate(packages, start=1)
|
|
301
363
|
]
|
|
302
364
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
shipmentCost=None,
|
|
308
|
-
items=items or None,
|
|
309
|
-
invoiceReferences=None,
|
|
310
|
-
value=None,
|
|
311
|
-
exportCustomsClearance=None,
|
|
312
|
-
client=None,
|
|
313
|
-
shipmentOriginAddress=lib.identity(
|
|
314
|
-
hermes_req.ShipmentOriginAddressType(
|
|
315
|
-
title=None,
|
|
316
|
-
firstname=shipper_firstname,
|
|
317
|
-
lastname=shipper_lastname,
|
|
318
|
-
company=shipper.company_name,
|
|
319
|
-
street=shipper.street_name,
|
|
320
|
-
houseNumber=shipper.street_number or "",
|
|
321
|
-
zipCode=shipper.postal_code,
|
|
322
|
-
town=shipper.city,
|
|
323
|
-
state=shipper.state_code,
|
|
324
|
-
countryCode=shipper.country_code,
|
|
325
|
-
addressAddition=shipper.address_line2,
|
|
326
|
-
addressAddition2=None,
|
|
327
|
-
addressAddition3=None,
|
|
328
|
-
phone=shipper.phone_number,
|
|
329
|
-
fax=None,
|
|
330
|
-
mobile=None,
|
|
331
|
-
mail=shipper.email,
|
|
332
|
-
)
|
|
333
|
-
if shipper
|
|
334
|
-
else None
|
|
335
|
-
),
|
|
365
|
+
return lib.Serializable(
|
|
366
|
+
requests,
|
|
367
|
+
lambda reqs: [lib.to_dict(req) for req in reqs],
|
|
368
|
+
dict(is_multi_piece=is_multi_piece),
|
|
336
369
|
)
|