karrio 2023.9.2__py3-none-any.whl → 2025.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- karrio/__init__.py +0 -100
- karrio/addons/renderer.py +1 -1
- karrio/api/gateway.py +58 -35
- karrio/api/interface.py +41 -4
- karrio/api/mapper.py +39 -0
- karrio/api/proxy.py +18 -5
- karrio/core/__init__.py +5 -1
- karrio/core/metadata.py +113 -20
- karrio/core/models.py +64 -5
- karrio/core/plugins.py +606 -0
- karrio/core/settings.py +39 -2
- karrio/core/units.py +574 -29
- karrio/core/utils/datetime.py +62 -2
- karrio/core/utils/dict.py +5 -0
- karrio/core/utils/enum.py +98 -13
- karrio/core/utils/helpers.py +83 -32
- karrio/core/utils/number.py +52 -8
- karrio/core/utils/string.py +52 -1
- karrio/core/utils/transformer.py +9 -4
- karrio/core/validators.py +88 -0
- karrio/lib.py +147 -2
- karrio/plugins/__init__.py +6 -0
- karrio/references.py +652 -67
- karrio/sdk.py +102 -0
- karrio/universal/mappers/rating_proxy.py +35 -9
- karrio/validators/__init__.py +6 -0
- {karrio-2023.9.2.dist-info → karrio-2025.5rc1.dist-info}/METADATA +9 -8
- karrio-2025.5rc1.dist-info/RECORD +57 -0
- {karrio-2023.9.2.dist-info → karrio-2025.5rc1.dist-info}/WHEEL +1 -1
- {karrio-2023.9.2.dist-info → karrio-2025.5rc1.dist-info}/top_level.txt +1 -0
- karrio-2023.9.2.dist-info/RECORD +0 -52
karrio/core/units.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Karrio universal data types and units definitions"""
|
2
2
|
|
3
|
+
from ctypes import util
|
3
4
|
import attr
|
4
5
|
import typing
|
5
6
|
import numbers
|
@@ -24,20 +25,20 @@ class PackagePreset:
|
|
24
25
|
packaging_type: str = None
|
25
26
|
|
26
27
|
|
27
|
-
class LabelType(utils.
|
28
|
+
class LabelType(utils.StrEnum):
|
28
29
|
PDF = "PDF"
|
29
30
|
ZPL = "ZPL"
|
30
31
|
PNG = "PNG"
|
31
32
|
|
32
33
|
|
33
|
-
class DocFormat(utils.
|
34
|
+
class DocFormat(utils.StrEnum):
|
34
35
|
gif = "GIF"
|
35
36
|
jpg = "JPG"
|
36
37
|
pdf = "PDF"
|
37
38
|
png = "PNG"
|
38
39
|
|
39
40
|
|
40
|
-
class PackagingUnit(utils.
|
41
|
+
class PackagingUnit(utils.StrEnum):
|
41
42
|
envelope = "Small Envelope"
|
42
43
|
pak = "Pak"
|
43
44
|
tube = "Tube"
|
@@ -47,19 +48,19 @@ class PackagingUnit(utils.Enum):
|
|
47
48
|
your_packaging = "Your Packaging"
|
48
49
|
|
49
50
|
|
50
|
-
class PaymentType(utils.
|
51
|
+
class PaymentType(utils.StrEnum):
|
51
52
|
sender = "SENDER"
|
52
53
|
recipient = "RECIPIENT"
|
53
54
|
third_party = "THIRD_PARTY"
|
54
55
|
|
55
56
|
|
56
|
-
class CreditCardType(utils.
|
57
|
+
class CreditCardType(utils.StrEnum):
|
57
58
|
visa = "Visa"
|
58
59
|
mastercard = "Mastercard"
|
59
60
|
american_express = "AmericanExpress"
|
60
61
|
|
61
62
|
|
62
|
-
class CustomsContentType(utils.
|
63
|
+
class CustomsContentType(utils.StrEnum):
|
63
64
|
documents = "DOCUMENTS"
|
64
65
|
gift = "GIFT"
|
65
66
|
sample = "SAMPLE"
|
@@ -68,13 +69,14 @@ class CustomsContentType(utils.Enum):
|
|
68
69
|
other = "OTHER"
|
69
70
|
|
70
71
|
|
71
|
-
class Incoterm(utils.
|
72
|
+
class Incoterm(utils.StrEnum):
|
72
73
|
"""universal international shipment incoterm (term of trades)"""
|
73
74
|
|
74
75
|
CFR = "Cost and Freight"
|
75
76
|
CIF = "Cost Insurance and Freight"
|
76
77
|
CIP = "Carriage and Insurance Paid"
|
77
78
|
CPT = "Carriage Paid To"
|
79
|
+
DAP = "Delivery at Place"
|
78
80
|
DAF = "Delivered at Frontier"
|
79
81
|
DDP = "Delivery Duty Paid"
|
80
82
|
DDU = "Delivery Duty Unpaid"
|
@@ -86,7 +88,7 @@ class Incoterm(utils.Enum):
|
|
86
88
|
FOB = "Free On Board"
|
87
89
|
|
88
90
|
|
89
|
-
class WeightUnit(utils.
|
91
|
+
class WeightUnit(utils.StrEnum):
|
90
92
|
"""universal weight units"""
|
91
93
|
|
92
94
|
KG = "KG"
|
@@ -95,13 +97,27 @@ class WeightUnit(utils.Enum):
|
|
95
97
|
G = "G"
|
96
98
|
|
97
99
|
|
98
|
-
class DimensionUnit(utils.
|
100
|
+
class DimensionUnit(utils.StrEnum):
|
99
101
|
"""universal dimension units"""
|
100
102
|
|
101
103
|
CM = "CM"
|
102
104
|
IN = "IN"
|
103
105
|
|
104
106
|
|
107
|
+
class VolumeUnit(utils.StrEnum):
|
108
|
+
"""universal dimension units"""
|
109
|
+
|
110
|
+
l = "l"
|
111
|
+
m3 = "m3"
|
112
|
+
i3 = "i3"
|
113
|
+
ft3 = "ft3"
|
114
|
+
cm3 = "cm3"
|
115
|
+
|
116
|
+
""" mapping from dimension units to volume units """
|
117
|
+
CM = "cm3"
|
118
|
+
IN = "i3"
|
119
|
+
|
120
|
+
|
105
121
|
class FreightClass(utils.Enum):
|
106
122
|
"""universal freight_class units"""
|
107
123
|
|
@@ -125,7 +141,7 @@ class FreightClass(utils.Enum):
|
|
125
141
|
freight_class_400 = 400
|
126
142
|
|
127
143
|
|
128
|
-
class UploadDocumentType(utils.
|
144
|
+
class UploadDocumentType(utils.StrEnum):
|
129
145
|
"""universal upload document types"""
|
130
146
|
|
131
147
|
certificate_of_origin = "certificate_of_origin"
|
@@ -136,6 +152,8 @@ class UploadDocumentType(utils.Enum):
|
|
136
152
|
|
137
153
|
|
138
154
|
class MeasurementOptionsType(typing.NamedTuple):
|
155
|
+
quant: typing.Optional[float] = None
|
156
|
+
|
139
157
|
min_in: typing.Optional[float] = None
|
140
158
|
min_cm: typing.Optional[float] = None
|
141
159
|
min_lb: typing.Optional[float] = None
|
@@ -148,7 +166,9 @@ class MeasurementOptionsType(typing.NamedTuple):
|
|
148
166
|
max_kg: typing.Optional[float] = None
|
149
167
|
max_oz: typing.Optional[float] = None
|
150
168
|
max_g: typing.Optional[float] = None
|
151
|
-
|
169
|
+
|
170
|
+
min_volume: typing.Optional[float] = None
|
171
|
+
max_volume: typing.Optional[float] = None
|
152
172
|
|
153
173
|
|
154
174
|
class CarrierCapabilities(utils.Enum):
|
@@ -157,6 +177,7 @@ class CarrierCapabilities(utils.Enum):
|
|
157
177
|
shipping = "shipping"
|
158
178
|
tracking = "tracking"
|
159
179
|
paperless = "paperless"
|
180
|
+
manifest = "manifest"
|
160
181
|
|
161
182
|
@classmethod
|
162
183
|
def get_capabilities(cls):
|
@@ -176,6 +197,8 @@ class CarrierCapabilities(utils.Enum):
|
|
176
197
|
return "shipping"
|
177
198
|
elif "document" in method_name:
|
178
199
|
return "paperless"
|
200
|
+
elif "manifest" in method_name:
|
201
|
+
return "manifest"
|
179
202
|
|
180
203
|
return None
|
181
204
|
|
@@ -260,24 +283,148 @@ class Volume:
|
|
260
283
|
"""The volume common processing helper"""
|
261
284
|
|
262
285
|
def __init__(
|
263
|
-
self,
|
286
|
+
self,
|
287
|
+
side1: Dimension = None,
|
288
|
+
side2: Dimension = None,
|
289
|
+
side3: Dimension = None,
|
290
|
+
value: float = None,
|
291
|
+
unit: typing.Union[VolumeUnit, str] = VolumeUnit.cm3,
|
292
|
+
options: MeasurementOptionsType = MeasurementOptionsType(),
|
264
293
|
):
|
265
294
|
self._side1 = side1
|
266
295
|
self._side2 = side2
|
267
296
|
self._side3 = side3
|
268
297
|
|
298
|
+
self._value = value
|
299
|
+
self._unit = VolumeUnit[unit] if isinstance(unit, str) else unit
|
300
|
+
|
301
|
+
self._quant = 0.01
|
302
|
+
self._min_volume = options.min_volume
|
303
|
+
|
304
|
+
def __getitem__(self, item):
|
305
|
+
return getattr(self, item)
|
306
|
+
|
307
|
+
def _compute(self, value: float):
|
308
|
+
below_min = self._min_volume is not None and value < self._min_volume
|
309
|
+
return utils.NF.decimal(
|
310
|
+
value=(self._min_volume if below_min else value),
|
311
|
+
quant=self._quant,
|
312
|
+
)
|
313
|
+
|
314
|
+
@property
|
315
|
+
def unit(self) -> str:
|
316
|
+
if self._unit is None:
|
317
|
+
return None
|
318
|
+
|
319
|
+
return self._unit.value
|
320
|
+
|
269
321
|
@property
|
270
322
|
def value(self):
|
271
|
-
|
323
|
+
missing_side_value = not all(
|
324
|
+
[
|
325
|
+
getattr(self._side1, "value", None),
|
326
|
+
getattr(self._side2, "value", None),
|
327
|
+
getattr(self._side3, "value", None),
|
328
|
+
]
|
329
|
+
)
|
330
|
+
missing_value = self._unit is None or self._value is None
|
331
|
+
|
332
|
+
if missing_side_value and missing_value:
|
272
333
|
return None
|
273
334
|
|
274
|
-
|
335
|
+
if not missing_value:
|
336
|
+
return self._value
|
337
|
+
|
338
|
+
return self._compute(self._side1.value * self._side2.value * self._side3.value)
|
275
339
|
|
276
340
|
@property
|
277
|
-
def
|
341
|
+
def l(self):
|
278
342
|
if self.value is None:
|
279
343
|
return None
|
280
|
-
|
344
|
+
if self._unit == VolumeUnit.m3:
|
345
|
+
return self._compute(self.value * 1000)
|
346
|
+
elif self._unit == VolumeUnit.i3:
|
347
|
+
return self._compute(self.value / 61.024)
|
348
|
+
elif self._unit == VolumeUnit.ft3:
|
349
|
+
return self._compute(self.value * 28.317)
|
350
|
+
if self._unit == VolumeUnit.cm3:
|
351
|
+
return self._compute(self.value / 1000)
|
352
|
+
else:
|
353
|
+
return self.value
|
354
|
+
|
355
|
+
@property
|
356
|
+
def m3(self):
|
357
|
+
if self.value is None:
|
358
|
+
return None
|
359
|
+
if self._unit == VolumeUnit.l:
|
360
|
+
return self._compute(self.value / 1000)
|
361
|
+
if self._unit == VolumeUnit.cm3:
|
362
|
+
return self._compute(self.value / 1e6)
|
363
|
+
elif self._unit == VolumeUnit.i3:
|
364
|
+
return self._compute(self.value / 61020)
|
365
|
+
elif self._unit == VolumeUnit.ft3:
|
366
|
+
return self._compute(self.value / 35.315)
|
367
|
+
else:
|
368
|
+
return self.value
|
369
|
+
|
370
|
+
@property
|
371
|
+
def i3(self):
|
372
|
+
if self.value is None:
|
373
|
+
return None
|
374
|
+
if self._unit == VolumeUnit.l:
|
375
|
+
return self._compute(self.value * 61.024)
|
376
|
+
if self._unit == VolumeUnit.m3:
|
377
|
+
return self._compute(self.value * 1000000)
|
378
|
+
elif self._unit == VolumeUnit.cm3:
|
379
|
+
return self._compute(self.value / 16.387)
|
380
|
+
elif self._unit == VolumeUnit.ft3:
|
381
|
+
return self._compute(self.value * 1728)
|
382
|
+
else:
|
383
|
+
return self.value
|
384
|
+
|
385
|
+
@property
|
386
|
+
def ft3(self):
|
387
|
+
if self.value is None:
|
388
|
+
return None
|
389
|
+
if self._unit == VolumeUnit.l:
|
390
|
+
return self._compute(self.value / 28.317)
|
391
|
+
if self._unit == VolumeUnit.m3:
|
392
|
+
return self._compute(self.value * 35.315)
|
393
|
+
elif self._unit == VolumeUnit.i3:
|
394
|
+
return self._compute(self.value / 1728)
|
395
|
+
elif self._unit == VolumeUnit.cm3:
|
396
|
+
return self._compute(self.value / 28320)
|
397
|
+
else:
|
398
|
+
return self.value
|
399
|
+
|
400
|
+
@property
|
401
|
+
def cm3(self):
|
402
|
+
if self.value is None:
|
403
|
+
return None
|
404
|
+
if self._unit == VolumeUnit.l:
|
405
|
+
return self._compute(self.value * 1000)
|
406
|
+
if self._unit == VolumeUnit.m3:
|
407
|
+
return self._compute(self.value * 1e6)
|
408
|
+
elif self._unit == VolumeUnit.i3:
|
409
|
+
return self._compute(self.value * 16.387)
|
410
|
+
elif self._unit == VolumeUnit.ft3:
|
411
|
+
return self._compute(self.value * 28320)
|
412
|
+
else:
|
413
|
+
return self.value
|
414
|
+
|
415
|
+
@property
|
416
|
+
def cubic_meter(self):
|
417
|
+
return self.m3
|
418
|
+
|
419
|
+
def map(self, options: MeasurementOptionsType):
|
420
|
+
return Volume(
|
421
|
+
side1=self._side1,
|
422
|
+
side2=self._side2,
|
423
|
+
side3=self._side3,
|
424
|
+
value=self._value,
|
425
|
+
unit=self._unit,
|
426
|
+
options=options,
|
427
|
+
)
|
281
428
|
|
282
429
|
|
283
430
|
class Girth:
|
@@ -458,6 +605,9 @@ class Products(typing.Iterable[Product]):
|
|
458
605
|
weight_unit: str = None,
|
459
606
|
):
|
460
607
|
self._items = [Product(item, weight_unit=weight_unit) for item in items]
|
608
|
+
self._weight_unit = (
|
609
|
+
weight_unit or self._items[0].weight_unit if any(self._items) else None
|
610
|
+
)
|
461
611
|
|
462
612
|
def __len__(self) -> int:
|
463
613
|
return len(self._items)
|
@@ -474,7 +624,30 @@ class Products(typing.Iterable[Product]):
|
|
474
624
|
|
475
625
|
@property
|
476
626
|
def value_amount(self):
|
477
|
-
return sum(
|
627
|
+
return sum(
|
628
|
+
(
|
629
|
+
item.value_amount * item.quantity
|
630
|
+
for item in self._items
|
631
|
+
if utils.NF.decimal(item.value_amount) is not None
|
632
|
+
),
|
633
|
+
0.0,
|
634
|
+
)
|
635
|
+
|
636
|
+
@property
|
637
|
+
def weight(self) -> Weight:
|
638
|
+
return Weight(
|
639
|
+
sum([item.weight * item.quantity for item in self._items], 0.0),
|
640
|
+
self._weight_unit,
|
641
|
+
)
|
642
|
+
|
643
|
+
@property
|
644
|
+
def description(self) -> typing.Optional[str]:
|
645
|
+
descriptions = set([item.description for item in self._items])
|
646
|
+
description: typing.Optional[str] = utils.SF.concat_str(
|
647
|
+
*list(descriptions), join=True
|
648
|
+
) # type:ignore
|
649
|
+
|
650
|
+
return description
|
478
651
|
|
479
652
|
|
480
653
|
class Package:
|
@@ -565,7 +738,9 @@ class Package:
|
|
565
738
|
|
566
739
|
@property
|
567
740
|
def volume(self) -> Volume:
|
568
|
-
return Volume(
|
741
|
+
return Volume(
|
742
|
+
self.width, self.length, self.height, unit=self.dimension_unit.value
|
743
|
+
)
|
569
744
|
|
570
745
|
@property
|
571
746
|
def thickness(self) -> Dimension:
|
@@ -603,6 +778,17 @@ class Package:
|
|
603
778
|
|
604
779
|
return Products(_items, self.weight_unit.value)
|
605
780
|
|
781
|
+
@property
|
782
|
+
def total_value(self) -> typing.Optional[float]:
|
783
|
+
if not any(self.parcel.items or []):
|
784
|
+
return None
|
785
|
+
|
786
|
+
return self.items.value_amount
|
787
|
+
|
788
|
+
@property
|
789
|
+
def reference_number(self) -> typing.Optional[str]:
|
790
|
+
return self.parcel.reference_number
|
791
|
+
|
606
792
|
|
607
793
|
class Packages(typing.Iterable[Package]):
|
608
794
|
"""The parcel collection common processing helper"""
|
@@ -695,11 +881,22 @@ class Packages(typing.Iterable[Package]):
|
|
695
881
|
return Weight(unit=unit, value=value)
|
696
882
|
|
697
883
|
@property
|
698
|
-
def volume(self) ->
|
884
|
+
def volume(self) -> Volume:
|
699
885
|
if not any([pkg.volume.value for pkg in self._items]):
|
700
|
-
return None
|
886
|
+
return Volume(value=None)
|
701
887
|
|
702
|
-
|
888
|
+
_, _dimension_unit = self._compatible_units
|
889
|
+
_volume_unit = VolumeUnit[_dimension_unit.name]
|
890
|
+
_total_volume = sum(
|
891
|
+
[
|
892
|
+
pkg.volume[_volume_unit.name]
|
893
|
+
for pkg in self._items
|
894
|
+
if pkg.volume is not None
|
895
|
+
],
|
896
|
+
0.0,
|
897
|
+
)
|
898
|
+
|
899
|
+
return Volume(value=_total_volume, unit=_volume_unit)
|
703
900
|
|
704
901
|
@property
|
705
902
|
def package_type(self) -> str:
|
@@ -722,6 +919,15 @@ class Packages(typing.Iterable[Package]):
|
|
722
919
|
|
723
920
|
return description
|
724
921
|
|
922
|
+
@property
|
923
|
+
def content(self) -> typing.Optional[str]:
|
924
|
+
contents = set([item.parcel.content for item in self._items])
|
925
|
+
content: typing.Optional[str] = utils.SF.concat_str(
|
926
|
+
*list(contents), join=True
|
927
|
+
) # type:ignore
|
928
|
+
|
929
|
+
return content
|
930
|
+
|
725
931
|
@property
|
726
932
|
def options(self) -> "ShippingOptions":
|
727
933
|
def merge_options(acc, pkg) -> dict:
|
@@ -776,6 +982,15 @@ class Packages(typing.Iterable[Package]):
|
|
776
982
|
|
777
983
|
return Products(_items, _weight_unit.value)
|
778
984
|
|
985
|
+
@property
|
986
|
+
def total_value(self) -> typing.Optional[float]:
|
987
|
+
if not any([_.total_value for _ in self._items]):
|
988
|
+
return None
|
989
|
+
|
990
|
+
return sum(
|
991
|
+
[pkg.total_value for pkg in self._items if pkg.total_value is not None], 0.0
|
992
|
+
)
|
993
|
+
|
779
994
|
def validate(self, required: typing.List[str] = None, max_weight: Weight = None):
|
780
995
|
required = required or self._required
|
781
996
|
max_weight = max_weight or self._max_weight
|
@@ -853,6 +1068,7 @@ class Options:
|
|
853
1068
|
_key = key
|
854
1069
|
option_values[key] = _val
|
855
1070
|
|
1071
|
+
self._raw_options = options
|
856
1072
|
self._options = option_values
|
857
1073
|
self._option_list = self._filter(
|
858
1074
|
option_values, (items_filter or utils.identity)
|
@@ -894,10 +1110,10 @@ class ShippingOption(utils.Enum):
|
|
894
1110
|
"""universal shipment options (special services)"""
|
895
1111
|
|
896
1112
|
currency = utils.OptionEnum("currency")
|
1113
|
+
is_return = utils.OptionEnum("is_return", bool)
|
897
1114
|
insurance = utils.OptionEnum("insurance", float)
|
898
1115
|
cash_on_delivery = utils.OptionEnum("COD", float)
|
899
1116
|
shipment_note = utils.OptionEnum("shipment_note")
|
900
|
-
shipment_date = utils.OptionEnum("shipment_date")
|
901
1117
|
dangerous_good = utils.OptionEnum("dangerous_good", bool)
|
902
1118
|
declared_value = utils.OptionEnum("declared_value", float)
|
903
1119
|
paperless_trade = utils.OptionEnum("paperless_trade", bool)
|
@@ -906,6 +1122,7 @@ class ShippingOption(utils.Enum):
|
|
906
1122
|
email_notification_to = utils.OptionEnum("email_notification_to")
|
907
1123
|
signature_confirmation = utils.OptionEnum("signature_confirmation", bool)
|
908
1124
|
saturday_delivery = utils.OptionEnum("saturday_delivery", bool)
|
1125
|
+
sunday_delivery = utils.OptionEnum("sunday_delivery", bool)
|
909
1126
|
doc_files = utils.OptionEnum("doc_files", utils.DP.to_dict)
|
910
1127
|
doc_references = utils.OptionEnum("doc_references", utils.DP.to_dict)
|
911
1128
|
hold_at_location = utils.OptionEnum("hold_at_location", bool)
|
@@ -913,6 +1130,14 @@ class ShippingOption(utils.Enum):
|
|
913
1130
|
"hold_at_location_address",
|
914
1131
|
functools.partial(utils.DP.to_object, models.Address),
|
915
1132
|
)
|
1133
|
+
shipper_instructions = utils.OptionEnum("shipper_instructions")
|
1134
|
+
recipient_instructions = utils.OptionEnum("recipient_instructions")
|
1135
|
+
|
1136
|
+
"""TODO: dreprecate these"""
|
1137
|
+
shipment_date = utils.OptionEnum("shipment_date")
|
1138
|
+
|
1139
|
+
"""TODO: standardize to these"""
|
1140
|
+
shipping_date = utils.OptionEnum("shipping_date") # format: %Y-%m-%dT%H:%M
|
916
1141
|
|
917
1142
|
|
918
1143
|
class ShippingOptions(Options):
|
@@ -958,8 +1183,22 @@ class ShippingOptions(Options):
|
|
958
1183
|
|
959
1184
|
@property
|
960
1185
|
def shipment_date(self) -> utils.OptionEnum:
|
1186
|
+
# Check if shipment_date is not defined and fallback to shipping_date
|
1187
|
+
if not self[ShippingOption.shipment_date.name].state:
|
1188
|
+
return utils.OptionEnum(
|
1189
|
+
"shipment_date",
|
1190
|
+
str,
|
1191
|
+
utils.DF.fdate(
|
1192
|
+
self._raw_options.get("shipping_date"), "%Y-%m-%dT%H:%M"
|
1193
|
+
),
|
1194
|
+
)
|
1195
|
+
|
961
1196
|
return self[ShippingOption.shipment_date.name]
|
962
1197
|
|
1198
|
+
@property
|
1199
|
+
def shipping_date(self) -> utils.OptionEnum:
|
1200
|
+
return self[ShippingOption.shipping_date.name]
|
1201
|
+
|
963
1202
|
@property
|
964
1203
|
def signature_confirmation(self) -> utils.OptionEnum:
|
965
1204
|
return self[ShippingOption.signature_confirmation.name]
|
@@ -984,19 +1223,63 @@ class ShippingOptions(Options):
|
|
984
1223
|
def shipment_note(self) -> utils.OptionEnum:
|
985
1224
|
return self[ShippingOption.shipment_note.name]
|
986
1225
|
|
1226
|
+
@property
|
1227
|
+
def shipper_instructions(self) -> utils.OptionEnum:
|
1228
|
+
return self[ShippingOption.shipper_instructions.name]
|
1229
|
+
|
1230
|
+
@property
|
1231
|
+
def recipient_instructions(self) -> utils.OptionEnum:
|
1232
|
+
return self[ShippingOption.recipient_instructions.name]
|
1233
|
+
|
987
1234
|
|
988
1235
|
class CustomsOption(utils.Enum):
|
989
1236
|
"""common shipment customs identifiers"""
|
990
1237
|
|
991
1238
|
aes = utils.OptionEnum("aes")
|
1239
|
+
ioss = utils.OptionEnum("ioss")
|
992
1240
|
eel_pfc = utils.OptionEnum("eel_pfc")
|
993
|
-
nip_number = utils.OptionEnum("
|
1241
|
+
nip_number = utils.OptionEnum("nip_number")
|
994
1242
|
eori_number = utils.OptionEnum("eori_number")
|
995
1243
|
license_number = utils.OptionEnum("license_number")
|
996
1244
|
certificate_number = utils.OptionEnum("certificate_number")
|
997
1245
|
vat_registration_number = utils.OptionEnum("vat_registration_number")
|
998
1246
|
|
999
1247
|
|
1248
|
+
class CustomsOptions(Options):
|
1249
|
+
"""The options common processing helper"""
|
1250
|
+
|
1251
|
+
def __init__(self, *args, **kwargs):
|
1252
|
+
super().__init__(*args, **kwargs, base_option_type=CustomsOption)
|
1253
|
+
|
1254
|
+
@property
|
1255
|
+
def aes(self) -> utils.OptionEnum:
|
1256
|
+
return self[CustomsOption.aes.name]
|
1257
|
+
|
1258
|
+
@property
|
1259
|
+
def eel_pfc(self) -> utils.OptionEnum:
|
1260
|
+
return self[CustomsOption.eel_pfc.name]
|
1261
|
+
|
1262
|
+
@property
|
1263
|
+
def nip_number(self) -> utils.OptionEnum:
|
1264
|
+
return self[CustomsOption.nip_number.name]
|
1265
|
+
|
1266
|
+
@property
|
1267
|
+
def eori_number(self) -> utils.OptionEnum:
|
1268
|
+
return self[CustomsOption.eori_number.name]
|
1269
|
+
|
1270
|
+
@property
|
1271
|
+
def license_number(self) -> utils.OptionEnum:
|
1272
|
+
return self[CustomsOption.license_number.name]
|
1273
|
+
|
1274
|
+
@property
|
1275
|
+
def certificate_number(self) -> utils.OptionEnum:
|
1276
|
+
return self[CustomsOption.certificate_number.name]
|
1277
|
+
|
1278
|
+
@property
|
1279
|
+
def vat_registration_number(self) -> utils.OptionEnum:
|
1280
|
+
return self[CustomsOption.vat_registration_number.name]
|
1281
|
+
|
1282
|
+
|
1000
1283
|
class CustomsInfo(models.Customs):
|
1001
1284
|
"""The customs info processing helper"""
|
1002
1285
|
|
@@ -1010,10 +1293,9 @@ class CustomsInfo(models.Customs):
|
|
1010
1293
|
recipient: typing.Optional[models.Address] = None,
|
1011
1294
|
):
|
1012
1295
|
_customs = customs or default_to
|
1013
|
-
options =
|
1296
|
+
options = CustomsOptions(
|
1014
1297
|
getattr(_customs, "options", None) or {},
|
1015
1298
|
option_type=option_type,
|
1016
|
-
base_option_type=CustomsOption,
|
1017
1299
|
)
|
1018
1300
|
|
1019
1301
|
self._customs = _customs
|
@@ -1036,7 +1318,7 @@ class CustomsInfo(models.Customs):
|
|
1036
1318
|
return self._customs is not None
|
1037
1319
|
|
1038
1320
|
@property
|
1039
|
-
def duty(self) ->
|
1321
|
+
def duty(self) -> models.Duty: # type:ignore
|
1040
1322
|
return getattr(self._customs, "duty", None) or models.Duty()
|
1041
1323
|
|
1042
1324
|
@property
|
@@ -1213,7 +1495,6 @@ class ComputedAddress(models.Address):
|
|
1213
1495
|
join=True,
|
1214
1496
|
),
|
1215
1497
|
)
|
1216
|
-
return self.address.address_line1.replace(self.street_number, "").strip()
|
1217
1498
|
|
1218
1499
|
@property
|
1219
1500
|
def tax_id(self) -> typing.Optional[str]:
|
@@ -1246,6 +1527,20 @@ class ComputedAddress(models.Address):
|
|
1246
1527
|
self.address, "company_name", None
|
1247
1528
|
)
|
1248
1529
|
|
1530
|
+
@property
|
1531
|
+
def first_name(self) -> typing.Optional[str]:
|
1532
|
+
if self.address.person_name is None:
|
1533
|
+
return None
|
1534
|
+
|
1535
|
+
return self.address.person_name.split(" ")[0]
|
1536
|
+
|
1537
|
+
@property
|
1538
|
+
def last_name(self) -> typing.Optional[str]:
|
1539
|
+
if self.address.person_name is None:
|
1540
|
+
return None
|
1541
|
+
|
1542
|
+
return self.address.person_name.split(" ")[-1]
|
1543
|
+
|
1249
1544
|
def _compute_address_line(self, join: bool = True) -> typing.Optional[str]:
|
1250
1545
|
if any(
|
1251
1546
|
[
|
@@ -1264,7 +1559,7 @@ class ComputedAddress(models.Address):
|
|
1264
1559
|
def _compute_street_number(self):
|
1265
1560
|
_value = getattr(self.address, "street_number", None)
|
1266
1561
|
|
1267
|
-
if _value is None:
|
1562
|
+
if _value is None and self.address:
|
1268
1563
|
words = self.address.address_line1.split(" ")
|
1269
1564
|
|
1270
1565
|
if any(_.isdigit() for _ in words[0]):
|
@@ -1293,16 +1588,20 @@ class ComputedDocumentFile(models.DocumentFile):
|
|
1293
1588
|
|
1294
1589
|
|
1295
1590
|
class TrackingStatus(utils.Enum):
|
1591
|
+
pending = ["pending"]
|
1296
1592
|
on_hold = ["on_hold"]
|
1593
|
+
cancelled = ["cancelled"]
|
1297
1594
|
delivered = ["delivered"]
|
1298
1595
|
in_transit = ["in_transit"]
|
1299
1596
|
delivery_failed = ["delivery_failed"]
|
1300
1597
|
delivery_delayed = ["delivery_delayed"]
|
1301
1598
|
out_for_delivery = ["out_for_delivery"]
|
1302
1599
|
ready_for_pickup = ["ready_for_pickup"]
|
1600
|
+
return_to_sender = ["return_to_sender"]
|
1601
|
+
unknown = ["unknown"]
|
1303
1602
|
|
1304
1603
|
|
1305
|
-
def create_enum(name, values):
|
1604
|
+
def create_enum(name, values) -> utils.Enum:
|
1306
1605
|
return utils.Enum(name, values) # type: ignore
|
1307
1606
|
|
1308
1607
|
|
@@ -1455,6 +1754,7 @@ class Currency(utils.Enum):
|
|
1455
1754
|
|
1456
1755
|
|
1457
1756
|
class Country(utils.Enum):
|
1757
|
+
AC = "Ascension Island"
|
1458
1758
|
AD = "Andorra"
|
1459
1759
|
AE = "United Arab Emirates"
|
1460
1760
|
AF = "Afghanistan"
|
@@ -1689,9 +1989,17 @@ class Country(utils.Enum):
|
|
1689
1989
|
ZA = "South Africa"
|
1690
1990
|
ZM = "Zambia"
|
1691
1991
|
ZW = "Zimbabwe"
|
1992
|
+
# Adding missing country codes
|
1993
|
+
EH = "Western Sahara"
|
1994
|
+
IM = "Isle of Man"
|
1995
|
+
BL = "Saint Barthelemy"
|
1996
|
+
MF = "Saint Martin"
|
1997
|
+
SX = "Sint Maarten"
|
1998
|
+
XK = "Kosovo"
|
1692
1999
|
|
1693
2000
|
|
1694
2001
|
class CountryCurrency(utils.Enum):
|
2002
|
+
AC = "USD"
|
1695
2003
|
AD = "EUR"
|
1696
2004
|
AE = "AED"
|
1697
2005
|
AF = "USD"
|
@@ -2419,3 +2727,240 @@ class EUCountry(utils.Enum):
|
|
2419
2727
|
SK = "Slovakia"
|
2420
2728
|
ES = "Spain"
|
2421
2729
|
SE = "Sweden"
|
2730
|
+
|
2731
|
+
|
2732
|
+
class CountryCode(utils.Enum):
|
2733
|
+
AD = "AND" # Andorra
|
2734
|
+
AE = "ARE" # United Arab Emirates
|
2735
|
+
AF = "AFG" # Afghanistan
|
2736
|
+
AG = "ATG" # Antigua
|
2737
|
+
AI = "AIA" # Anguilla
|
2738
|
+
AL = "ALB" # Albania
|
2739
|
+
AM = "ARM" # Armenia
|
2740
|
+
AN = "ANT" # Netherlands Antilles
|
2741
|
+
AO = "AGO" # Angola
|
2742
|
+
AR = "ARG" # Argentina
|
2743
|
+
AS = "ASM" # American Samoa
|
2744
|
+
AT = "AUT" # Austria
|
2745
|
+
AU = "AUS" # Australia
|
2746
|
+
AW = "ABW" # Aruba
|
2747
|
+
AZ = "AZE" # Azerbaijan
|
2748
|
+
BA = "BIH" # Bosnia And Herzegovina
|
2749
|
+
BB = "BRB" # Barbados
|
2750
|
+
BD = "BGD" # Bangladesh
|
2751
|
+
BE = "BEL" # Belgium
|
2752
|
+
BF = "BFA" # Burkina Faso
|
2753
|
+
BG = "BGR" # Bulgaria
|
2754
|
+
BH = "BHR" # Bahrain
|
2755
|
+
BI = "BDI" # Burundi
|
2756
|
+
BJ = "BEN" # Benin
|
2757
|
+
BM = "BMU" # Bermuda
|
2758
|
+
BN = "BRN" # Brunei
|
2759
|
+
BO = "BOL" # Bolivia
|
2760
|
+
BR = "BRA" # Brazil
|
2761
|
+
BS = "BHS" # Bahamas
|
2762
|
+
BT = "BTN" # Bhutan
|
2763
|
+
BW = "BWA" # Botswana
|
2764
|
+
BY = "BLR" # Belarus
|
2765
|
+
BZ = "BLZ" # Belize
|
2766
|
+
CA = "CAN" # Canada
|
2767
|
+
CD = "COD" # Congo, Democratic Republic of the
|
2768
|
+
CF = "CAF" # Central African Republic
|
2769
|
+
CG = "COG" # Congo
|
2770
|
+
CH = "CHE" # Switzerland
|
2771
|
+
CI = "CIV" # Cote D Ivoire
|
2772
|
+
CK = "COK" # Cook Islands
|
2773
|
+
CL = "CHL" # Chile
|
2774
|
+
CM = "CMR" # Cameroon
|
2775
|
+
CN = "CHN" # China
|
2776
|
+
CO = "COL" # Colombia
|
2777
|
+
CR = "CRI" # Costa Rica
|
2778
|
+
CU = "CUB" # Cuba
|
2779
|
+
CV = "CPV" # Cape Verde
|
2780
|
+
CY = "CYP" # Cyprus
|
2781
|
+
CZ = "CZE" # Czech Republic
|
2782
|
+
DE = "DEU" # Germany
|
2783
|
+
DJ = "DJI" # Djibouti
|
2784
|
+
DK = "DNK" # Denmark
|
2785
|
+
DM = "DMA" # Dominica
|
2786
|
+
DO = "DOM" # Dominican Republic
|
2787
|
+
DZ = "DZA" # Algeria
|
2788
|
+
EC = "ECU" # Ecuador
|
2789
|
+
EE = "EST" # Estonia
|
2790
|
+
EG = "EGY" # Egypt
|
2791
|
+
ER = "ERI" # Eritrea
|
2792
|
+
ES = "ESP" # Spain
|
2793
|
+
ET = "ETH" # Ethiopia
|
2794
|
+
FI = "FIN" # Finland
|
2795
|
+
FJ = "FJI" # Fiji
|
2796
|
+
FK = "FLK" # Falkland Islands
|
2797
|
+
FM = "FSM" # Micronesia, Federated States Of
|
2798
|
+
FO = "FRO" # Faroe Islands
|
2799
|
+
FR = "FRA" # France
|
2800
|
+
GA = "GAB" # Gabon
|
2801
|
+
GB = "GBR" # United Kingdom
|
2802
|
+
GD = "GRD" # Grenada
|
2803
|
+
GE = "GEO" # Georgia
|
2804
|
+
GF = "GUF" # French Guyana
|
2805
|
+
GG = "GGY" # Guernsey
|
2806
|
+
GH = "GHA" # Ghana
|
2807
|
+
GI = "GIB" # Gibraltar
|
2808
|
+
GL = "GRL" # Greenland
|
2809
|
+
GM = "GMB" # Gambia
|
2810
|
+
GN = "GIN" # Guinea Republic
|
2811
|
+
GP = "GLP" # Guadeloupe
|
2812
|
+
GQ = "GNQ" # Guinea-equatorial
|
2813
|
+
GR = "GRC" # Greece
|
2814
|
+
GT = "GTM" # Guatemala
|
2815
|
+
GU = "GUM" # Guam
|
2816
|
+
GW = "GNB" # Guinea-bissau
|
2817
|
+
GY = "GUY" # Guyana (british)
|
2818
|
+
HK = "HKG" # Hong Kong
|
2819
|
+
HN = "HND" # Honduras
|
2820
|
+
HR = "HRV" # Croatia
|
2821
|
+
HT = "HTI" # Haiti
|
2822
|
+
HU = "HUN" # Hungary
|
2823
|
+
IC = "ICA" # Canary Islands
|
2824
|
+
ID = "IDN" # Indonesia
|
2825
|
+
IE = "IRL" # Ireland
|
2826
|
+
IL = "ISR" # Israel
|
2827
|
+
IN = "IND" # India
|
2828
|
+
IQ = "IRQ" # Iraq
|
2829
|
+
IR = "IRN" # Iran
|
2830
|
+
IS = "ISL" # Iceland
|
2831
|
+
IT = "ITA" # Italy
|
2832
|
+
JE = "JEY" # Jersey
|
2833
|
+
JM = "JAM" # Jamaica
|
2834
|
+
JO = "JOR" # Jordan
|
2835
|
+
JP = "JPN" # Japan
|
2836
|
+
KE = "KEN" # Kenya
|
2837
|
+
KG = "KGZ" # Kyrgyzstan
|
2838
|
+
KH = "KHM" # Cambodia
|
2839
|
+
KI = "KIR" # Kiribati
|
2840
|
+
KM = "COM" # Comoros
|
2841
|
+
KN = "KNA" # St. Kitts
|
2842
|
+
KP = "PRK" # Korea, The D.p.r Of (north K.)
|
2843
|
+
KR = "KOR" # Korea, Republic of (South Korea)
|
2844
|
+
KV = "XKX" # Kosovo
|
2845
|
+
KW = "KWT" # Kuwait
|
2846
|
+
KY = "CYM" # Cayman Islands
|
2847
|
+
KZ = "KAZ" # Kazakhstan
|
2848
|
+
LA = "LAO" # Lao Peoples Democratic Republic
|
2849
|
+
LB = "LBN" # Lebanon
|
2850
|
+
LC = "LCA" # St. Lucia
|
2851
|
+
LI = "LIE" # Liechtenstein
|
2852
|
+
LK = "LKA" # Sri Lanka
|
2853
|
+
LR = "LBR" # Liberia
|
2854
|
+
LS = "LSO" # Lesotho
|
2855
|
+
LT = "LTU" # Lithuania
|
2856
|
+
LU = "LUX" # Luxembourg
|
2857
|
+
LV = "LVA" # Latvia
|
2858
|
+
LY = "LBY" # Libya
|
2859
|
+
MA = "MAR" # Morocco
|
2860
|
+
MC = "MCO" # Monaco
|
2861
|
+
MD = "MDA" # Moldova
|
2862
|
+
ME = "MNE" # Montenegro
|
2863
|
+
MG = "MDG" # Madagascar
|
2864
|
+
MH = "MHL" # Marshall Islands
|
2865
|
+
MK = "MKD" # Macedonia
|
2866
|
+
ML = "MLI" # Mali
|
2867
|
+
MM = "MMR" # Myanmar
|
2868
|
+
MN = "MNG" # Mongolia
|
2869
|
+
MO = "MAC" # Macau
|
2870
|
+
MP = "MNP" # Nothern Mariana Islands, Commonwealth of
|
2871
|
+
MQ = "MTQ" # Martinique
|
2872
|
+
MR = "MRT" # Mauritania
|
2873
|
+
MS = "MSR" # Montserrat
|
2874
|
+
MT = "MLT" # Malta
|
2875
|
+
MU = "MUS" # Mauritius
|
2876
|
+
MV = "MDV" # Maldives
|
2877
|
+
MW = "MWI" # Malawi
|
2878
|
+
MX = "MEX" # Mexico
|
2879
|
+
MY = "MYS" # Malaysia
|
2880
|
+
MZ = "MOZ" # Mozambique
|
2881
|
+
NA = "NAM" # Namibia
|
2882
|
+
NC = "NCL" # New Caledonia
|
2883
|
+
NE = "NER" # Niger
|
2884
|
+
NG = "NGA" # Nigeria
|
2885
|
+
NI = "NIC" # Nicaragua
|
2886
|
+
NL = "NLD" # Netherlands
|
2887
|
+
NO = "NOR" # Norway
|
2888
|
+
NP = "NPL" # Nepal
|
2889
|
+
NR = "NRU" # Nauru
|
2890
|
+
NU = "NIU" # Niue
|
2891
|
+
NZ = "NZL" # New Zealand
|
2892
|
+
OM = "OMN" # Oman
|
2893
|
+
PA = "PAN" # Panama
|
2894
|
+
PE = "PER" # Peru
|
2895
|
+
PF = "PYF" # Tahiti
|
2896
|
+
PG = "PNG" # Papua New Guinea
|
2897
|
+
PH = "PHL" # Philippines
|
2898
|
+
PK = "PAK" # Pakistan
|
2899
|
+
PL = "POL" # Poland
|
2900
|
+
PR = "PRI" # Puerto Rico
|
2901
|
+
PT = "PRT" # Portugal
|
2902
|
+
PW = "PLW" # Palau
|
2903
|
+
PY = "PRY" # Paraguay
|
2904
|
+
QA = "QAT" # Qatar
|
2905
|
+
RE = "REU" # Reunion
|
2906
|
+
RO = "ROU" # Romania
|
2907
|
+
RS = "SRB" # Serbia
|
2908
|
+
RU = "RUS" # Russia
|
2909
|
+
RW = "RWA" # Rwanda
|
2910
|
+
SA = "SAU" # Saudi Arabia
|
2911
|
+
SB = "SLB" # Solomon Islands
|
2912
|
+
SC = "SYC" # Seychelles
|
2913
|
+
SD = "SDN" # Sudan
|
2914
|
+
SE = "SWE" # Sweden
|
2915
|
+
SG = "SGP" # Singapore
|
2916
|
+
SH = "SHN" # Saint Helena
|
2917
|
+
SI = "SVN" # Slovenia
|
2918
|
+
SK = "SVK" # Slovakia
|
2919
|
+
SL = "SLE" # Sierra Leone
|
2920
|
+
SM = "SMR" # San Marino
|
2921
|
+
SN = "SEN" # Senegal
|
2922
|
+
SO = "SOM" # Somalia
|
2923
|
+
SR = "SUR" # Suriname
|
2924
|
+
SS = "SSD" # South Sudan
|
2925
|
+
ST = "STP" # Sao Tome And Principe
|
2926
|
+
SV = "SLV" # El Salvador
|
2927
|
+
SY = "SYR" # Syria
|
2928
|
+
SZ = "SWZ" # Swaziland
|
2929
|
+
TC = "TCA" # Turks And Caicos Islands
|
2930
|
+
TD = "TCD" # Chad
|
2931
|
+
TG = "TGO" # Togo
|
2932
|
+
TH = "THA" # Thailand
|
2933
|
+
TJ = "TJK" # Tajikistan
|
2934
|
+
TL = "TLS" # Timor Leste
|
2935
|
+
TN = "TUN" # Tunisia
|
2936
|
+
TO = "TON" # Tonga
|
2937
|
+
TR = "TUR" # Turkey
|
2938
|
+
TT = "TTO" # Trinidad And Tobago
|
2939
|
+
TV = "TUV" # Tuvalu
|
2940
|
+
TW = "TWN" # Taiwan
|
2941
|
+
TZ = "TZA" # Tanzania
|
2942
|
+
UA = "UKR" # Ukraine
|
2943
|
+
UG = "UGA" # Uganda
|
2944
|
+
US = "USA" # United States
|
2945
|
+
UY = "URY" # Uruguay
|
2946
|
+
UZ = "UZB" # Uzbekistan
|
2947
|
+
VA = "VAT" # Vatican City
|
2948
|
+
VC = "VCT" # St. Vincent
|
2949
|
+
VE = "VEN" # Venezuela
|
2950
|
+
VG = "VGB" # British Virgin Islands
|
2951
|
+
VI = "VIR" # U.S. Virgin Islands
|
2952
|
+
VN = "VNM" # Vietnam
|
2953
|
+
VU = "VUT" # Vanuatu
|
2954
|
+
WS = "WSM" # Samoa
|
2955
|
+
XB = "BES" # Bonaire
|
2956
|
+
XC = "CUW" # Curacao
|
2957
|
+
XE = "EUX" # St. Eustatius
|
2958
|
+
XM = "SXM" # St. Maarten
|
2959
|
+
XN = "KNA" # Nevis
|
2960
|
+
XS = "SOM" # Somaliland, Rep Of (north Somalia)
|
2961
|
+
XY = "BLM" # St. Barthelemy
|
2962
|
+
YE = "YEM" # Yemen
|
2963
|
+
YT = "MYT" # Mayotte
|
2964
|
+
ZA = "ZAF" # South Africa
|
2965
|
+
ZM = "ZMB" # Zambia
|
2966
|
+
ZW = "ZWE" # Zimbabwe
|