karrio-server-core 2025.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. karrio/server/conf.py +54 -0
  2. karrio/server/core/__init__.py +3 -0
  3. karrio/server/core/admin.py +1 -0
  4. karrio/server/core/apps.py +10 -0
  5. karrio/server/core/authentication.py +347 -0
  6. karrio/server/core/config.py +31 -0
  7. karrio/server/core/context_processors.py +12 -0
  8. karrio/server/core/datatypes.py +394 -0
  9. karrio/server/core/dataunits.py +187 -0
  10. karrio/server/core/exceptions.py +404 -0
  11. karrio/server/core/fields.py +12 -0
  12. karrio/server/core/filters.py +837 -0
  13. karrio/server/core/gateway.py +1011 -0
  14. karrio/server/core/logging.py +403 -0
  15. karrio/server/core/management/commands/cli.py +19 -0
  16. karrio/server/core/management/commands/create_oauth_client.py +41 -0
  17. karrio/server/core/management/commands/runserver.py +5 -0
  18. karrio/server/core/middleware.py +197 -0
  19. karrio/server/core/migrations/0001_initial.py +28 -0
  20. karrio/server/core/migrations/0002_apilogindex.py +69 -0
  21. karrio/server/core/migrations/0003_apilogindex_test_mode.py +62 -0
  22. karrio/server/core/migrations/0004_metafield.py +74 -0
  23. karrio/server/core/migrations/0005_alter_metafield_type_alter_metafield_value.py +23 -0
  24. karrio/server/core/migrations/0006_add_api_log_requested_at_index.py +22 -0
  25. karrio/server/core/migrations/__init__.py +0 -0
  26. karrio/server/core/models/__init__.py +48 -0
  27. karrio/server/core/models/base.py +103 -0
  28. karrio/server/core/models/entity.py +24 -0
  29. karrio/server/core/models/metafield.py +144 -0
  30. karrio/server/core/models/third_party.py +21 -0
  31. karrio/server/core/oauth_validators.py +170 -0
  32. karrio/server/core/permissions.py +36 -0
  33. karrio/server/core/renderers.py +11 -0
  34. karrio/server/core/router.py +3 -0
  35. karrio/server/core/serializers.py +1971 -0
  36. karrio/server/core/signals.py +55 -0
  37. karrio/server/core/telemetry.py +573 -0
  38. karrio/server/core/tests.py +99 -0
  39. karrio/server/core/tests_resource_token.py +411 -0
  40. karrio/server/core/urls.py +12 -0
  41. karrio/server/core/utils.py +1025 -0
  42. karrio/server/core/validators.py +264 -0
  43. karrio/server/core/views/__init__.py +2 -0
  44. karrio/server/core/views/api.py +133 -0
  45. karrio/server/core/views/metadata.py +44 -0
  46. karrio/server/core/views/oauth.py +75 -0
  47. karrio/server/core/views/references.py +82 -0
  48. karrio/server/core/views/schema.py +310 -0
  49. karrio/server/filters/__init__.py +2 -0
  50. karrio/server/filters/abstract.py +26 -0
  51. karrio/server/iam/__init__.py +0 -0
  52. karrio/server/iam/admin.py +3 -0
  53. karrio/server/iam/apps.py +21 -0
  54. karrio/server/iam/migrations/0001_initial.py +33 -0
  55. karrio/server/iam/migrations/__init__.py +0 -0
  56. karrio/server/iam/models.py +48 -0
  57. karrio/server/iam/permissions.py +155 -0
  58. karrio/server/iam/serializers.py +54 -0
  59. karrio/server/iam/signals.py +18 -0
  60. karrio/server/iam/tests.py +3 -0
  61. karrio/server/iam/views.py +3 -0
  62. karrio/server/openapi.py +75 -0
  63. karrio/server/providers/__init__.py +1 -0
  64. karrio/server/providers/admin.py +364 -0
  65. karrio/server/providers/apps.py +10 -0
  66. karrio/server/providers/management/commands/migrate_rate_sheets.py +101 -0
  67. karrio/server/providers/migrations/0001_initial.py +140 -0
  68. karrio/server/providers/migrations/0002_carrier_active.py +18 -0
  69. karrio/server/providers/migrations/0003_auto_20201230_0820.py +24 -0
  70. karrio/server/providers/migrations/0004_auto_20210212_0554.py +178 -0
  71. karrio/server/providers/migrations/0005_auto_20210212_0555.py +18 -0
  72. karrio/server/providers/migrations/0006_australiapostsettings.py +29 -0
  73. karrio/server/providers/migrations/0007_auto_20210213_0206.py +21 -0
  74. karrio/server/providers/migrations/0008_auto_20210214_0409.py +30 -0
  75. karrio/server/providers/migrations/0009_auto_20210308_0302.py +18 -0
  76. karrio/server/providers/migrations/0010_auto_20210409_0852.py +32 -0
  77. karrio/server/providers/migrations/0011_auto_20210409_0853.py +21 -0
  78. karrio/server/providers/migrations/0012_alter_carrier_options.py +17 -0
  79. karrio/server/providers/migrations/0013_tntsettings.py +30 -0
  80. karrio/server/providers/migrations/0014_auto_20210612_1608.py +46 -0
  81. karrio/server/providers/migrations/0015_auto_20210615_1601.py +28 -0
  82. karrio/server/providers/migrations/0016_alter_purolatorsettings_user_token.py +18 -0
  83. karrio/server/providers/migrations/0017_auto_20210805_0359.py +1293 -0
  84. karrio/server/providers/migrations/0018_alter_fedexsettings_user_key.py +18 -0
  85. karrio/server/providers/migrations/0019_dhlpolandsettings_servicelevel.py +65 -0
  86. karrio/server/providers/migrations/0020_genericsettings_labeltemplate.py +52 -0
  87. karrio/server/providers/migrations/0021_auto_20211231_2353.py +40 -0
  88. karrio/server/providers/migrations/0022_carrier_metadata.py +18 -0
  89. karrio/server/providers/migrations/0023_auto_20220124_1916.py +27 -0
  90. karrio/server/providers/migrations/0024_alter_genericsettings_custom_carrier_name.py +19 -0
  91. karrio/server/providers/migrations/0025_alter_servicelevel_service_code.py +19 -0
  92. karrio/server/providers/migrations/0026_auto_20220208_0132.py +59 -0
  93. karrio/server/providers/migrations/0027_auto_20220304_1340.py +29 -0
  94. karrio/server/providers/migrations/0028_auto_20220323_1500.py +33 -0
  95. karrio/server/providers/migrations/0029_easypostsettings.py +27 -0
  96. karrio/server/providers/migrations/0030_amazonmwssettings.py +29 -0
  97. karrio/server/providers/migrations/0031_delete_amazonmwssettings.py +18 -0
  98. karrio/server/providers/migrations/0032_alter_carrier_test.py +18 -0
  99. karrio/server/providers/migrations/0033_auto_20220708_1350.py +22 -0
  100. karrio/server/providers/migrations/0034_amazonmwssettings_dpdhlsettings.py +47 -0
  101. karrio/server/providers/migrations/0035_alter_carrier_capabilities.py +43 -0
  102. karrio/server/providers/migrations/0036_upsfreightsettings.py +31 -0
  103. karrio/server/providers/migrations/0037_chronopostsettings.py +29 -0
  104. karrio/server/providers/migrations/0038_alter_genericsettings_label_template.py +19 -0
  105. karrio/server/providers/migrations/0039_auto_20220906_0612.py +23 -0
  106. karrio/server/providers/migrations/0040_dpdhlsettings_services.py +18 -0
  107. karrio/server/providers/migrations/0041_auto_20221105_0705.py +38 -0
  108. karrio/server/providers/migrations/0042_auto_20221215_1642.py +23 -0
  109. karrio/server/providers/migrations/0043_alter_genericsettings_account_number_and_more.py +39 -0
  110. karrio/server/providers/migrations/0044_carrier_carrier_capabilities.py +64 -0
  111. karrio/server/providers/migrations/0045_alter_carrier_active_alter_carrier_carrier_id.py +31 -0
  112. karrio/server/providers/migrations/0046_remove_dpdhlsettings_signature_and_more.py +41 -0
  113. karrio/server/providers/migrations/0047_dpdsettings.py +286 -0
  114. karrio/server/providers/migrations/0048_servicelevel_min_weight_servicelevel_transit_days_and_more.py +64 -0
  115. karrio/server/providers/migrations/0049_boxknightsettings_geodissettings_lapostesettings_and_more.py +156 -0
  116. karrio/server/providers/migrations/0050_carrier_is_system_alter_carrier_metadata_and_more.py +106 -0
  117. karrio/server/providers/migrations/0051_rename_username_upssettings_client_id_and_more.py +31 -0
  118. karrio/server/providers/migrations/0052_alter_upssettings_account_number_and_more.py +20 -0
  119. karrio/server/providers/migrations/0053_locate2usettings.py +281 -0
  120. karrio/server/providers/migrations/0054_zoom2usettings.py +280 -0
  121. karrio/server/providers/migrations/0055_rename_amazonmwssettings_amazonshippingsettings_and_more.py +44 -0
  122. karrio/server/providers/migrations/0056_asendiaussettings_geodissettings_code_client_and_more.py +75 -0
  123. karrio/server/providers/migrations/0057_alter_servicelevel_weight_unit_belgianpostsettings.py +51 -0
  124. karrio/server/providers/migrations/0058_alliedexpresssettings.py +38 -0
  125. karrio/server/providers/migrations/0059_ratesheet.py +81 -0
  126. karrio/server/providers/migrations/0060_belgianpostsettings_rate_sheet_and_more.py +73 -0
  127. karrio/server/providers/migrations/0061_alliedexpresssettings_service_type.py +17 -0
  128. karrio/server/providers/migrations/0062_sendlesettings_account_country_code.py +257 -0
  129. karrio/server/providers/migrations/0063_servicelevel_metadata.py +25 -0
  130. karrio/server/providers/migrations/0064_alliedexpresslocalsettings.py +43 -0
  131. karrio/server/providers/migrations/0065_servicelevel_carrier_service_code_and_more.py +66 -0
  132. karrio/server/providers/migrations/0066_rename_fedexsettings_fedexwssettings_and_more.py +28 -0
  133. karrio/server/providers/migrations/0067_fedexsettings.py +283 -0
  134. karrio/server/providers/migrations/0068_fedexsettings_track_api_key_and_more.py +38 -0
  135. karrio/server/providers/migrations/0069_alter_canadapostsettings_contract_id_and_more.py +23 -0
  136. karrio/server/providers/migrations/0070_tgesettings_alter_carrier_capabilities.py +65 -0
  137. karrio/server/providers/migrations/0071_alter_tgesettings_my_toll_token.py +18 -0
  138. karrio/server/providers/migrations/0072_rename_eshippersettings_eshipperxmlsettings_and_more.py +28 -0
  139. karrio/server/providers/migrations/0073_delete_eshipperxmlsettings.py +41 -0
  140. karrio/server/providers/migrations/0074_eshippersettings.py +38 -0
  141. karrio/server/providers/migrations/0075_haypostsettings.py +40 -0
  142. karrio/server/providers/migrations/0076_rename_customer_registration_id_uspsinternationalsettings_account_number_and_more.py +125 -0
  143. karrio/server/providers/migrations/0077_uspswtinternationalsettings_uspswtsettings_and_more.py +165 -0
  144. karrio/server/providers/migrations/0078_auto_20240813_1552.py +120 -0
  145. karrio/server/providers/migrations/0079_alter_carrier_options_alter_ratesheet_created_by.py +31 -0
  146. karrio/server/providers/migrations/0080_alter_aramexsettings_account_country_code_and_more.py +3025 -0
  147. karrio/server/providers/migrations/0081_remove_alliedexpresssettings_carrier_ptr_and_more.py +338 -0
  148. karrio/server/providers/migrations/0082_add_zone_identifiers.py +50 -0
  149. karrio/server/providers/migrations/0083_add_optimized_rate_sheet_structure.py +33 -0
  150. karrio/server/providers/migrations/0084_alter_servicelevel_currency.py +168 -0
  151. karrio/server/providers/migrations/0085_populate_dhl_parcel_de_oauth_credentials.py +82 -0
  152. karrio/server/providers/migrations/0086_rename_dhl_parcel_de_customer_number_to_billing_number.py +71 -0
  153. karrio/server/providers/migrations/__init__.py +0 -0
  154. karrio/server/providers/models/__init__.py +16 -0
  155. karrio/server/providers/models/carrier.py +387 -0
  156. karrio/server/providers/models/config.py +30 -0
  157. karrio/server/providers/models/service.py +192 -0
  158. karrio/server/providers/models/sheet.py +287 -0
  159. karrio/server/providers/models/template.py +39 -0
  160. karrio/server/providers/models/utils.py +58 -0
  161. karrio/server/providers/router.py +3 -0
  162. karrio/server/providers/serializers/__init__.py +3 -0
  163. karrio/server/providers/serializers/base.py +538 -0
  164. karrio/server/providers/signals.py +25 -0
  165. karrio/server/providers/templates/providers/oauth_callback.html +105 -0
  166. karrio/server/providers/tests/__init__.py +5 -0
  167. karrio/server/providers/tests/test_connections.py +895 -0
  168. karrio/server/providers/urls.py +11 -0
  169. karrio/server/providers/views/__init__.py +0 -0
  170. karrio/server/providers/views/carriers.py +267 -0
  171. karrio/server/providers/views/connections.py +496 -0
  172. karrio/server/samples.py +352 -0
  173. karrio/server/serializers/__init__.py +2 -0
  174. karrio/server/serializers/abstract.py +602 -0
  175. karrio/server/tracing/__init__.py +0 -0
  176. karrio/server/tracing/admin.py +63 -0
  177. karrio/server/tracing/apps.py +8 -0
  178. karrio/server/tracing/migrations/0001_initial.py +41 -0
  179. karrio/server/tracing/migrations/0002_auto_20220710_1307.py +22 -0
  180. karrio/server/tracing/migrations/0003_auto_20221105_0317.py +43 -0
  181. karrio/server/tracing/migrations/0004_tracingrecord_carrier_account_idx.py +24 -0
  182. karrio/server/tracing/migrations/0005_optimise_tracingrecord_request_log_idx.py +25 -0
  183. karrio/server/tracing/migrations/0006_alter_tracingrecord_options_and_more.py +49 -0
  184. karrio/server/tracing/migrations/0007_tracingrecord_tracing_created_at_idx.py +19 -0
  185. karrio/server/tracing/migrations/__init__.py +0 -0
  186. karrio/server/tracing/models.py +82 -0
  187. karrio/server/tracing/tests.py +3 -0
  188. karrio/server/tracing/utils.py +109 -0
  189. karrio/server/user/__init__.py +0 -0
  190. karrio/server/user/admin.py +96 -0
  191. karrio/server/user/apps.py +7 -0
  192. karrio/server/user/forms.py +35 -0
  193. karrio/server/user/migrations/0001_initial.py +41 -0
  194. karrio/server/user/migrations/0002_token.py +29 -0
  195. karrio/server/user/migrations/0003_token_test_mode.py +20 -0
  196. karrio/server/user/migrations/0004_group.py +26 -0
  197. karrio/server/user/migrations/0005_token_label.py +21 -0
  198. karrio/server/user/migrations/0006_workspaceconfig.py +63 -0
  199. karrio/server/user/migrations/0007_user_metadata.py +25 -0
  200. karrio/server/user/migrations/__init__.py +0 -0
  201. karrio/server/user/models.py +218 -0
  202. karrio/server/user/serializers.py +47 -0
  203. karrio/server/user/templates/registration/login.html +108 -0
  204. karrio/server/user/templates/registration/registration_confirm_email.html +10 -0
  205. karrio/server/user/templates/registration/registration_confirm_email.txt +3 -0
  206. karrio/server/user/tests.py +3 -0
  207. karrio/server/user/urls.py +10 -0
  208. karrio/server/user/utils.py +60 -0
  209. karrio/server/user/views.py +9 -0
  210. karrio_server_core-2025.5.dist-info/METADATA +32 -0
  211. karrio_server_core-2025.5.dist-info/RECORD +213 -0
  212. karrio_server_core-2025.5.dist-info/WHEEL +5 -0
  213. karrio_server_core-2025.5.dist-info/top_level.txt +2 -0
@@ -0,0 +1,394 @@
1
+ import attr
2
+ import typing
3
+ import jstruct
4
+ import karrio.core.units as units
5
+ from karrio.core.models import (
6
+ DocumentDetails,
7
+ Documents,
8
+ Images,
9
+ Parcel,
10
+ Message,
11
+ Address as BaseAddress,
12
+ TrackingRequest as BaseTrackingRequest,
13
+ ShipmentDetails,
14
+ Payment,
15
+ Customs,
16
+ RateRequest as BaseRateRequest,
17
+ ShipmentRequest as BaseShipmentRequest,
18
+ ShipmentCancelRequest,
19
+ ChargeDetails,
20
+ PickupRequest as BasePickupRequest,
21
+ PickupDetails,
22
+ PickupUpdateRequest as BasePickupUpdateRequest,
23
+ PickupCancelRequest as BasePickupCancelRequest,
24
+ ConfirmationDetails as Confirmation,
25
+ TrackingEvent,
26
+ TrackingInfo,
27
+ ManifestDocument,
28
+ TrackingDetails,
29
+ DocumentFile,
30
+ DocumentUploadRequest,
31
+ ManifestRequest,
32
+ ManifestDetails,
33
+ RequestPayload,
34
+ OAuthAuthorizePayload,
35
+ OAuthAuthorizeRequest,
36
+ WebhookEventDetails,
37
+ WebhookRegistrationDetails,
38
+ WebhookDeregistrationRequest,
39
+ WebhookRegistrationRequest,
40
+ DutiesCalculationRequest,
41
+ DutiesCalculationDetails,
42
+ InsuranceRequest,
43
+ InsuranceDetails,
44
+ )
45
+
46
+
47
+ COUNTRIES = [(c.name, c.name) for c in units.Country]
48
+ CURRENCIES = [(c.name, c.name) for c in units.Currency]
49
+ WEIGHT_UNITS = [(c.name, c.name) for c in units.WeightUnit]
50
+ DIMENSION_UNITS = [(c.name, c.name) for c in units.DimensionUnit]
51
+ CAPABILITIES_CHOICES = [(c, c) for c in units.CarrierCapabilities.get_capabilities()]
52
+ LABEL_TYPES = [(c.name, c.name) for c in list(units.LabelType)]
53
+
54
+
55
+ class CarrierSettings:
56
+ def __init__(
57
+ self,
58
+ carrier_name: str,
59
+ carrier_id: str,
60
+ test_mode: bool = None,
61
+ active: bool = None,
62
+ id: str = None,
63
+ display_name: str = None,
64
+ **kwargs
65
+ ):
66
+ self.carrier_name = carrier_name
67
+ self.display_name = display_name
68
+ self.carrier_id = carrier_id
69
+ self.active = active
70
+ self.test_mode = test_mode
71
+ self.id = id
72
+
73
+ for name, value in kwargs.items():
74
+ if name not in ["carrier_ptr", "created_by", "active_users", "active_orgs"]:
75
+ self.__setattr__(name, value)
76
+
77
+ # TODO: rename this to avoid confusion
78
+ def to_dict(self):
79
+ return {
80
+ name: value
81
+ for name, value in self.__dict__.items()
82
+ if name
83
+ not in [
84
+ "carrier_name",
85
+ "created_by",
86
+ "active",
87
+ "capabilities",
88
+ "active_users",
89
+ "active_orgs",
90
+ *([] if self.carrier_name == "generic" else ["display_name"]),
91
+ ]
92
+ }
93
+
94
+ @classmethod
95
+ def create(cls, data: dict):
96
+ return cls(**data)
97
+
98
+
99
+ @attr.s(auto_attribs=True)
100
+ class AddressValidation:
101
+ success: bool
102
+ meta: dict = {}
103
+
104
+
105
+ @attr.s(auto_attribs=True)
106
+ class Address(BaseAddress):
107
+ id: str = None
108
+ postal_code: str = None
109
+ city: str = None
110
+ person_name: str = None
111
+ company_name: str = None
112
+ country_code: str = None
113
+ email: str = None
114
+ phone_number: str = None
115
+
116
+ state_code: str = None
117
+ residential: bool = False
118
+
119
+ address_line1: str = ""
120
+ address_line2: str = None
121
+ street_number: str = None
122
+ suite: str = None
123
+
124
+ federal_tax_id: str = None
125
+ state_tax_id: str = None
126
+
127
+ validate_location: bool = None
128
+ validation: jstruct.JStruct[AddressValidation] = None
129
+
130
+
131
+ @attr.s(auto_attribs=True)
132
+ class PickupRequest(BasePickupRequest):
133
+ pickup_date: str
134
+ ready_time: str
135
+ closing_time: str
136
+ address: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
137
+
138
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel]
139
+ shipment_identifiers: typing.List[str] = []
140
+ package_location: str = None
141
+ metadata: typing.Dict = {}
142
+ options: typing.Dict = {}
143
+ instruction: str = None
144
+
145
+
146
+ @attr.s(auto_attribs=True)
147
+ class PickupUpdateRequest(BasePickupUpdateRequest):
148
+ confirmation_number: str
149
+ pickup_date: str
150
+ ready_time: str
151
+ closing_time: str
152
+ address: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
153
+
154
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel]
155
+ shipment_identifiers: typing.List[str] = []
156
+ package_location: str = None
157
+ options: typing.Dict = {}
158
+ instruction: str = None
159
+
160
+
161
+ @attr.s(auto_attribs=True)
162
+ class PickupCancelRequest(BasePickupCancelRequest):
163
+ confirmation_number: str
164
+
165
+ address: Address = jstruct.JStruct[Address]
166
+ pickup_date: str = None
167
+ reason: str = None
168
+ options: typing.Dict = {}
169
+
170
+
171
+ @attr.s(auto_attribs=True)
172
+ class Rate:
173
+ carrier_name: str
174
+ carrier_id: str
175
+
176
+ currency: str = None
177
+ transit_days: int = None
178
+ service: str = None
179
+ total_charge: float = 0.0
180
+ extra_charges: typing.List[ChargeDetails] = []
181
+ estimated_delivery: str = None
182
+ id: str = None
183
+ meta: dict = None
184
+ test_mode: bool = None
185
+ object_type: str = "rate"
186
+
187
+
188
+ @attr.s(auto_attribs=True)
189
+ class RateRequest(BaseRateRequest):
190
+ shipper: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
191
+ recipient: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
192
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel, jstruct.REQUIRED]
193
+
194
+ payment: Payment = jstruct.JStruct[Payment]
195
+ customs: Customs = jstruct.JStruct[Customs]
196
+ return_address: Address = jstruct.JStruct[Address]
197
+ billing_address: Address = jstruct.JStruct[Address]
198
+
199
+ services: typing.List[str] = []
200
+ options: typing.Dict = {}
201
+ reference: str = ""
202
+
203
+ carrier_ids: typing.List[str] = []
204
+
205
+
206
+ @attr.s(auto_attribs=True)
207
+ class ShipmentRequest(BaseShipmentRequest):
208
+ service: str
209
+ selected_rate_id: str # type: ignore
210
+
211
+ shipper: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
212
+ recipient: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
213
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel, jstruct.REQUIRED]
214
+ rates: typing.List[Rate] = jstruct.JList[Rate, jstruct.REQUIRED]
215
+
216
+ payment: Payment = jstruct.JStruct[Payment]
217
+ customs: Customs = jstruct.JStruct[Customs]
218
+ return_address: Address = jstruct.JStruct[Address]
219
+ billing_address: Address = jstruct.JStruct[Address]
220
+
221
+ options: typing.Dict = {}
222
+ reference: str = ""
223
+ label_type: str = None
224
+ id: str = None
225
+
226
+ metadata: typing.Dict = {}
227
+
228
+
229
+ @attr.s(auto_attribs=True)
230
+ class Shipment:
231
+ carrier_id: str
232
+ carrier_name: str
233
+ tracking_number: str
234
+ shipment_identifier: str
235
+ service: str
236
+ selected_rate_id: str
237
+
238
+ shipper: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
239
+ recipient: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
240
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel, jstruct.REQUIRED]
241
+ rates: typing.List[Rate] = jstruct.JList[Rate, jstruct.REQUIRED]
242
+ selected_rate: Rate = jstruct.JStruct[Rate, jstruct.REQUIRED]
243
+ docs: Documents = jstruct.JStruct[Documents, jstruct.REQUIRED]
244
+
245
+ payment: Payment = jstruct.JStruct[Payment]
246
+ customs: Customs = jstruct.JStruct[Customs]
247
+ billing_address: Address = jstruct.JStruct[Address]
248
+
249
+ options: typing.Dict = {}
250
+ reference: str = ""
251
+ label_type: str = None
252
+ tracking_url: str = None
253
+ tracker_id: str = None
254
+ status: str = ""
255
+ metadata: typing.Dict = {}
256
+ meta: dict = {}
257
+ id: str = None
258
+
259
+ messages: typing.List[Message] = jstruct.JList[Message]
260
+ created_at: str = None
261
+ test_mode: bool = None
262
+
263
+
264
+ @attr.s(auto_attribs=True)
265
+ class Pickup:
266
+ carrier_id: str
267
+ carrier_name: str
268
+
269
+ pickup_date: str
270
+ ready_time: str
271
+ closing_time: str
272
+ confirmation_number: str
273
+ address: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
274
+ parcels: typing.List[Parcel] = jstruct.JList[Parcel, jstruct.REQUIRED]
275
+
276
+ pickup_charge: ChargeDetails = jstruct.JStruct[ChargeDetails]
277
+ instruction: str = None
278
+ package_location: str = None
279
+ metadata: typing.Dict = {}
280
+ options: typing.Dict = {}
281
+ meta: dict = {}
282
+ id: str = None
283
+
284
+ messages: typing.List[Message] = jstruct.JList[Message]
285
+ test_mode: bool = None
286
+
287
+
288
+ @attr.s(auto_attribs=True)
289
+ class TrackingRequest(BaseTrackingRequest):
290
+ tracking_numbers: typing.List[str]
291
+ account_numer: str = None
292
+ reference: str = None
293
+ options: typing.Dict = {}
294
+ info: TrackingInfo = jstruct.JStruct[TrackingInfo]
295
+
296
+
297
+ @attr.s(auto_attribs=True)
298
+ class Tracking:
299
+ carrier_name: str
300
+ carrier_id: str
301
+ tracking_number: str
302
+ events: typing.List[TrackingEvent] = jstruct.JList[TrackingEvent]
303
+
304
+ status: str = "unknown"
305
+ info: TrackingInfo = jstruct.JStruct[TrackingInfo]
306
+ images: Images = jstruct.JStruct[Images]
307
+ estimated_delivery: str = None
308
+ delivered: bool = None
309
+ test_mode: bool = None
310
+ options: typing.Dict = {}
311
+ meta: dict = None
312
+ id: str = None
313
+
314
+
315
+ @attr.s(auto_attribs=True)
316
+ class DocumentUploadResponse:
317
+ carrier_name: str
318
+ carrier_id: str
319
+ documents: typing.List[DocumentDetails] = jstruct.JList[DocumentDetails]
320
+ reference: str = ""
321
+
322
+ test_mode: bool = None
323
+ options: typing.Dict = {}
324
+ meta: dict = None
325
+ id: str = None
326
+ messages: typing.List[Message] = jstruct.JList[Message]
327
+
328
+
329
+ @attr.s(auto_attribs=True)
330
+ class Manifest:
331
+ carrier_id: str
332
+ carrier_name: str
333
+
334
+ shipment_identifiers: typing.List[str]
335
+ address: Address = jstruct.JStruct[Address, jstruct.REQUIRED]
336
+ doc: ManifestDocument = jstruct.JStruct[ManifestDocument]
337
+
338
+ reference: str = None
339
+ metadata: typing.Dict = {}
340
+ options: typing.Dict = {}
341
+ meta: dict = {}
342
+ id: str = None
343
+
344
+ messages: typing.List[Message] = jstruct.JList[Message]
345
+ test_mode: bool = None
346
+
347
+
348
+ @attr.s(auto_attribs=True)
349
+ class ConfirmationResponse:
350
+ messages: typing.List[Message] = jstruct.JList[Message]
351
+ confirmation: Confirmation = jstruct.JStruct[Confirmation]
352
+
353
+
354
+ @attr.s(auto_attribs=True)
355
+ class PickupResponse:
356
+ messages: typing.List[Message] = jstruct.JList[Message]
357
+ pickup: Pickup = jstruct.JStruct[Pickup]
358
+
359
+
360
+ @attr.s(auto_attribs=True)
361
+ class RateResponse:
362
+ messages: typing.List[Message] = jstruct.JList[Message]
363
+ rates: typing.List[Rate] = jstruct.JList[Rate]
364
+
365
+
366
+ @attr.s(auto_attribs=True)
367
+ class TrackingResponse:
368
+ messages: typing.List[Message] = jstruct.JList[Message]
369
+ tracking: Tracking = jstruct.JStruct[Tracking]
370
+
371
+
372
+ @attr.s(auto_attribs=True)
373
+ class ManifestResponse:
374
+ messages: typing.List[Message] = jstruct.JList[Message]
375
+ manifest: Manifest = jstruct.JStruct[Manifest]
376
+
377
+
378
+ @attr.s(auto_attribs=True)
379
+ class DutiesResponse:
380
+ messages: typing.List[Message] = jstruct.JList[Message]
381
+ duties: DutiesCalculationDetails = jstruct.JStruct[DutiesCalculationDetails]
382
+
383
+
384
+ @attr.s(auto_attribs=True)
385
+ class InsuranceResponse:
386
+ messages: typing.List[Message] = jstruct.JList[Message]
387
+ insurance: InsuranceDetails = jstruct.JStruct[InsuranceDetails]
388
+
389
+
390
+ @attr.s(auto_attribs=True)
391
+ class Error:
392
+ code: str = None
393
+ message: str = None
394
+ details: typing.Dict = None
@@ -0,0 +1,187 @@
1
+ from constance import config
2
+ from django.urls import reverse
3
+ from rest_framework.request import Request
4
+
5
+ import karrio.lib as lib
6
+ import karrio.references as ref
7
+ import karrio.core.units as units
8
+ import karrio.server.conf as conf
9
+ import karrio.server.core.utils as utils
10
+
11
+
12
+ PACKAGE_MAPPERS = ref.collect_providers_data()
13
+ REFERENCE_MODELS = {
14
+ **ref.collect_references(),
15
+ "customs_content_type": {c.name: c.value for c in list(units.CustomsContentType)},
16
+ "incoterms": {c.name: c.value for c in list(units.Incoterm)},
17
+ }
18
+ REFERENCE_EXCLUSIONS = [
19
+ "currencies",
20
+ "incoterms",
21
+ "weight_units",
22
+ "dimension_units",
23
+ "payment_types",
24
+ "option_names",
25
+ "customs_content_type",
26
+ "options",
27
+ ]
28
+ CARRIER_NAMES = list(sorted(set([*REFERENCE_MODELS["carriers"].keys(), "generic"])))
29
+ CARRIER_HUBS = list(sorted(REFERENCE_MODELS["carrier_hubs"].keys()))
30
+ NON_HUBS_CARRIERS = [
31
+ carrier_name for carrier_name in CARRIER_NAMES if carrier_name not in CARRIER_HUBS
32
+ ]
33
+
34
+
35
+ def contextual_metadata(request: Request):
36
+ # Detect HTTPS from headers (for proxied environments like Caddy/ALB)
37
+ is_https = False
38
+ if hasattr(request, 'META'):
39
+ # Check X-Forwarded-Proto header (set by load balancers/proxies)
40
+ forwarded_proto = request.META.get('HTTP_X_FORWARDED_PROTO', '').lower()
41
+ # Check if request is secure (Django's built-in HTTPS detection)
42
+ is_secure = getattr(request, 'is_secure', lambda: False)()
43
+ is_https = forwarded_proto == 'https' or is_secure
44
+
45
+ if hasattr(request, "build_absolute_uri"):
46
+ _host: str = request.build_absolute_uri(
47
+ reverse("karrio.server.core:metadata", kwargs={})
48
+ )
49
+ # Override protocol if we detected HTTPS but build_absolute_uri returned HTTP
50
+ if is_https and _host.startswith('http://'):
51
+ _host = _host.replace('http://', 'https://', 1)
52
+ else:
53
+ _host = "/"
54
+
55
+ host = _host[:-1] if _host[-1] == "/" else _host
56
+ name = lib.identity(
57
+ getattr(conf.settings.tenant, "name", conf.settings.APP_NAME)
58
+ if conf.settings.MULTI_TENANTS
59
+ else getattr(config, "APP_NAME", None) or conf.settings.APP_NAME
60
+ )
61
+ website = lib.identity(
62
+ getattr(conf.settings.tenant, "website", conf.settings.APP_WEBSITE)
63
+ if conf.settings.MULTI_TENANTS
64
+ else getattr(config, "APP_WEBSITE", None) or conf.settings.APP_WEBSITE
65
+ )
66
+
67
+ # Batch fetch all feature flags
68
+ flag_names = [flag for flag, _ in conf.settings.FEATURE_FLAGS]
69
+
70
+ if conf.settings.MULTI_TENANTS:
71
+ # In multi-tenancy mode, feature flags come from tenant.feature_flags (JSON field)
72
+ # No N+1 issue since it's a single field access on an already-loaded tenant object
73
+ tenant = conf.settings.tenant
74
+ tenant_flags = getattr(tenant, "feature_flags", {}) if tenant else {}
75
+ feature_flags = {
76
+ flag: tenant_flags.get(flag, getattr(conf.settings, flag, None))
77
+ for flag in flag_names
78
+ }
79
+ else:
80
+ # In single-tenant mode, batch fetch from Constance to avoid N+1 queries
81
+ constance_values = utils.batch_get_constance_values(flag_names)
82
+ feature_flags = {
83
+ flag: (
84
+ constance_values.get(flag)
85
+ if flag in constance_values
86
+ else getattr(conf.settings, flag, None)
87
+ )
88
+ for flag in flag_names
89
+ }
90
+
91
+ return {
92
+ "VERSION": conf.settings.VERSION,
93
+ "APP_NAME": name,
94
+ "APP_WEBSITE": website,
95
+ "HOST": f"{host}/",
96
+ "ADMIN": f"{host}/admin",
97
+ "GRAPHQL": f"{host}/graphql",
98
+ "OPENAPI": f"{host}/openapi",
99
+ **feature_flags,
100
+ }
101
+
102
+
103
+ def contextual_reference(request: Request = None, reduced: bool = True):
104
+ import karrio.server.core.gateway as gateway
105
+ import karrio.server.core.validators as validators
106
+ import karrio.server.core.middleware as middleware
107
+
108
+ request = request or middleware.SessionContext.get_current_request()
109
+ is_authenticated = lib.identity(
110
+ request.user.is_authenticated if hasattr(request, "user") else False
111
+ )
112
+ references = {
113
+ **contextual_metadata(request),
114
+ "ADDRESS_AUTO_COMPLETE": lib.failsafe(
115
+ lambda: validators.Address.get_info(is_authenticated),
116
+ None,
117
+ ),
118
+ **{
119
+ k: v
120
+ for k, v in REFERENCE_MODELS.items()
121
+ if k not in (REFERENCE_EXCLUSIONS if reduced else [])
122
+ },
123
+ }
124
+
125
+ def _get_generic_carriers():
126
+ # Get all carriers, then filter by extension instead of hardcoded slug
127
+ system_custom_carriers = [
128
+ c for c in gateway.Carriers.list(system_only=True)
129
+ if c.ext == "generic"
130
+ ]
131
+ custom_carriers = [
132
+ c
133
+ for c in (
134
+ gateway.Carriers.list(context=request).exclude(is_system=True)
135
+ if is_authenticated
136
+ else []
137
+ )
138
+ if c.ext == "generic"
139
+ ]
140
+
141
+ extra_carriers = {
142
+ c.carrier_code: c.display_name
143
+ for c in custom_carriers
144
+ }
145
+ system_carriers = {
146
+ c.carrier_code: c.display_name
147
+ for c in system_custom_carriers
148
+ }
149
+ extra_services = {
150
+ c.carrier_code: {
151
+ s.service_code: s.service_code
152
+ for s in c.services
153
+ or [
154
+ lib.to_object(lib.models.ServiceLevel, _)
155
+ for _ in references["service_levels"][c.ext]
156
+ ]
157
+ }
158
+ for c in custom_carriers
159
+ }
160
+ extra_service_names = {
161
+ name: {key: key.upper().replace("_", " ") for key, _ in value.items()}
162
+ for name, value in extra_services.items()
163
+ }
164
+
165
+ references.update(
166
+ dict(
167
+ custom_carriers=extra_carriers,
168
+ services={**references["services"], **extra_services},
169
+ carriers={**references["carriers"], **system_carriers},
170
+ service_names={**references["service_names"], **extra_service_names},
171
+ )
172
+ )
173
+
174
+ if request is not None and "generic" in CARRIER_NAMES:
175
+ _get_generic_carriers()
176
+
177
+ return references
178
+
179
+
180
+ def get_carrier_details(
181
+ carrier_name: str,
182
+ contextual_reference: dict = None,
183
+ ) -> dict:
184
+ return ref.get_carrier_details(
185
+ carrier_name,
186
+ contextual_reference=contextual_reference or contextual_reference(),
187
+ )