karrio-hermes 2026.1.3__py3-none-any.whl → 2026.1.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.
- karrio/providers/hermes/tracking.py +5 -6
- karrio/providers/hermes/units.py +217 -288
- karrio/schemas/hermes/tracking_response.py +0 -1
- {karrio_hermes-2026.1.3.dist-info → karrio_hermes-2026.1.5.dist-info}/METADATA +1 -1
- {karrio_hermes-2026.1.3.dist-info → karrio_hermes-2026.1.5.dist-info}/RECORD +8 -8
- {karrio_hermes-2026.1.3.dist-info → karrio_hermes-2026.1.5.dist-info}/WHEEL +0 -0
- {karrio_hermes-2026.1.3.dist-info → karrio_hermes-2026.1.5.dist-info}/entry_points.txt +0 -0
- {karrio_hermes-2026.1.3.dist-info → karrio_hermes-2026.1.5.dist-info}/top_level.txt +0 -0
|
@@ -20,12 +20,11 @@ def _match_status(code: str) -> typing.Optional[str]:
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _match_reason(code: str) -> typing.Optional[str]:
|
|
23
|
-
"""Match Hermes event code against
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return reason.name
|
|
23
|
+
"""Match Hermes event code against incident reasons.
|
|
24
|
+
|
|
25
|
+
Hermes uses numeric codes that map to statuses, not specific incident reasons.
|
|
26
|
+
Returns None as Hermes does not provide granular incident reason codes.
|
|
27
|
+
"""
|
|
29
28
|
return None
|
|
30
29
|
|
|
31
30
|
|
karrio/providers/hermes/units.py
CHANGED
|
@@ -64,48 +64,198 @@ class ShippingService(lib.StrEnum):
|
|
|
64
64
|
class ShippingOption(lib.Enum):
|
|
65
65
|
"""Carrier specific options."""
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
hermes_stated_day = lib.OptionEnum(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
67
|
+
# Delivery Options (Zustelloptionen tab)
|
|
68
|
+
hermes_next_day = lib.OptionEnum(
|
|
69
|
+
"nextDayService", bool,
|
|
70
|
+
help="Enable next-day delivery service",
|
|
71
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
72
|
+
)
|
|
73
|
+
hermes_bulk_goods = lib.OptionEnum(
|
|
74
|
+
"bulkGoodService", bool,
|
|
75
|
+
help="Mark shipment as bulky goods (Sperrgut)",
|
|
76
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
77
|
+
)
|
|
78
|
+
hermes_compact_parcel = lib.OptionEnum(
|
|
79
|
+
"compactParcelService", bool,
|
|
80
|
+
help="Enable compact parcel service",
|
|
81
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
82
|
+
)
|
|
83
|
+
hermes_redirection_prohibited = lib.OptionEnum(
|
|
84
|
+
"redirectionProhibitedService", bool,
|
|
85
|
+
help="Do not allow redirection to neighbor",
|
|
86
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
87
|
+
)
|
|
88
|
+
hermes_stated_day = lib.OptionEnum(
|
|
89
|
+
"statedDay", str,
|
|
90
|
+
help="Specific delivery date (YYYY-MM-DD format)",
|
|
91
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
92
|
+
)
|
|
93
|
+
hermes_time_slot = lib.OptionEnum(
|
|
94
|
+
"timeSlot", str,
|
|
95
|
+
help="Delivery time slot (FORENOON, NOON, AFTERNOON, EVENING)",
|
|
96
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
97
|
+
)
|
|
98
|
+
hermes_express = lib.OptionEnum(
|
|
99
|
+
"expressService", bool,
|
|
100
|
+
help="Enable express delivery service",
|
|
101
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
102
|
+
)
|
|
103
|
+
hermes_after_hours_delivery = lib.OptionEnum(
|
|
104
|
+
"afterHoursDeliveryService", bool,
|
|
105
|
+
help="Enable after-hours delivery (Feierabendservice)",
|
|
106
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
107
|
+
)
|
|
108
|
+
hermes_parcel_class = lib.OptionEnum(
|
|
109
|
+
"parcelClass", str,
|
|
110
|
+
help="Parcel size class (XS, S, M, L, XL)",
|
|
111
|
+
meta=dict(category="DELIVERY_OPTIONS", configurable=True)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Signature Options
|
|
115
|
+
hermes_signature = lib.OptionEnum(
|
|
116
|
+
"signatureService", bool,
|
|
117
|
+
help="Require signature upon delivery",
|
|
118
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
119
|
+
)
|
|
120
|
+
hermes_household_signature = lib.OptionEnum(
|
|
121
|
+
"householdSignatureService", bool,
|
|
122
|
+
help="Require household member signature",
|
|
123
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
124
|
+
)
|
|
125
|
+
hermes_ident_id = lib.OptionEnum(
|
|
126
|
+
"identID", str,
|
|
127
|
+
help="ID number for identity verification",
|
|
128
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
129
|
+
)
|
|
130
|
+
hermes_ident_type = lib.OptionEnum(
|
|
131
|
+
"identType", str,
|
|
132
|
+
help="Type of ID for verification (e.g., GERMAN_IDENTITY_CARD)",
|
|
133
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
134
|
+
)
|
|
135
|
+
hermes_ident_fsk = lib.OptionEnum(
|
|
136
|
+
"identVerifyFsk", str,
|
|
137
|
+
help="Minimum age verification (e.g., 18)",
|
|
138
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
139
|
+
)
|
|
140
|
+
hermes_ident_birthday = lib.OptionEnum(
|
|
141
|
+
"identVerifyBirthday", str,
|
|
142
|
+
help="Verify recipient birthday (YYYY-MM-DD)",
|
|
143
|
+
meta=dict(category="SIGNATURE", configurable=True)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# PUDO Options (Parcel Shop)
|
|
147
|
+
hermes_parcel_shop_id = lib.OptionEnum(
|
|
148
|
+
"psID", str,
|
|
149
|
+
help="Hermes ParcelShop ID for delivery",
|
|
150
|
+
meta=dict(category="PUDO", configurable=True)
|
|
151
|
+
)
|
|
152
|
+
hermes_parcel_shop_selection_rule = lib.OptionEnum(
|
|
153
|
+
"psSelectionRule", str,
|
|
154
|
+
help="ParcelShop selection rule (SELECT_BY_ID, SELECT_BY_RECEIVER_ADDRESS)",
|
|
155
|
+
meta=dict(category="PUDO", configurable=True)
|
|
156
|
+
)
|
|
157
|
+
hermes_parcel_shop_customer_firstname = lib.OptionEnum(
|
|
158
|
+
"psCustomerFirstName", str,
|
|
159
|
+
help="Customer first name for ParcelShop pickup",
|
|
160
|
+
meta=dict(category="PUDO", configurable=True)
|
|
161
|
+
)
|
|
162
|
+
hermes_parcel_shop_customer_lastname = lib.OptionEnum(
|
|
163
|
+
"psCustomerLastName", str,
|
|
164
|
+
help="Customer last name for ParcelShop pickup",
|
|
165
|
+
meta=dict(category="PUDO", configurable=True)
|
|
166
|
+
)
|
|
167
|
+
hermes_exclude_parcel_shop_auth = lib.OptionEnum(
|
|
168
|
+
"excludeParcelShopAuthorization", bool,
|
|
169
|
+
help="Exclude ParcelShop delivery authorization",
|
|
170
|
+
meta=dict(category="PUDO", configurable=True)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Notification Options
|
|
174
|
+
hermes_notification_email = lib.OptionEnum(
|
|
175
|
+
"notificationEmail", str,
|
|
176
|
+
help="Email for delivery notifications",
|
|
177
|
+
meta=dict(category="NOTIFICATION", configurable=True)
|
|
178
|
+
)
|
|
179
|
+
hermes_notification_type = lib.OptionEnum(
|
|
180
|
+
"notificationType", str,
|
|
181
|
+
help="Notification type (EMAIL, SMS, EMAIL_SMS)",
|
|
182
|
+
meta=dict(category="NOTIFICATION", configurable=True)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# COD Options (Cash on Delivery)
|
|
186
|
+
hermes_cod_amount = lib.OptionEnum(
|
|
187
|
+
"codAmount", float,
|
|
188
|
+
help="Cash on delivery amount",
|
|
189
|
+
meta=dict(category="COD", configurable=True)
|
|
190
|
+
)
|
|
191
|
+
hermes_cod_currency = lib.OptionEnum(
|
|
192
|
+
"codCurrency", str,
|
|
193
|
+
help="Currency for COD amount",
|
|
194
|
+
meta=dict(category="COD", configurable=True)
|
|
195
|
+
)
|
|
196
|
+
hermes_cod_distribution = lib.OptionEnum(
|
|
197
|
+
"codDistribution", str,
|
|
198
|
+
help="COD distribution method (e.g., transfer, check)",
|
|
199
|
+
meta=dict(category="COD", configurable=True)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Dangerous Goods
|
|
203
|
+
hermes_limited_quantities = lib.OptionEnum(
|
|
204
|
+
"limitedQuantitiesService", bool,
|
|
205
|
+
help="Mark shipment as containing limited quantity hazardous materials",
|
|
206
|
+
meta=dict(category="DANGEROUS_GOOD", configurable=True)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Return Options
|
|
210
|
+
hermes_return_enabled = lib.OptionEnum(
|
|
211
|
+
"returnService", bool,
|
|
212
|
+
help="Enable return label for this shipment",
|
|
213
|
+
meta=dict(category="RETURN", configurable=True)
|
|
214
|
+
)
|
|
215
|
+
hermes_include_return_label = lib.OptionEnum(
|
|
216
|
+
"includeReturnLabel", bool,
|
|
217
|
+
help="Include a pre-printed return label inside the package",
|
|
218
|
+
meta=dict(category="RETURN", configurable=True)
|
|
219
|
+
)
|
|
220
|
+
hermes_digital_sales_return = lib.OptionEnum(
|
|
221
|
+
"digitalSalesReturn", bool,
|
|
222
|
+
help="Enable digital sales return (digitale Verkaufsretoure)",
|
|
223
|
+
meta=dict(category="RETURN", configurable=True)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Reference/Instructions Options
|
|
227
|
+
hermes_customer_reference_1 = lib.OptionEnum(
|
|
228
|
+
"customerReference1", str,
|
|
229
|
+
help="Customer reference field 1 (Kundenreferenz 1)",
|
|
230
|
+
meta=dict(category="INSTRUCTIONS", configurable=True)
|
|
231
|
+
)
|
|
232
|
+
hermes_customer_reference_2 = lib.OptionEnum(
|
|
233
|
+
"customerReference2", str,
|
|
234
|
+
help="Customer reference field 2 (Kundenreferenz 2)",
|
|
235
|
+
meta=dict(category="INSTRUCTIONS", configurable=True)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Internal/Multipart Options (not configurable in shipping method editor)
|
|
239
|
+
hermes_tan_service = lib.OptionEnum(
|
|
240
|
+
"tanService", bool,
|
|
241
|
+
meta=dict(configurable=False)
|
|
242
|
+
)
|
|
243
|
+
hermes_late_injection = lib.OptionEnum(
|
|
244
|
+
"lateInjectionService", bool,
|
|
245
|
+
meta=dict(configurable=False)
|
|
246
|
+
)
|
|
247
|
+
hermes_part_number = lib.OptionEnum(
|
|
248
|
+
"partNumber", int,
|
|
249
|
+
meta=dict(configurable=False)
|
|
250
|
+
)
|
|
251
|
+
hermes_number_of_parts = lib.OptionEnum(
|
|
252
|
+
"numberOfParts", int,
|
|
253
|
+
meta=dict(configurable=False)
|
|
254
|
+
)
|
|
255
|
+
hermes_parent_shipment_order_id = lib.OptionEnum(
|
|
256
|
+
"parentShipmentOrderID", str,
|
|
257
|
+
meta=dict(configurable=False)
|
|
258
|
+
)
|
|
109
259
|
|
|
110
260
|
"""Unified Option type mapping."""
|
|
111
261
|
signature_required = hermes_signature
|
|
@@ -128,247 +278,16 @@ def shipping_options_initializer(
|
|
|
128
278
|
|
|
129
279
|
|
|
130
280
|
class TrackingStatus(lib.Enum):
|
|
131
|
-
"""Hermes tracking status mapping.
|
|
281
|
+
"""Hermes tracking status mapping based on Hermes event codes."""
|
|
132
282
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"0800", # Shipment has not arrived at the depot (7 days)
|
|
142
|
-
"0900", # Shipment has not arrived at the depot (28 days)
|
|
143
|
-
]
|
|
144
|
-
picked_up = [
|
|
145
|
-
"1000", # Die Sendung hat das Lager des Auftraggebers verlassen
|
|
146
|
-
"1900", # Shipment accepted after collection
|
|
147
|
-
"1901", # Shipment arrived at branch by self-delivery
|
|
148
|
-
"1910", # Shipment received on tour
|
|
149
|
-
]
|
|
150
|
-
in_transit = [
|
|
151
|
-
"1510", # Sendung am LC umgeschlagen (sorted at logistics center)
|
|
152
|
-
"1610", # Shipment in international transit
|
|
153
|
-
"1710", # Customs export created
|
|
154
|
-
"1720", # Customs clearance completed
|
|
155
|
-
"1810", # Handed to partner carrier (international)
|
|
156
|
-
"1820", # Handed to partner carrier (international)
|
|
157
|
-
"2000", # Die Sendung ist eingetroffen (arrived at depot)
|
|
158
|
-
"2100", # Arrived without advice data
|
|
159
|
-
"2300", # Automatic shipment received
|
|
160
|
-
"2400", # Shipment handed in at ParcelShop
|
|
161
|
-
]
|
|
162
|
-
out_for_delivery = [
|
|
163
|
-
"3000", # Die Sendung ist auf Zustelltour gegangen
|
|
164
|
-
"3010", # Shipment has left depot on tour
|
|
165
|
-
"3300", # Sorted for delivery tour (automatic)
|
|
166
|
-
]
|
|
167
|
-
ready_for_pickup = [
|
|
168
|
-
"3410", # Die Sendung liegt im PaketShop zur Abholung bereit
|
|
169
|
-
"3430", # Handed over to island carrier
|
|
170
|
-
]
|
|
171
|
-
delivered = [
|
|
172
|
-
"3500", # Die Sendung wurde zugestellt
|
|
173
|
-
"3510", # Shipment delivered (with scanner)
|
|
174
|
-
"3511", # Delivered in letterbox
|
|
175
|
-
"3520", # Shipment delivered (without scanner)
|
|
176
|
-
"3530", # Collected by recipient from ParcelShop
|
|
177
|
-
"7500", # Return shipment arrived at client
|
|
178
|
-
]
|
|
179
|
-
delivery_failed = [
|
|
180
|
-
"3710", # Annahmeverweigerung (refused)
|
|
181
|
-
"3715", # COD not paid
|
|
182
|
-
"3720", # Address not found
|
|
183
|
-
"3731", # Recipient not present (1st attempt)
|
|
184
|
-
"3732", # Recipient not present (2nd attempt)
|
|
185
|
-
"3733", # Recipient not present (3rd attempt)
|
|
186
|
-
"3734", # Recipient not present (4th attempt)
|
|
187
|
-
"3740", # Damage detected
|
|
188
|
-
"3750", # Tour cancellation
|
|
189
|
-
"3751", # Incorrect TAN (1st attempt)
|
|
190
|
-
"3752", # Incorrect TAN (2nd attempt)
|
|
191
|
-
"3753", # Incorrect TAN (3rd attempt)
|
|
192
|
-
"3754", # Incorrect TAN (4th attempt)
|
|
193
|
-
"3760", # Return shipment collected
|
|
194
|
-
"3761", # Return shipment taken
|
|
195
|
-
"3780", # Misdirected
|
|
196
|
-
"3782", # Ident failed - photo mismatch
|
|
197
|
-
"3783", # Ident failed - name mismatch
|
|
198
|
-
"3784", # Ident failed - DOB mismatch
|
|
199
|
-
"3785", # Ident failed - document mismatch
|
|
200
|
-
"3786", # Ident failed - PIN code
|
|
201
|
-
"3787", # Ident failed - age verification
|
|
202
|
-
"3795", # Shipment stopped
|
|
203
|
-
]
|
|
204
|
-
on_hold = [
|
|
205
|
-
"1730", # Held by customs
|
|
206
|
-
"1751", # Rejected by customs
|
|
207
|
-
"4100", # Sendung wird aufbewahrt (shipment stored)
|
|
208
|
-
"4500", # Stored (stocktaking)
|
|
209
|
-
"4610", # ParcelShop - high volume, cannot pick up
|
|
210
|
-
"4620", # ParcelShop - shipment not available
|
|
211
|
-
"4630", # ParcelShop - shipment not found
|
|
212
|
-
"4690", # Status corrected at ParcelShop
|
|
213
|
-
]
|
|
214
|
-
delivery_delayed = [
|
|
215
|
-
"4010", # Return - refused
|
|
216
|
-
"4015", # Return - COD not paid
|
|
217
|
-
"4020", # Return - address not found
|
|
218
|
-
"4024", # Return - too large/heavy for ParcelShop
|
|
219
|
-
"4025", # Shipment returned to depot
|
|
220
|
-
"4031", # Return - N1 (1st attempt failed)
|
|
221
|
-
"4032", # Return - N2 (2nd attempt failed)
|
|
222
|
-
"4033", # Return - N3 (3rd attempt failed)
|
|
223
|
-
"4034", # Return - N4 (4th attempt failed)
|
|
224
|
-
"4035", # Return - not collected from ParcelShop
|
|
225
|
-
"4040", # Return - damage
|
|
226
|
-
"4050", # Return - tour cancellation
|
|
227
|
-
"4051", # Return - TAN 1
|
|
228
|
-
"4052", # Return - TAN 2
|
|
229
|
-
"4053", # Return - TAN 3
|
|
230
|
-
"4054", # Return - TAN 4
|
|
231
|
-
"4060", # Return shipment received (pickup)
|
|
232
|
-
"4061", # Return shipment received (take-away)
|
|
233
|
-
"4062", # Return handed in at ParcelShop
|
|
234
|
-
"4070", # Tour departure cancelled
|
|
235
|
-
"4072", # Return cancelled (correction)
|
|
236
|
-
"4080", # Misdirected
|
|
237
|
-
"4081", # Ident failed
|
|
238
|
-
"4082", # Ident failed - photo
|
|
239
|
-
"4083", # Ident failed - name
|
|
240
|
-
"4084", # Ident failed - DOB
|
|
241
|
-
"4085", # Ident failed - document
|
|
242
|
-
"4086", # Ident failed - PIN
|
|
243
|
-
"4087", # Ident failed - age
|
|
244
|
-
"4095", # Delivery stopped
|
|
245
|
-
]
|
|
246
|
-
return_to_sender = [
|
|
247
|
-
"1520", # Return shipment sorted
|
|
248
|
-
"6080", # Rückversand (return shipment)
|
|
249
|
-
"6081", # Return - refused
|
|
250
|
-
"6082", # Return - address not readable
|
|
251
|
-
"6083", # Return - address not found
|
|
252
|
-
"6084", # Return - receiver not met
|
|
253
|
-
"6085", # Return - damage
|
|
254
|
-
"6086", # Return - sorting error
|
|
255
|
-
"6087", # Return - technical issue
|
|
256
|
-
"6088", # Redirected at receiver request
|
|
257
|
-
"6089", # Return - returns
|
|
258
|
-
"6090", # Forwarded to logistics center
|
|
259
|
-
"6092", # Return - ident failed
|
|
260
|
-
"6093", # Return - not collected from ParcelShop
|
|
261
|
-
"6094", # Return - COD not paid
|
|
262
|
-
"6096", # Return - too large/heavy for ParcelShop
|
|
263
|
-
"6098", # Return - incorrect TAN
|
|
264
|
-
"6099", # Return - delivery stopped
|
|
265
|
-
]
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
class TrackingIncidentReason(lib.Enum):
|
|
269
|
-
"""Maps Hermes exception codes to normalized incident reasons.
|
|
270
|
-
|
|
271
|
-
Based on Hermes Germany Eventcodes.csv.
|
|
272
|
-
Maps carrier-specific exception/status codes to standardized
|
|
273
|
-
incident reasons for tracking events.
|
|
274
|
-
"""
|
|
275
|
-
|
|
276
|
-
# Consignee-caused issues
|
|
277
|
-
consignee_refused = [
|
|
278
|
-
"3710", # Annahmeverweigerung
|
|
279
|
-
"4010", # Return - refused
|
|
280
|
-
"6081", # Return - refused
|
|
281
|
-
]
|
|
282
|
-
consignee_not_home = [
|
|
283
|
-
"3731", # Recipient not present (1st attempt)
|
|
284
|
-
"3732", # Recipient not present (2nd attempt)
|
|
285
|
-
"3733", # Recipient not present (3rd attempt)
|
|
286
|
-
"3734", # Recipient not present (4th attempt)
|
|
287
|
-
"4031", # Return - N1
|
|
288
|
-
"4032", # Return - N2
|
|
289
|
-
"4033", # Return - N3
|
|
290
|
-
"4034", # Return - N4
|
|
291
|
-
"6084", # Return - receiver not met
|
|
292
|
-
]
|
|
293
|
-
consignee_incorrect_address = [
|
|
294
|
-
"3720", # Address not found
|
|
295
|
-
"4020", # Return - address not found
|
|
296
|
-
"6082", # Return - address not readable
|
|
297
|
-
"6083", # Return - address not found
|
|
298
|
-
]
|
|
299
|
-
consignee_not_available = [
|
|
300
|
-
"4035", # Not collected from ParcelShop
|
|
301
|
-
"6093", # Return - not collected from ParcelShop
|
|
302
|
-
]
|
|
303
|
-
consignee_cod_unpaid = [
|
|
304
|
-
"3715", # COD not paid
|
|
305
|
-
"4015", # Return - COD not paid
|
|
306
|
-
"6094", # Return - COD not paid
|
|
307
|
-
]
|
|
308
|
-
consignee_id_failed = [
|
|
309
|
-
"3782", # Ident failed - photo mismatch
|
|
310
|
-
"3783", # Ident failed - name mismatch
|
|
311
|
-
"3784", # Ident failed - DOB mismatch
|
|
312
|
-
"3785", # Ident failed - document mismatch
|
|
313
|
-
"3786", # Ident failed - PIN code
|
|
314
|
-
"3787", # Ident failed - age verification
|
|
315
|
-
"4081", # Ident failed
|
|
316
|
-
"4082", # Ident failed - photo
|
|
317
|
-
"4083", # Ident failed - name
|
|
318
|
-
"4084", # Ident failed - DOB
|
|
319
|
-
"4085", # Ident failed - document
|
|
320
|
-
"4086", # Ident failed - PIN
|
|
321
|
-
"4087", # Ident failed - age
|
|
322
|
-
"6092", # Return - ident failed
|
|
323
|
-
]
|
|
324
|
-
consignee_tan_invalid = [
|
|
325
|
-
"3751", # Incorrect TAN (1st attempt)
|
|
326
|
-
"3752", # Incorrect TAN (2nd attempt)
|
|
327
|
-
"3753", # Incorrect TAN (3rd attempt)
|
|
328
|
-
"3754", # Incorrect TAN (4th attempt)
|
|
329
|
-
"4051", # Return - TAN 1
|
|
330
|
-
"4052", # Return - TAN 2
|
|
331
|
-
"4053", # Return - TAN 3
|
|
332
|
-
"4054", # Return - TAN 4
|
|
333
|
-
"6098", # Return - incorrect TAN
|
|
334
|
-
]
|
|
335
|
-
|
|
336
|
-
# Carrier-caused issues
|
|
337
|
-
carrier_damaged_parcel = [
|
|
338
|
-
"3740", # Damage detected
|
|
339
|
-
"4040", # Return - damage
|
|
340
|
-
"6085", # Return - damage
|
|
341
|
-
]
|
|
342
|
-
carrier_sorting_error = [
|
|
343
|
-
"3780", # Misdirected
|
|
344
|
-
"4080", # Misdirected
|
|
345
|
-
"6086", # Return - sorting error
|
|
346
|
-
"6087", # Return - technical issue
|
|
347
|
-
]
|
|
348
|
-
carrier_not_enough_time = [
|
|
349
|
-
"3750", # Tour cancellation
|
|
350
|
-
"4050", # Return - tour cancellation
|
|
351
|
-
"4070", # Tour departure cancelled
|
|
352
|
-
]
|
|
353
|
-
carrier_parcel_too_large = [
|
|
354
|
-
"4024", # Return - too large/heavy for ParcelShop
|
|
355
|
-
"6096", # Return - too large/heavy for ParcelShop
|
|
356
|
-
]
|
|
357
|
-
|
|
358
|
-
# Customs-related issues
|
|
359
|
-
customs_delay = [
|
|
360
|
-
"1730", # Held by customs
|
|
361
|
-
]
|
|
362
|
-
customs_rejected = [
|
|
363
|
-
"1751", # Rejected by customs
|
|
364
|
-
]
|
|
365
|
-
|
|
366
|
-
# Shipment stopped
|
|
367
|
-
shipment_stopped = [
|
|
368
|
-
"3795", # Shipment stopped
|
|
369
|
-
"4095", # Delivery stopped
|
|
370
|
-
"6099", # Return - delivery stopped
|
|
371
|
-
]
|
|
283
|
+
pending = ["0000"]
|
|
284
|
+
in_transit = ["1000", "2000"]
|
|
285
|
+
out_for_delivery = ["3000"]
|
|
286
|
+
delivered = ["3500"]
|
|
287
|
+
delivery_failed = ["4000", "4500"]
|
|
288
|
+
ready_for_pickup = ["5000"]
|
|
289
|
+
on_hold = ["6000"]
|
|
290
|
+
delivery_delayed = ["7000"]
|
|
372
291
|
|
|
373
292
|
|
|
374
293
|
class LabelType(lib.StrEnum):
|
|
@@ -418,18 +337,17 @@ def load_services_from_csv() -> list:
|
|
|
418
337
|
# Map carrier service code to karrio service code
|
|
419
338
|
karrio_service_code = ShippingService.map(service_code).name_or_key
|
|
420
339
|
|
|
340
|
+
row_min_weight = float(row["min_weight"]) if row.get("min_weight") else None
|
|
341
|
+
row_max_weight = float(row["max_weight"]) if row.get("max_weight") else None
|
|
342
|
+
|
|
421
343
|
# Initialize service if not exists
|
|
422
344
|
if karrio_service_code not in services_dict:
|
|
423
345
|
services_dict[karrio_service_code] = {
|
|
424
346
|
"service_name": service_name,
|
|
425
347
|
"service_code": karrio_service_code,
|
|
426
348
|
"currency": row.get("currency", "EUR"),
|
|
427
|
-
"min_weight":
|
|
428
|
-
|
|
429
|
-
),
|
|
430
|
-
"max_weight": (
|
|
431
|
-
float(row["max_weight"]) if row.get("max_weight") else None
|
|
432
|
-
),
|
|
349
|
+
"min_weight": row_min_weight,
|
|
350
|
+
"max_weight": row_max_weight,
|
|
433
351
|
"max_length": (
|
|
434
352
|
float(row["max_length"]) if row.get("max_length") else None
|
|
435
353
|
),
|
|
@@ -447,6 +365,15 @@ def load_services_from_csv() -> list:
|
|
|
447
365
|
),
|
|
448
366
|
"zones": [],
|
|
449
367
|
}
|
|
368
|
+
else:
|
|
369
|
+
# Update service-level weight bounds to cover all zones
|
|
370
|
+
current = services_dict[karrio_service_code]
|
|
371
|
+
if row_min_weight is not None:
|
|
372
|
+
if current["min_weight"] is None or row_min_weight < current["min_weight"]:
|
|
373
|
+
current["min_weight"] = row_min_weight
|
|
374
|
+
if row_max_weight is not None:
|
|
375
|
+
if current["max_weight"] is None or row_max_weight > current["max_weight"]:
|
|
376
|
+
current["max_weight"] = row_max_weight
|
|
450
377
|
|
|
451
378
|
# Parse country codes
|
|
452
379
|
country_codes = [
|
|
@@ -457,6 +384,8 @@ def load_services_from_csv() -> list:
|
|
|
457
384
|
zone = models.ServiceZone(
|
|
458
385
|
label=row.get("zone_label", "Default Zone"),
|
|
459
386
|
rate=float(row.get("rate", 0.0)),
|
|
387
|
+
min_weight=row_min_weight,
|
|
388
|
+
max_weight=row_max_weight,
|
|
460
389
|
transit_days=(
|
|
461
390
|
int(row["transit_days"].split("-")[0]) if row.get("transit_days") and row["transit_days"].split("-")[0].isdigit() else None
|
|
462
391
|
),
|
|
@@ -5,8 +5,8 @@ karrio/mappers/hermes/settings.py,sha256=4BTM7ncSQqTBP6J_oJTksBDGhaFHOWoHDnpq0iO
|
|
|
5
5
|
karrio/plugins/hermes/__init__.py,sha256=zUfOhtzuErd1H1ICL4rQt68e6BWRQf_B6_kW5UVzd9M,1020
|
|
6
6
|
karrio/providers/hermes/__init__.py,sha256=wMiVnPz7EgKdbu1pGBuvalfyuHj5QwSeMijjvEP6q4w,590
|
|
7
7
|
karrio/providers/hermes/error.py,sha256=YWmYC_l7_nyJCxiqM0Kgf6yV9IpJ04tkdZyyMT7QnkY,2974
|
|
8
|
-
karrio/providers/hermes/tracking.py,sha256=
|
|
9
|
-
karrio/providers/hermes/units.py,sha256=
|
|
8
|
+
karrio/providers/hermes/tracking.py,sha256=olKfHEVYZuwGL9e4348Ui-mAKBGr94FLF6VyxCwtZZg,5941
|
|
9
|
+
karrio/providers/hermes/units.py,sha256=8-_jGT_yjnBmZWcNBzbGD1AszxUIJ4v3apQjOfzW6N0,14275
|
|
10
10
|
karrio/providers/hermes/utils.py,sha256=NOfS_K0Zw7_wxJGBJfkdXwZP6hs0bsaZye5uypztGc4,4163
|
|
11
11
|
karrio/providers/hermes/pickup/__init__.py,sha256=E59ks-qJAsNmfQgaZ7X0tZuBetITsdRSMJUAZnqxabg,307
|
|
12
12
|
karrio/providers/hermes/pickup/cancel.py,sha256=JYVGL9decPB0QOHsjrYU5cwT_4vzrpsaEjapC9F3t7A,1587
|
|
@@ -21,9 +21,9 @@ karrio/schemas/hermes/pickup_create_request.py,sha256=0V14LMl0XVLOZ_zXRzoTLUZ8nB
|
|
|
21
21
|
karrio/schemas/hermes/pickup_create_response.py,sha256=sAgq92J0TZOUolhMcvDkp6qSRvxOqWCIZHNQWQN7KeU,397
|
|
22
22
|
karrio/schemas/hermes/shipment_request.py,sha256=FMqrSlCDG4eGDkuqPDc_DDCbtV_vGRyrRBFCiNTFkmM,7415
|
|
23
23
|
karrio/schemas/hermes/shipment_response.py,sha256=BI2kUemG-wTzAFpok9KEnlwjm6eK_LemBA_Cyga_e6A,3477
|
|
24
|
-
karrio/schemas/hermes/tracking_response.py,sha256=
|
|
25
|
-
karrio_hermes-2026.1.
|
|
26
|
-
karrio_hermes-2026.1.
|
|
27
|
-
karrio_hermes-2026.1.
|
|
28
|
-
karrio_hermes-2026.1.
|
|
29
|
-
karrio_hermes-2026.1.
|
|
24
|
+
karrio/schemas/hermes/tracking_response.py,sha256=6-bqTSl-N65PHl8cs1puOCFubzIZB55RveLdpNsmTEE,1967
|
|
25
|
+
karrio_hermes-2026.1.5.dist-info/METADATA,sha256=Ute2gZYW9m-tFogSK6_rWzfYnplNCXt63cvk1bzjhuk,982
|
|
26
|
+
karrio_hermes-2026.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
27
|
+
karrio_hermes-2026.1.5.dist-info/entry_points.txt,sha256=R8XDrYBqWXTPqaO0iTi4Buz8iQiiBiuHkJVNtZIXTkU,57
|
|
28
|
+
karrio_hermes-2026.1.5.dist-info/top_level.txt,sha256=9Nasa6abG7pPPG8MGzlemnqw1ohIqgouzQ7HGBnOFLg,27
|
|
29
|
+
karrio_hermes-2026.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|