karrio-dicom 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.
@@ -0,0 +1,192 @@
1
+ from typing import Tuple, List
2
+ from karrio.schemas.dicom.shipments import (
3
+ ShipmentRequest as DicomShipmentRequest,
4
+ Address as DicomAddress,
5
+ Parcel,
6
+ Surcharge,
7
+ Contact,
8
+ InternationalDetails,
9
+ Product,
10
+ Broker,
11
+ ShipmentResponse,
12
+ )
13
+ import karrio.lib as lib
14
+ from karrio.core.units import Packages
15
+ from karrio.core.utils import Serializable, DP
16
+ from karrio.core.models import (
17
+ Documents,
18
+ ShipmentRequest,
19
+ ShipmentDetails,
20
+ Message,
21
+ Address,
22
+ Payment,
23
+ )
24
+
25
+ from karrio.providers.dicom.units import (
26
+ UnitOfMeasurement,
27
+ ParcelType,
28
+ Service,
29
+ ShippingOption,
30
+ PaymentType,
31
+ Purpose,
32
+ )
33
+ from karrio.providers.dicom.error import parse_error_response
34
+ from karrio.providers.dicom.utils import Settings
35
+
36
+
37
+ def parse_shipment_response(
38
+ _response: lib.Deserializable[dict],
39
+ settings: Settings,
40
+ ) -> Tuple[ShipmentDetails, List[Message]]:
41
+ response = _response.deserialize()
42
+ errors = parse_error_response(response, settings)
43
+ details = (
44
+ _extract_details(response, settings)
45
+ if all(key in response for key in ["label", "shipment"])
46
+ else None
47
+ )
48
+
49
+ return details, errors
50
+
51
+
52
+ def _extract_details(response: dict, settings: Settings) -> ShipmentDetails:
53
+ label: str = response["label"]
54
+ shipment = DP.to_object(ShipmentResponse, response["shipment"])
55
+
56
+ return ShipmentDetails(
57
+ carrier_name=settings.carrier_name,
58
+ carrier_id=settings.carrier_id,
59
+ tracking_number=shipment.trackingNumber,
60
+ shipment_identifier=shipment.ID,
61
+ docs=Documents(label=label),
62
+ )
63
+
64
+
65
+ def shipment_request(payload: ShipmentRequest, settings: Settings) -> Serializable:
66
+ packages = Packages(payload.parcels)
67
+ shipper = lib.to_address(payload.shipper)
68
+ recipient = lib.to_address(payload.recipient)
69
+ is_international = shipper.country_code != recipient.country_code
70
+ broker_info = payload.options.get("dicom_broker_info", {})
71
+ importer_info = (
72
+ Address(**payload.options.get("importer_info"))
73
+ if "importer_info" in payload.options
74
+ else None
75
+ )
76
+ payment = payload.payment or Payment("prepaid")
77
+ delivery_type = Service[payload.service].value
78
+ options = {
79
+ key: (value if ShippingOption[key].value in ["DCV", "COD"] else None)
80
+ for key, value in payload.options
81
+ if key in ShippingOption.__members__
82
+ }
83
+
84
+ request = DicomShipmentRequest(
85
+ paymentType=PaymentType[payment.paid_by or "prepaid"].value,
86
+ billingAccount=settings.billing_account,
87
+ sender=DicomAddress(
88
+ city=shipper.city,
89
+ provinceCode=shipper.state_code,
90
+ postalCode=shipper.postal_code,
91
+ countryCode=shipper.country_code,
92
+ customerName=shipper.company_name,
93
+ addressLine1=shipper.street,
94
+ addressLine2=shipper.address_line2,
95
+ contact=Contact(
96
+ fullName=shipper.person_name,
97
+ email=shipper.email,
98
+ telephone=shipper.phone_number,
99
+ ),
100
+ ),
101
+ consignee=DicomAddress(
102
+ city=recipient.city,
103
+ provinceCode=recipient.state_code,
104
+ postalCode=recipient.postal_code,
105
+ countryCode=recipient.country_code,
106
+ customerName=recipient.company_name,
107
+ addressLine1=recipient.street,
108
+ addressLine2=recipient.address_line2,
109
+ contact=Contact(
110
+ fullName=recipient.person_name,
111
+ email=recipient.email,
112
+ telephone=recipient.phone_number,
113
+ ),
114
+ ),
115
+ parcels=[
116
+ Parcel(
117
+ quantity=1,
118
+ parcelType=ParcelType[package.packaging_type or "dicom_box"].value,
119
+ weight=package.weight.KG,
120
+ length=package.height.CM,
121
+ depth=package.length.CM,
122
+ width=package.width.CM,
123
+ note=None,
124
+ status=None,
125
+ FCAClass=None,
126
+ hazmat=None,
127
+ requestReturnLabel=None,
128
+ returnWaybill=None,
129
+ )
130
+ for package in packages
131
+ ],
132
+ note=None,
133
+ category="Parcel",
134
+ pickupDate=None,
135
+ deliveryType=delivery_type,
136
+ trackingNumber=None,
137
+ unitOfMeasurement=UnitOfMeasurement.KC.value,
138
+ surcharges=[Surcharge(type=key, value=value) for key, value in options.items()],
139
+ promoCodes=None,
140
+ references=None,
141
+ returnAddress=None,
142
+ appointment=None,
143
+ internationalDetails=(
144
+ InternationalDetails(
145
+ isDicomBroker=(broker_info is not None),
146
+ descriptionOfGoods=payload.customs.content_description,
147
+ dutyBilling=payload.customs.duty.paid_by,
148
+ importerOfRecord=(
149
+ DicomAddress(
150
+ city=importer_info.city,
151
+ provinceCode=importer_info.state_code,
152
+ postalCode=importer_info.postal_code,
153
+ countryCode=importer_info.country_code,
154
+ customerName=importer_info.company_name,
155
+ addressLine1=lib.text(
156
+ importer_info.street_number, importer_info.address_line1
157
+ ),
158
+ addressLine2=importer_info.address_line2,
159
+ contact=Contact(
160
+ fullName=importer_info.person_name,
161
+ email=importer_info.email,
162
+ telephone=importer_info.phone_number,
163
+ ),
164
+ )
165
+ if importer_info is not None
166
+ else None
167
+ ),
168
+ broker=(
169
+ Broker(
170
+ id=broker_info.get("id"),
171
+ CSA_BusinessNumber=broker_info.get("CSA_BusinessNumber"),
172
+ otherBroker=broker_info.get("otherBroker"),
173
+ )
174
+ if broker_info is not None
175
+ else None
176
+ ),
177
+ purpose=(
178
+ Purpose[payload.customs.content_type].value
179
+ if payload.customs.content_type is not None
180
+ else None
181
+ ),
182
+ products=[
183
+ Product(id=index, Quantity=product.quantity)
184
+ for index, product in enumerate(payload.customs.commodities, 1)
185
+ ],
186
+ )
187
+ if is_international and payload.customs is not None
188
+ else None
189
+ ),
190
+ )
191
+
192
+ return Serializable(request)
@@ -0,0 +1,47 @@
1
+ from typing import Tuple, List
2
+ from karrio.schemas.dicom.tracking import Tracking
3
+ from karrio.core.utils import Serializable, DF, DP
4
+ from karrio.core.models import TrackingRequest, TrackingDetails, TrackingEvent, Message
5
+
6
+ from karrio.providers.dicom.error import parse_error_response
7
+ from karrio.providers.dicom.utils import Settings
8
+ import karrio.lib as lib
9
+
10
+
11
+ def parse_tracking_response(
12
+ _response: lib.Deserializable[dict],
13
+ settings: Settings,
14
+ ) -> Tuple[List[TrackingDetails], List[Message]]:
15
+ response = _response.deserialize()
16
+ errors = [e for e in response if "activities" not in e]
17
+ details = [
18
+ _extract_detail(DP.to_object(Tracking, d), settings)
19
+ for d in response
20
+ if "activities" in d
21
+ ]
22
+
23
+ return details, parse_error_response(errors, settings)
24
+
25
+
26
+ def _extract_detail(detail: Tracking, settings: Settings) -> TrackingDetails:
27
+ return TrackingDetails(
28
+ carrier_name=settings.carrier_name,
29
+ carrier_id=settings.carrier_id,
30
+ tracking_number=detail.trackingNumber,
31
+ events=[
32
+ TrackingEvent(
33
+ date=DF.fdate(event.activityDate, "%Y-%m-%dT%H:%M:%SZ"),
34
+ description=event.statusDetail,
35
+ location=event.terminal,
36
+ code=event.status,
37
+ time=DF.ftime(event.activityDate, "%Y-%m-%dT%H:%M:%SZ"),
38
+ )
39
+ for event in detail.activities
40
+ ],
41
+ )
42
+
43
+
44
+ def tracking_request(payload: TrackingRequest, _) -> Serializable:
45
+ request = payload.tracking_numbers
46
+
47
+ return Serializable(request)
@@ -0,0 +1,167 @@
1
+ import karrio.lib as lib
2
+ import karrio.core.units as units
3
+
4
+
5
+ class UnitOfMeasurement(lib.StrEnum):
6
+ K = "K"
7
+ L = "L"
8
+ KC = "KC"
9
+ KM = "KM"
10
+ LI = "LI"
11
+ LF = "LF"
12
+
13
+
14
+ class Category(lib.StrEnum):
15
+ parcel = "Parcel"
16
+ freight = "Freight"
17
+ distribution = "Distribution"
18
+ logistics = "Logistics"
19
+
20
+
21
+ class Purpose(lib.StrEnum):
22
+ com = "COM"
23
+ per = "PER"
24
+ doc = "DOC"
25
+ ret = "RET"
26
+
27
+ """ Unified Customs Content Type mapping """
28
+
29
+ documents = doc
30
+ gift = per
31
+ sample = per
32
+ merchandise = com
33
+ return_merchandise = ret
34
+ other = per
35
+
36
+
37
+ class PaymentType(lib.StrEnum):
38
+ prepaid = "Prepaid"
39
+ third_party = "ThirdParty"
40
+ collect = "Collect"
41
+
42
+ """ Unified Payment Type mapping """
43
+
44
+ sender = prepaid
45
+ recipient = collect
46
+
47
+
48
+ class ParcelType(lib.StrEnum):
49
+ dicom_barrel = "Barrel"
50
+ dicom_bundle = "Bundle"
51
+ dicom_box = "Box"
52
+ dicom_crate = "Crate"
53
+ dicom_full_load = "FullLoad"
54
+ dicom_mixed = "Mixed"
55
+ dicom_other = "Other"
56
+ dicom_piece = "Piece"
57
+ dicom_skid = "Skid"
58
+ dicom_tube = "Tube"
59
+ dicom_envelope = "Envelope"
60
+ dicom_maxpak = "Maxpak"
61
+ dicom_pallet = "Pallet"
62
+
63
+ """ Unified Packaging type mapping """
64
+
65
+ envelope = dicom_envelope
66
+ pak = dicom_maxpak
67
+ tube = dicom_tube
68
+ pallet = dicom_pallet
69
+ small_box = dicom_box
70
+ medium_box = dicom_box
71
+ your_packaging = dicom_other
72
+
73
+
74
+ class Service(lib.StrEnum): # DeliveryType
75
+ dicom_air_delivery = "AIR"
76
+ dicom_ground_delivery = "GRD"
77
+
78
+
79
+ class ShippingOption(lib.Enum):
80
+ dicom_common_declared_value = lib.OptionEnum("DCV")
81
+ dicom_common_dangerous_goods = lib.OptionEnum("DGG")
82
+ dicom_common_residential_delivery = lib.OptionEnum("PHD")
83
+ dicom_common_tradeshow_delivery = lib.OptionEnum("TRD")
84
+ dicom_common_signature_not_required = lib.OptionEnum("SNR")
85
+ dicom_parcel_ca_hold_for_pickup = lib.OptionEnum("HFP")
86
+ dicom_parcel_ca_non_conveyable = lib.OptionEnum("NCV")
87
+ dicom_parcel_ca_residential_delivery_signature = lib.OptionEnum("PHDS")
88
+ dicom_parcel_ca_weekend_delivery = lib.OptionEnum("WKD")
89
+ dicom_freight_construction_site_delivery = lib.OptionEnum("CNSTD")
90
+ dicom_freight_collect_on_delivery = lib.OptionEnum("COD")
91
+ dicom_freight_heating = lib.OptionEnum("HEAT")
92
+ dicom_freight_inside_delivery = lib.OptionEnum("IDEL")
93
+ dicom_freight_residential_delivery_signature = lib.OptionEnum("PHDS")
94
+ dicom_freight_residential_pickup = lib.OptionEnum("PHPU")
95
+ dicom_freight_tailgate_delivery = lib.OptionEnum("TGT")
96
+ dicom_freight_tailgate_pickup = lib.OptionEnum("TGTPU")
97
+ dicom_parcel_us_adult_signature = lib.OptionEnum("ADLSIG")
98
+ dicom_parcel_us_direct_signature = lib.OptionEnum("DIRSIG")
99
+ dicom_parcel_us_saturday_delivery = lib.OptionEnum("SAT")
100
+ dicom_parcel_us_sunday_delivery = lib.OptionEnum("SUN")
101
+ dicom_parcel_us_residential_delivery_signature = lib.OptionEnum("PHDS")
102
+ dicom_parcel_us_earliest_possible = lib.OptionEnum("EP")
103
+ dicom_parcel_us_priority_service = lib.OptionEnum("PR")
104
+ dicom_parcel_us_pouch_service = lib.OptionEnum("PO")
105
+ dicom_parcel_us_pallet_service_pa = lib.OptionEnum("PA")
106
+ dicom_parcel_us_pallet_service_rap = lib.OptionEnum("RAP")
107
+ dicom_parcel_us_pallet_service_nd = lib.OptionEnum("ND")
108
+
109
+ """ Unified Option type mapping """
110
+ saturday_delivery = dicom_parcel_us_saturday_delivery
111
+
112
+
113
+ def shipping_options_initializer(
114
+ options: dict,
115
+ package_options: units.Options = None,
116
+ ) -> units.Options:
117
+ """
118
+ Apply default values to the given options.
119
+ """
120
+ _options = options.copy()
121
+
122
+ if package_options is not None:
123
+ _options.update(package_options.content)
124
+
125
+ return units.ShippingOptions(_options, ShippingOption)
126
+
127
+
128
+ class Surcharge(lib.StrEnum):
129
+ dicom_common_base = "BAS"
130
+ dicom_common_declared_value = "DCV"
131
+ dicom_common_dangerous_goods = "DGG"
132
+ dicom_common_other_charge = "OTH"
133
+ dicom_common_residential_delivery = "PHD"
134
+ dicom_common_tradeshow_delivery = "TRD"
135
+ dicom_parcel_ca_collect_on_delivery = "COD"
136
+ dicom_parcel_ca_collect = "COL"
137
+ dicom_parcel_ca_chain_of_signature = "COS"
138
+ dicom_parcel_ca_dangerous_goods = "DGA"
139
+ dicom_parcel_ca_hold_for_pickup = "HFP"
140
+ dicom_parcel_ca_multi_pieces = "MPC"
141
+ dicom_parcel_ca_non_conveyable = "NCV"
142
+ dicom_parcel_ca_residential_delivery_signature = "PHS"
143
+ dicom_parcel_ca_over_36_inches = "O36"
144
+ dicom_parcel_ca_over_44_inches = "O44"
145
+ dicom_parcel_ca_over_96_inches = "O96"
146
+ dicom_parcel_ca_overweight = "OVW"
147
+ dicom_parcel_ca_signature_not_required = "SNR"
148
+ dicom_parcel_ca_trade_show = "TRD"
149
+ dicom_parcel_ca_weekend_delivery = "WKD"
150
+ dicom_parcel_ca_weekend_kilometers = "WKK"
151
+ dicom_parcel_ca_zone = "ZON"
152
+ dicom_parcel_ca_ontario_minimum_wage = "SMO"
153
+ dicom_parcel_ca_overweight_shipment = "OWS"
154
+ dicom_freight_construction_site_delivery = "CNSTD"
155
+ dicom_freight_collect_on_delivery = "COD"
156
+ dicom_freight_congestion_pickup = "CPU"
157
+ dicom_freight_heating = "HEAT"
158
+ dicom_freight_inside_delivery = "IDEL"
159
+ dicom_freight_pan_am_games_delivery = "PADL"
160
+ dicom_freight_pan_am_games_pickup = "PAPU"
161
+ dicom_freight_residential_delivery_signature = "PHDS"
162
+ dicom_freight_residential_pickup = "PHPU"
163
+ dicom_freight_promo_cash = "PRC"
164
+ dicom_freight_promo_pourcent = "PRP"
165
+ dicom_freight_single_pickup = "SPU"
166
+ dicom_freight_tailgate_delivery = "TGT"
167
+ dicom_freight_tailgate_pickup = "TGTPU"
@@ -0,0 +1,34 @@
1
+ """Karrio Dicom client settings."""
2
+
3
+ from base64 import b64encode
4
+ from karrio.core.settings import Settings as BaseSettings
5
+
6
+
7
+ class Settings(BaseSettings):
8
+ """Dicom connection settings."""
9
+
10
+ # Carrier specific properties
11
+ username: str
12
+ password: str
13
+ billing_account: str = None
14
+
15
+ id: str = None
16
+ account_country_code: str = None
17
+ metadata: dict = {}
18
+
19
+ @property
20
+ def carrier_name(self):
21
+ return "dicom"
22
+
23
+ @property
24
+ def server_url(self):
25
+ return (
26
+ "https://sandbox-smart4i.dicom.com"
27
+ if self.test_mode
28
+ else "https://smart4i.dicom.com"
29
+ )
30
+
31
+ @property
32
+ def authorization(self):
33
+ pair = "%s:%s" % (self.username, self.password)
34
+ return b64encode(pair.encode("utf-8")).decode("ascii")
File without changes
@@ -0,0 +1,83 @@
1
+ import attr
2
+ from jstruct import JStruct, JList, REQUIRED
3
+ from typing import Optional, List
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class Contact:
8
+ fullName: str
9
+ extension: Optional[int] = None
10
+ language: Optional[str] = None
11
+ email: Optional[str] = None
12
+ department: Optional[str] = None
13
+ telephone: Optional[str] = None
14
+
15
+
16
+ @attr.s(auto_attribs=True)
17
+ class Shipment:
18
+ id: int
19
+ status: Optional[int] = None
20
+
21
+
22
+ @attr.s(auto_attribs=True)
23
+ class Sender:
24
+ city: str
25
+ provinceCode: str
26
+ postalCode: str
27
+ countryCode: str
28
+ customerName: str
29
+
30
+ streetNumber: Optional[int] = None
31
+ suite: Optional[int] = None
32
+ addressLine1: Optional[str] = None
33
+ addressLine2: Optional[str] = None
34
+ streetType: Optional[str] = None
35
+ streetName: Optional[str] = None
36
+ streetDirection: Optional[str] = None
37
+ customerNickName: Optional[str] = None
38
+ contact: Optional[Contact] = JStruct[Contact]
39
+
40
+
41
+ @attr.s(auto_attribs=True)
42
+ class Shipment:
43
+ id: Optional[int] = None
44
+ status: Optional[int] = None
45
+
46
+
47
+ @attr.s(auto_attribs=True)
48
+ class Pickup:
49
+ shipments: Optional[List[Shipment]] = JStruct[Shipment]
50
+ id: Optional[int] = None
51
+ officeClose: Optional[str] = None
52
+ date: Optional[str] = None
53
+ ready: Optional[str] = None
54
+ location: Optional[str] = None
55
+ otherLocation: Optional[str] = None
56
+ category: Optional[str] = None
57
+ sender: Optional[Sender] = JStruct[Sender]
58
+ contact: Optional[Contact] = JStruct[Contact]
59
+
60
+
61
+ @attr.s(auto_attribs=True)
62
+ class ShipmentPickupRequest:
63
+ date: str
64
+ ready: str
65
+ category: str
66
+ officeClose: str
67
+ contact: Contact = JStruct[Contact, REQUIRED]
68
+ shipments: List[Shipment] = JList[Shipment, REQUIRED]
69
+
70
+ location: Optional[str] = None
71
+ otherLocation: Optional[str] = None
72
+
73
+
74
+ @attr.s(auto_attribs=True)
75
+ class PickupRequest:
76
+ date: str
77
+ ready: str
78
+ category: str
79
+ officeClose: str
80
+ sender: Sender = JStruct[Sender, REQUIRED]
81
+
82
+ location: Optional[str] = None
83
+ otherLocation: Optional[str] = None
@@ -0,0 +1,126 @@
1
+ import attr
2
+ from jstruct import JStruct, JList, REQUIRED
3
+ from typing import Optional, List
4
+
5
+
6
+ @attr.s(auto_attribs=True)
7
+ class Appointment:
8
+ type: str
9
+ date: Optional[str] = None
10
+ time: Optional[str] = None
11
+ phone: Optional[str] = None
12
+
13
+
14
+ @attr.s(auto_attribs=True)
15
+ class Address:
16
+ postalCode: str
17
+ provinceCode: str
18
+ number: Optional[int] = None
19
+ countryCode: Optional[str] = None
20
+ name: Optional[str] = None
21
+
22
+
23
+ @attr.s(auto_attribs=True)
24
+ class Hazmat:
25
+ number: int
26
+ phone: str
27
+
28
+
29
+ @attr.s(auto_attribs=True)
30
+ class Parcel:
31
+ quantity: int
32
+ parcelType: str
33
+
34
+ id: Optional[int] = None
35
+ weight: Optional[int] = None
36
+ length: Optional[int] = None
37
+ depth: Optional[int] = None
38
+ width: Optional[int] = None
39
+ note: Optional[str] = None
40
+ status: Optional[int] = None
41
+ FCA_Class: Optional[str] = None
42
+ hazmat: Optional[Hazmat] = JStruct[Hazmat]
43
+ requestReturnLabel: Optional[bool] = None
44
+ returnWaybill: Optional[str] = None
45
+
46
+
47
+ @attr.s(auto_attribs=True)
48
+ class PromoCode:
49
+ code: Optional[str] = None
50
+
51
+
52
+ @attr.s(auto_attribs=True)
53
+ class Surcharge:
54
+ type: str
55
+ id: Optional[int] = None
56
+ value: Optional[str] = None
57
+ name: Optional[str] = None
58
+ amount: Optional[int] = None
59
+
60
+
61
+ @attr.s(auto_attribs=True)
62
+ class RateRequest:
63
+ category: str
64
+ paymentType: str
65
+ deliveryType: str
66
+ unitOfMeasurement: str
67
+ sender: Address = JStruct[Address, REQUIRED]
68
+ consignee: Address = JStruct[Address, REQUIRED]
69
+ parcels: List[Parcel] = JList[Parcel, REQUIRED]
70
+
71
+ billing: Optional[int] = None
72
+ promoCodes: Optional[List[PromoCode]] = JList[PromoCode]
73
+ surcharges: Optional[List[Surcharge]] = JList[Surcharge]
74
+ appointment: Optional[Appointment] = JStruct[Appointment]
75
+
76
+
77
+ @attr.s(auto_attribs=True)
78
+ class TaxesDetail:
79
+ type: Optional[str] = None
80
+ amount: Optional[str] = None
81
+ name: Optional[str] = None
82
+
83
+
84
+ @attr.s(auto_attribs=True)
85
+ class Rate:
86
+ grossAmount: Optional[int] = None
87
+ discountAmount: Optional[int] = None
88
+ otherCharge: Optional[int] = None
89
+ fuelChargePercentage: Optional[int] = None
90
+ accountType: Optional[str] = None
91
+ rateType: Optional[str] = None
92
+ cubicWeight: Optional[float] = None
93
+ basicCharge: Optional[float] = None
94
+ weightCharge: Optional[float] = None
95
+ surcharges: List[Surcharge] = JList[Surcharge]
96
+ subTotal: Optional[float] = None
97
+ unitOfMeasurement: Optional[str] = None
98
+ taxesDetails: List[TaxesDetail] = JList[TaxesDetail]
99
+ taxes: Optional[float] = None
100
+ fuelCharge: Optional[float] = None
101
+ zoneCharge: Optional[float] = None
102
+ total: Optional[float] = None
103
+
104
+
105
+ @attr.s(auto_attribs=True)
106
+ class Reference:
107
+ code: Optional[int] = None
108
+ type: Optional[str] = None
109
+
110
+
111
+ @attr.s(auto_attribs=True)
112
+ class RateResponse:
113
+ delay: Optional[int] = None
114
+ terminalLimit: Optional[int] = None
115
+ singleShipmentCost: Optional[int] = None
116
+ quantity: Optional[int] = None
117
+ rates: List[Rate] = JList[Rate]
118
+ references: List[Reference] = JList[Reference]
119
+ unitOfMeasurement: Optional[str] = None
120
+ parcelType: Optional[str] = None
121
+ weight: Optional[str] = None
122
+ postalCodeDelivery: Optional[str] = None
123
+ postalCodePickup: Optional[str] = None
124
+ creator: Optional[str] = None
125
+ date: Optional[str] = None
126
+ warning: Optional[str] = None