karrio 2023.5.1__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/errors.py +6 -5
- karrio/core/metadata.py +113 -20
- karrio/core/models.py +66 -5
- karrio/core/plugins.py +606 -0
- karrio/core/settings.py +39 -2
- karrio/core/units.py +639 -32
- karrio/core/utils/__init__.py +1 -1
- karrio/core/utils/datetime.py +75 -13
- karrio/core/utils/dict.py +5 -0
- karrio/core/utils/enum.py +132 -34
- karrio/core/utils/helpers.py +92 -35
- karrio/core/utils/number.py +52 -8
- karrio/core/utils/string.py +52 -1
- karrio/core/utils/transformer.py +12 -5
- karrio/core/validators.py +88 -0
- karrio/lib.py +241 -15
- karrio/plugins/__init__.py +6 -0
- karrio/references.py +652 -67
- karrio/schemas/__init__.py +2 -0
- karrio/sdk.py +102 -0
- karrio/universal/mappers/rating_proxy.py +35 -9
- karrio/validators/__init__.py +6 -0
- {karrio-2023.5.1.dist-info → karrio-2025.5rc1.dist-info}/METADATA +13 -15
- karrio-2025.5rc1.dist-info/RECORD +57 -0
- {karrio-2023.5.1.dist-info → karrio-2025.5rc1.dist-info}/WHEEL +1 -1
- {karrio-2023.5.1.dist-info → karrio-2025.5rc1.dist-info}/top_level.txt +1 -0
- karrio-2023.5.1.dist-info/RECORD +0 -51
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,20 +88,36 @@ 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"
|
93
95
|
LB = "LB"
|
96
|
+
OZ = "OZ"
|
97
|
+
G = "G"
|
94
98
|
|
95
99
|
|
96
|
-
class DimensionUnit(utils.
|
100
|
+
class DimensionUnit(utils.StrEnum):
|
97
101
|
"""universal dimension units"""
|
98
102
|
|
99
103
|
CM = "CM"
|
100
104
|
IN = "IN"
|
101
105
|
|
102
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
|
+
|
103
121
|
class FreightClass(utils.Enum):
|
104
122
|
"""universal freight_class units"""
|
105
123
|
|
@@ -123,7 +141,7 @@ class FreightClass(utils.Enum):
|
|
123
141
|
freight_class_400 = 400
|
124
142
|
|
125
143
|
|
126
|
-
class UploadDocumentType(utils.
|
144
|
+
class UploadDocumentType(utils.StrEnum):
|
127
145
|
"""universal upload document types"""
|
128
146
|
|
129
147
|
certificate_of_origin = "certificate_of_origin"
|
@@ -134,17 +152,23 @@ class UploadDocumentType(utils.Enum):
|
|
134
152
|
|
135
153
|
|
136
154
|
class MeasurementOptionsType(typing.NamedTuple):
|
155
|
+
quant: typing.Optional[float] = None
|
156
|
+
|
137
157
|
min_in: typing.Optional[float] = None
|
138
158
|
min_cm: typing.Optional[float] = None
|
139
159
|
min_lb: typing.Optional[float] = None
|
140
160
|
min_kg: typing.Optional[float] = None
|
141
161
|
min_oz: typing.Optional[float] = None
|
162
|
+
min_g: typing.Optional[float] = None
|
142
163
|
max_in: typing.Optional[float] = None
|
143
164
|
max_cm: typing.Optional[float] = None
|
144
165
|
max_lb: typing.Optional[float] = None
|
145
166
|
max_kg: typing.Optional[float] = None
|
146
167
|
max_oz: typing.Optional[float] = None
|
147
|
-
|
168
|
+
max_g: typing.Optional[float] = None
|
169
|
+
|
170
|
+
min_volume: typing.Optional[float] = None
|
171
|
+
max_volume: typing.Optional[float] = None
|
148
172
|
|
149
173
|
|
150
174
|
class CarrierCapabilities(utils.Enum):
|
@@ -153,6 +177,7 @@ class CarrierCapabilities(utils.Enum):
|
|
153
177
|
shipping = "shipping"
|
154
178
|
tracking = "tracking"
|
155
179
|
paperless = "paperless"
|
180
|
+
manifest = "manifest"
|
156
181
|
|
157
182
|
@classmethod
|
158
183
|
def get_capabilities(cls):
|
@@ -172,6 +197,8 @@ class CarrierCapabilities(utils.Enum):
|
|
172
197
|
return "shipping"
|
173
198
|
elif "document" in method_name:
|
174
199
|
return "paperless"
|
200
|
+
elif "manifest" in method_name:
|
201
|
+
return "manifest"
|
175
202
|
|
176
203
|
return None
|
177
204
|
|
@@ -241,6 +268,13 @@ class Dimension:
|
|
241
268
|
else:
|
242
269
|
return self._compute(self.CM / 100)
|
243
270
|
|
271
|
+
@property
|
272
|
+
def MM(self):
|
273
|
+
if self._unit is None or self._value is None:
|
274
|
+
return None
|
275
|
+
else:
|
276
|
+
return self._compute(self.CM * 10)
|
277
|
+
|
244
278
|
def map(self, options: MeasurementOptionsType):
|
245
279
|
return Dimension(value=self._value, unit=self._unit, options=options)
|
246
280
|
|
@@ -249,24 +283,148 @@ class Volume:
|
|
249
283
|
"""The volume common processing helper"""
|
250
284
|
|
251
285
|
def __init__(
|
252
|
-
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(),
|
253
293
|
):
|
254
294
|
self._side1 = side1
|
255
295
|
self._side2 = side2
|
256
296
|
self._side3 = side3
|
257
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
|
+
|
258
321
|
@property
|
259
322
|
def value(self):
|
260
|
-
|
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:
|
261
333
|
return None
|
262
334
|
|
263
|
-
|
335
|
+
if not missing_value:
|
336
|
+
return self._value
|
337
|
+
|
338
|
+
return self._compute(self._side1.value * self._side2.value * self._side3.value)
|
264
339
|
|
265
340
|
@property
|
266
|
-
def
|
341
|
+
def l(self):
|
342
|
+
if self.value is None:
|
343
|
+
return None
|
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):
|
267
402
|
if self.value is None:
|
268
403
|
return None
|
269
|
-
|
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
|
+
)
|
270
428
|
|
271
429
|
|
272
430
|
class Girth:
|
@@ -306,6 +464,7 @@ class Weight:
|
|
306
464
|
self._min_lb = options.min_lb
|
307
465
|
self._min_kg = options.min_kg
|
308
466
|
self._min_oz = options.min_oz
|
467
|
+
self._min_g = options.min_g
|
309
468
|
self._quant = options.quant
|
310
469
|
|
311
470
|
def __getitem__(self, item):
|
@@ -339,6 +498,10 @@ class Weight:
|
|
339
498
|
return self._compute(self._value, self._min_kg)
|
340
499
|
elif self._unit == WeightUnit.LB:
|
341
500
|
return self._compute(self._value / 2.205, self._min_kg)
|
501
|
+
elif self._unit == WeightUnit.OZ:
|
502
|
+
return self._compute(self._value / 35.274, self._min_kg)
|
503
|
+
elif self._unit == WeightUnit.G:
|
504
|
+
return self._compute(self._value / 1000, self._min_kg)
|
342
505
|
|
343
506
|
return None
|
344
507
|
|
@@ -350,6 +513,10 @@ class Weight:
|
|
350
513
|
return self._compute(self._value, self._min_lb)
|
351
514
|
elif self._unit == WeightUnit.KG:
|
352
515
|
return self._compute(self._value * 2.205, self._min_lb)
|
516
|
+
elif self._unit == WeightUnit.OZ:
|
517
|
+
return self._compute(self._value / 16, self._min_lb)
|
518
|
+
elif self._unit == WeightUnit.G:
|
519
|
+
return self._compute(self._value / 453.6, self._min_lb)
|
353
520
|
|
354
521
|
return None
|
355
522
|
|
@@ -357,10 +524,29 @@ class Weight:
|
|
357
524
|
def OZ(self) -> typing.Optional[float]:
|
358
525
|
if self._unit is None or self._value is None:
|
359
526
|
return None
|
527
|
+
elif self._unit == WeightUnit.OZ:
|
528
|
+
return self._compute(self._value, self._min_oz)
|
360
529
|
if self._unit == WeightUnit.LB:
|
361
530
|
return self._compute(self._value * 16, self._min_oz)
|
362
531
|
elif self._unit == WeightUnit.KG:
|
363
532
|
return self._compute(self._value * 35.274, self._min_oz)
|
533
|
+
elif self._unit == WeightUnit.G:
|
534
|
+
return self._compute(self._value / 28.35, self._min_oz)
|
535
|
+
|
536
|
+
return None
|
537
|
+
|
538
|
+
@property
|
539
|
+
def G(self) -> typing.Optional[float]:
|
540
|
+
if self._unit is None or self._value is None:
|
541
|
+
return None
|
542
|
+
elif self._unit == WeightUnit.G:
|
543
|
+
return self._compute(self._value, self._min_g)
|
544
|
+
if self._unit == WeightUnit.LB:
|
545
|
+
return self._compute(self._value * 453.6, self._min_g)
|
546
|
+
elif self._unit == WeightUnit.KG:
|
547
|
+
return self._compute(self._value * 1000, self._min_g)
|
548
|
+
elif self._unit == WeightUnit.OZ:
|
549
|
+
return self._compute(self._value * 28.35, self._min_g)
|
364
550
|
|
365
551
|
return None
|
366
552
|
|
@@ -419,6 +605,9 @@ class Products(typing.Iterable[Product]):
|
|
419
605
|
weight_unit: str = None,
|
420
606
|
):
|
421
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
|
+
)
|
422
611
|
|
423
612
|
def __len__(self) -> int:
|
424
613
|
return len(self._items)
|
@@ -435,7 +624,30 @@ class Products(typing.Iterable[Product]):
|
|
435
624
|
|
436
625
|
@property
|
437
626
|
def value_amount(self):
|
438
|
-
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
|
439
651
|
|
440
652
|
|
441
653
|
class Package:
|
@@ -449,13 +661,16 @@ class Package:
|
|
449
661
|
package_option_type: typing.Type[utils.Enum] = utils.Enum,
|
450
662
|
weight_unit: str = None,
|
451
663
|
dimension_unit: str = None,
|
664
|
+
shipping_options_initializer: typing.Callable = None,
|
452
665
|
):
|
453
666
|
self.parcel: models.Parcel = parcel
|
454
667
|
self.preset: PackagePreset = template or PackagePreset()
|
455
668
|
|
456
|
-
|
457
|
-
|
458
|
-
|
669
|
+
_options = {**parcel.options, **getattr(options, "content", {})}
|
670
|
+
self._options: "ShippingOptions" = (
|
671
|
+
shipping_options_initializer(_options)
|
672
|
+
if shipping_options_initializer is not None
|
673
|
+
else ShippingOptions(_options, package_option_type)
|
459
674
|
)
|
460
675
|
self._dimension_unit = (
|
461
676
|
dimension_unit or self.parcel.dimension_unit or self.preset.dimension_unit
|
@@ -523,7 +738,9 @@ class Package:
|
|
523
738
|
|
524
739
|
@property
|
525
740
|
def volume(self) -> Volume:
|
526
|
-
return Volume(
|
741
|
+
return Volume(
|
742
|
+
self.width, self.length, self.height, unit=self.dimension_unit.value
|
743
|
+
)
|
527
744
|
|
528
745
|
@property
|
529
746
|
def thickness(self) -> Dimension:
|
@@ -561,6 +778,17 @@ class Package:
|
|
561
778
|
|
562
779
|
return Products(_items, self.weight_unit.value)
|
563
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
|
+
|
564
792
|
|
565
793
|
class Packages(typing.Iterable[Package]):
|
566
794
|
"""The parcel collection common processing helper"""
|
@@ -573,6 +801,7 @@ class Packages(typing.Iterable[Package]):
|
|
573
801
|
max_weight: Weight = None,
|
574
802
|
options: "ShippingOptions" = None,
|
575
803
|
package_option_type: typing.Type[utils.Enum] = utils.Enum,
|
804
|
+
shipping_options_initializer: typing.Callable = None,
|
576
805
|
):
|
577
806
|
self._compatible_units = self._compute_compatible_units(parcels, presets)
|
578
807
|
self._options = options or ShippingOptions({}, package_option_type)
|
@@ -584,12 +813,14 @@ class Packages(typing.Iterable[Package]):
|
|
584
813
|
package_option_type=package_option_type,
|
585
814
|
weight_unit=self._compatible_units[0].value,
|
586
815
|
dimension_unit=self._compatible_units[1].value,
|
816
|
+
shipping_options_initializer=shipping_options_initializer,
|
587
817
|
)
|
588
818
|
for parcel in parcels
|
589
819
|
]
|
590
820
|
self._required = required
|
591
821
|
self._max_weight = max_weight
|
592
822
|
self._package_option_type = package_option_type
|
823
|
+
self._shipping_options_initializer = shipping_options_initializer
|
593
824
|
|
594
825
|
self.validate()
|
595
826
|
|
@@ -649,6 +880,24 @@ class Packages(typing.Iterable[Package]):
|
|
649
880
|
|
650
881
|
return Weight(unit=unit, value=value)
|
651
882
|
|
883
|
+
@property
|
884
|
+
def volume(self) -> Volume:
|
885
|
+
if not any([pkg.volume.value for pkg in self._items]):
|
886
|
+
return Volume(value=None)
|
887
|
+
|
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)
|
900
|
+
|
652
901
|
@property
|
653
902
|
def package_type(self) -> str:
|
654
903
|
return (
|
@@ -670,6 +919,15 @@ class Packages(typing.Iterable[Package]):
|
|
670
919
|
|
671
920
|
return description
|
672
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
|
+
|
673
931
|
@property
|
674
932
|
def options(self) -> "ShippingOptions":
|
675
933
|
def merge_options(acc, pkg) -> dict:
|
@@ -699,6 +957,9 @@ class Packages(typing.Iterable[Package]):
|
|
699
957
|
self._options.content,
|
700
958
|
)
|
701
959
|
|
960
|
+
if self._shipping_options_initializer is not None:
|
961
|
+
return self._shipping_options_initializer(options)
|
962
|
+
|
702
963
|
return ShippingOptions(options, self._package_option_type)
|
703
964
|
|
704
965
|
@property
|
@@ -721,6 +982,15 @@ class Packages(typing.Iterable[Package]):
|
|
721
982
|
|
722
983
|
return Products(_items, _weight_unit.value)
|
723
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
|
+
|
724
994
|
def validate(self, required: typing.List[str] = None, max_weight: Weight = None):
|
725
995
|
required = required or self._required
|
726
996
|
max_weight = max_weight or self._max_weight
|
@@ -760,6 +1030,7 @@ class Packages(typing.Iterable[Package]):
|
|
760
1030
|
max_weight: Weight = None,
|
761
1031
|
options: "ShippingOptions" = None,
|
762
1032
|
package_option_type: typing.Type[utils.Enum] = utils.Enum,
|
1033
|
+
shipping_options_initializer: typing.Callable = None,
|
763
1034
|
) -> typing.Union[typing.List[Package], "Packages"]:
|
764
1035
|
return typing.cast(
|
765
1036
|
typing.Union[typing.List[Package], Packages],
|
@@ -770,6 +1041,7 @@ class Packages(typing.Iterable[Package]):
|
|
770
1041
|
max_weight,
|
771
1042
|
options,
|
772
1043
|
package_option_type,
|
1044
|
+
shipping_options_initializer,
|
773
1045
|
),
|
774
1046
|
)
|
775
1047
|
|
@@ -796,6 +1068,7 @@ class Options:
|
|
796
1068
|
_key = key
|
797
1069
|
option_values[key] = _val
|
798
1070
|
|
1071
|
+
self._raw_options = options
|
799
1072
|
self._options = option_values
|
800
1073
|
self._option_list = self._filter(
|
801
1074
|
option_values, (items_filter or utils.identity)
|
@@ -837,20 +1110,34 @@ class ShippingOption(utils.Enum):
|
|
837
1110
|
"""universal shipment options (special services)"""
|
838
1111
|
|
839
1112
|
currency = utils.OptionEnum("currency")
|
1113
|
+
is_return = utils.OptionEnum("is_return", bool)
|
840
1114
|
insurance = utils.OptionEnum("insurance", float)
|
841
1115
|
cash_on_delivery = utils.OptionEnum("COD", float)
|
842
1116
|
shipment_note = utils.OptionEnum("shipment_note")
|
843
|
-
shipment_date = utils.OptionEnum("shipment_date")
|
844
1117
|
dangerous_good = utils.OptionEnum("dangerous_good", bool)
|
845
1118
|
declared_value = utils.OptionEnum("declared_value", float)
|
846
1119
|
paperless_trade = utils.OptionEnum("paperless_trade", bool)
|
847
|
-
|
848
|
-
sms_notification = utils.OptionEnum("email_notification", bool)
|
1120
|
+
sms_notification = utils.OptionEnum("sms_notification", bool)
|
849
1121
|
email_notification = utils.OptionEnum("email_notification", bool)
|
850
1122
|
email_notification_to = utils.OptionEnum("email_notification_to")
|
851
1123
|
signature_confirmation = utils.OptionEnum("signature_confirmation", bool)
|
1124
|
+
saturday_delivery = utils.OptionEnum("saturday_delivery", bool)
|
1125
|
+
sunday_delivery = utils.OptionEnum("sunday_delivery", bool)
|
852
1126
|
doc_files = utils.OptionEnum("doc_files", utils.DP.to_dict)
|
853
1127
|
doc_references = utils.OptionEnum("doc_references", utils.DP.to_dict)
|
1128
|
+
hold_at_location = utils.OptionEnum("hold_at_location", bool)
|
1129
|
+
hold_at_location_address = utils.OptionEnum(
|
1130
|
+
"hold_at_location_address",
|
1131
|
+
functools.partial(utils.DP.to_object, models.Address),
|
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
|
854
1141
|
|
855
1142
|
|
856
1143
|
class ShippingOptions(Options):
|
@@ -896,8 +1183,22 @@ class ShippingOptions(Options):
|
|
896
1183
|
|
897
1184
|
@property
|
898
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
|
+
|
899
1196
|
return self[ShippingOption.shipment_date.name]
|
900
1197
|
|
1198
|
+
@property
|
1199
|
+
def shipping_date(self) -> utils.OptionEnum:
|
1200
|
+
return self[ShippingOption.shipping_date.name]
|
1201
|
+
|
901
1202
|
@property
|
902
1203
|
def signature_confirmation(self) -> utils.OptionEnum:
|
903
1204
|
return self[ShippingOption.signature_confirmation.name]
|
@@ -922,19 +1223,63 @@ class ShippingOptions(Options):
|
|
922
1223
|
def shipment_note(self) -> utils.OptionEnum:
|
923
1224
|
return self[ShippingOption.shipment_note.name]
|
924
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
|
+
|
925
1234
|
|
926
1235
|
class CustomsOption(utils.Enum):
|
927
1236
|
"""common shipment customs identifiers"""
|
928
1237
|
|
929
1238
|
aes = utils.OptionEnum("aes")
|
1239
|
+
ioss = utils.OptionEnum("ioss")
|
930
1240
|
eel_pfc = utils.OptionEnum("eel_pfc")
|
931
|
-
nip_number = utils.OptionEnum("
|
1241
|
+
nip_number = utils.OptionEnum("nip_number")
|
932
1242
|
eori_number = utils.OptionEnum("eori_number")
|
933
1243
|
license_number = utils.OptionEnum("license_number")
|
934
1244
|
certificate_number = utils.OptionEnum("certificate_number")
|
935
1245
|
vat_registration_number = utils.OptionEnum("vat_registration_number")
|
936
1246
|
|
937
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
|
+
|
938
1283
|
class CustomsInfo(models.Customs):
|
939
1284
|
"""The customs info processing helper"""
|
940
1285
|
|
@@ -948,10 +1293,9 @@ class CustomsInfo(models.Customs):
|
|
948
1293
|
recipient: typing.Optional[models.Address] = None,
|
949
1294
|
):
|
950
1295
|
_customs = customs or default_to
|
951
|
-
options =
|
1296
|
+
options = CustomsOptions(
|
952
1297
|
getattr(_customs, "options", None) or {},
|
953
1298
|
option_type=option_type,
|
954
|
-
base_option_type=CustomsOption,
|
955
1299
|
)
|
956
1300
|
|
957
1301
|
self._customs = _customs
|
@@ -974,7 +1318,7 @@ class CustomsInfo(models.Customs):
|
|
974
1318
|
return self._customs is not None
|
975
1319
|
|
976
1320
|
@property
|
977
|
-
def duty(self) ->
|
1321
|
+
def duty(self) -> models.Duty: # type:ignore
|
978
1322
|
return getattr(self._customs, "duty", None) or models.Duty()
|
979
1323
|
|
980
1324
|
@property
|
@@ -1116,7 +1460,7 @@ class ComputedAddress(models.Address):
|
|
1116
1460
|
|
1117
1461
|
@property
|
1118
1462
|
def country_name(self):
|
1119
|
-
return Country
|
1463
|
+
return Country.map(self.address.country_code).value
|
1120
1464
|
|
1121
1465
|
@property
|
1122
1466
|
def address_line(self) -> str:
|
@@ -1151,7 +1495,6 @@ class ComputedAddress(models.Address):
|
|
1151
1495
|
join=True,
|
1152
1496
|
),
|
1153
1497
|
)
|
1154
|
-
return self.address.address_line1.replace(self.street_number, "").strip()
|
1155
1498
|
|
1156
1499
|
@property
|
1157
1500
|
def tax_id(self) -> typing.Optional[str]:
|
@@ -1184,6 +1527,20 @@ class ComputedAddress(models.Address):
|
|
1184
1527
|
self.address, "company_name", None
|
1185
1528
|
)
|
1186
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
|
+
|
1187
1544
|
def _compute_address_line(self, join: bool = True) -> typing.Optional[str]:
|
1188
1545
|
if any(
|
1189
1546
|
[
|
@@ -1202,7 +1559,7 @@ class ComputedAddress(models.Address):
|
|
1202
1559
|
def _compute_street_number(self):
|
1203
1560
|
_value = getattr(self.address, "street_number", None)
|
1204
1561
|
|
1205
|
-
if _value is None:
|
1562
|
+
if _value is None and self.address:
|
1206
1563
|
words = self.address.address_line1.split(" ")
|
1207
1564
|
|
1208
1565
|
if any(_.isdigit() for _ in words[0]):
|
@@ -1231,16 +1588,20 @@ class ComputedDocumentFile(models.DocumentFile):
|
|
1231
1588
|
|
1232
1589
|
|
1233
1590
|
class TrackingStatus(utils.Enum):
|
1591
|
+
pending = ["pending"]
|
1234
1592
|
on_hold = ["on_hold"]
|
1593
|
+
cancelled = ["cancelled"]
|
1235
1594
|
delivered = ["delivered"]
|
1236
1595
|
in_transit = ["in_transit"]
|
1237
1596
|
delivery_failed = ["delivery_failed"]
|
1238
1597
|
delivery_delayed = ["delivery_delayed"]
|
1239
1598
|
out_for_delivery = ["out_for_delivery"]
|
1240
1599
|
ready_for_pickup = ["ready_for_pickup"]
|
1600
|
+
return_to_sender = ["return_to_sender"]
|
1601
|
+
unknown = ["unknown"]
|
1241
1602
|
|
1242
1603
|
|
1243
|
-
def create_enum(name, values):
|
1604
|
+
def create_enum(name, values) -> utils.Enum:
|
1244
1605
|
return utils.Enum(name, values) # type: ignore
|
1245
1606
|
|
1246
1607
|
|
@@ -1393,6 +1754,7 @@ class Currency(utils.Enum):
|
|
1393
1754
|
|
1394
1755
|
|
1395
1756
|
class Country(utils.Enum):
|
1757
|
+
AC = "Ascension Island"
|
1396
1758
|
AD = "Andorra"
|
1397
1759
|
AE = "United Arab Emirates"
|
1398
1760
|
AF = "Afghanistan"
|
@@ -1627,9 +1989,17 @@ class Country(utils.Enum):
|
|
1627
1989
|
ZA = "South Africa"
|
1628
1990
|
ZM = "Zambia"
|
1629
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"
|
1630
1999
|
|
1631
2000
|
|
1632
2001
|
class CountryCurrency(utils.Enum):
|
2002
|
+
AC = "USD"
|
1633
2003
|
AD = "EUR"
|
1634
2004
|
AE = "AED"
|
1635
2005
|
AF = "USD"
|
@@ -2357,3 +2727,240 @@ class EUCountry(utils.Enum):
|
|
2357
2727
|
SK = "Slovakia"
|
2358
2728
|
ES = "Spain"
|
2359
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
|