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.
- karrio/server/conf.py +54 -0
- karrio/server/core/__init__.py +3 -0
- karrio/server/core/admin.py +1 -0
- karrio/server/core/apps.py +10 -0
- karrio/server/core/authentication.py +347 -0
- karrio/server/core/config.py +31 -0
- karrio/server/core/context_processors.py +12 -0
- karrio/server/core/datatypes.py +394 -0
- karrio/server/core/dataunits.py +187 -0
- karrio/server/core/exceptions.py +404 -0
- karrio/server/core/fields.py +12 -0
- karrio/server/core/filters.py +837 -0
- karrio/server/core/gateway.py +1011 -0
- karrio/server/core/logging.py +403 -0
- karrio/server/core/management/commands/cli.py +19 -0
- karrio/server/core/management/commands/create_oauth_client.py +41 -0
- karrio/server/core/management/commands/runserver.py +5 -0
- karrio/server/core/middleware.py +197 -0
- karrio/server/core/migrations/0001_initial.py +28 -0
- karrio/server/core/migrations/0002_apilogindex.py +69 -0
- karrio/server/core/migrations/0003_apilogindex_test_mode.py +62 -0
- karrio/server/core/migrations/0004_metafield.py +74 -0
- karrio/server/core/migrations/0005_alter_metafield_type_alter_metafield_value.py +23 -0
- karrio/server/core/migrations/0006_add_api_log_requested_at_index.py +22 -0
- karrio/server/core/migrations/__init__.py +0 -0
- karrio/server/core/models/__init__.py +48 -0
- karrio/server/core/models/base.py +103 -0
- karrio/server/core/models/entity.py +24 -0
- karrio/server/core/models/metafield.py +144 -0
- karrio/server/core/models/third_party.py +21 -0
- karrio/server/core/oauth_validators.py +170 -0
- karrio/server/core/permissions.py +36 -0
- karrio/server/core/renderers.py +11 -0
- karrio/server/core/router.py +3 -0
- karrio/server/core/serializers.py +1971 -0
- karrio/server/core/signals.py +55 -0
- karrio/server/core/telemetry.py +573 -0
- karrio/server/core/tests.py +99 -0
- karrio/server/core/tests_resource_token.py +411 -0
- karrio/server/core/urls.py +12 -0
- karrio/server/core/utils.py +1025 -0
- karrio/server/core/validators.py +264 -0
- karrio/server/core/views/__init__.py +2 -0
- karrio/server/core/views/api.py +133 -0
- karrio/server/core/views/metadata.py +44 -0
- karrio/server/core/views/oauth.py +75 -0
- karrio/server/core/views/references.py +82 -0
- karrio/server/core/views/schema.py +310 -0
- karrio/server/filters/__init__.py +2 -0
- karrio/server/filters/abstract.py +26 -0
- karrio/server/iam/__init__.py +0 -0
- karrio/server/iam/admin.py +3 -0
- karrio/server/iam/apps.py +21 -0
- karrio/server/iam/migrations/0001_initial.py +33 -0
- karrio/server/iam/migrations/__init__.py +0 -0
- karrio/server/iam/models.py +48 -0
- karrio/server/iam/permissions.py +155 -0
- karrio/server/iam/serializers.py +54 -0
- karrio/server/iam/signals.py +18 -0
- karrio/server/iam/tests.py +3 -0
- karrio/server/iam/views.py +3 -0
- karrio/server/openapi.py +75 -0
- karrio/server/providers/__init__.py +1 -0
- karrio/server/providers/admin.py +364 -0
- karrio/server/providers/apps.py +10 -0
- karrio/server/providers/management/commands/migrate_rate_sheets.py +101 -0
- karrio/server/providers/migrations/0001_initial.py +140 -0
- karrio/server/providers/migrations/0002_carrier_active.py +18 -0
- karrio/server/providers/migrations/0003_auto_20201230_0820.py +24 -0
- karrio/server/providers/migrations/0004_auto_20210212_0554.py +178 -0
- karrio/server/providers/migrations/0005_auto_20210212_0555.py +18 -0
- karrio/server/providers/migrations/0006_australiapostsettings.py +29 -0
- karrio/server/providers/migrations/0007_auto_20210213_0206.py +21 -0
- karrio/server/providers/migrations/0008_auto_20210214_0409.py +30 -0
- karrio/server/providers/migrations/0009_auto_20210308_0302.py +18 -0
- karrio/server/providers/migrations/0010_auto_20210409_0852.py +32 -0
- karrio/server/providers/migrations/0011_auto_20210409_0853.py +21 -0
- karrio/server/providers/migrations/0012_alter_carrier_options.py +17 -0
- karrio/server/providers/migrations/0013_tntsettings.py +30 -0
- karrio/server/providers/migrations/0014_auto_20210612_1608.py +46 -0
- karrio/server/providers/migrations/0015_auto_20210615_1601.py +28 -0
- karrio/server/providers/migrations/0016_alter_purolatorsettings_user_token.py +18 -0
- karrio/server/providers/migrations/0017_auto_20210805_0359.py +1293 -0
- karrio/server/providers/migrations/0018_alter_fedexsettings_user_key.py +18 -0
- karrio/server/providers/migrations/0019_dhlpolandsettings_servicelevel.py +65 -0
- karrio/server/providers/migrations/0020_genericsettings_labeltemplate.py +52 -0
- karrio/server/providers/migrations/0021_auto_20211231_2353.py +40 -0
- karrio/server/providers/migrations/0022_carrier_metadata.py +18 -0
- karrio/server/providers/migrations/0023_auto_20220124_1916.py +27 -0
- karrio/server/providers/migrations/0024_alter_genericsettings_custom_carrier_name.py +19 -0
- karrio/server/providers/migrations/0025_alter_servicelevel_service_code.py +19 -0
- karrio/server/providers/migrations/0026_auto_20220208_0132.py +59 -0
- karrio/server/providers/migrations/0027_auto_20220304_1340.py +29 -0
- karrio/server/providers/migrations/0028_auto_20220323_1500.py +33 -0
- karrio/server/providers/migrations/0029_easypostsettings.py +27 -0
- karrio/server/providers/migrations/0030_amazonmwssettings.py +29 -0
- karrio/server/providers/migrations/0031_delete_amazonmwssettings.py +18 -0
- karrio/server/providers/migrations/0032_alter_carrier_test.py +18 -0
- karrio/server/providers/migrations/0033_auto_20220708_1350.py +22 -0
- karrio/server/providers/migrations/0034_amazonmwssettings_dpdhlsettings.py +47 -0
- karrio/server/providers/migrations/0035_alter_carrier_capabilities.py +43 -0
- karrio/server/providers/migrations/0036_upsfreightsettings.py +31 -0
- karrio/server/providers/migrations/0037_chronopostsettings.py +29 -0
- karrio/server/providers/migrations/0038_alter_genericsettings_label_template.py +19 -0
- karrio/server/providers/migrations/0039_auto_20220906_0612.py +23 -0
- karrio/server/providers/migrations/0040_dpdhlsettings_services.py +18 -0
- karrio/server/providers/migrations/0041_auto_20221105_0705.py +38 -0
- karrio/server/providers/migrations/0042_auto_20221215_1642.py +23 -0
- karrio/server/providers/migrations/0043_alter_genericsettings_account_number_and_more.py +39 -0
- karrio/server/providers/migrations/0044_carrier_carrier_capabilities.py +64 -0
- karrio/server/providers/migrations/0045_alter_carrier_active_alter_carrier_carrier_id.py +31 -0
- karrio/server/providers/migrations/0046_remove_dpdhlsettings_signature_and_more.py +41 -0
- karrio/server/providers/migrations/0047_dpdsettings.py +286 -0
- karrio/server/providers/migrations/0048_servicelevel_min_weight_servicelevel_transit_days_and_more.py +64 -0
- karrio/server/providers/migrations/0049_boxknightsettings_geodissettings_lapostesettings_and_more.py +156 -0
- karrio/server/providers/migrations/0050_carrier_is_system_alter_carrier_metadata_and_more.py +106 -0
- karrio/server/providers/migrations/0051_rename_username_upssettings_client_id_and_more.py +31 -0
- karrio/server/providers/migrations/0052_alter_upssettings_account_number_and_more.py +20 -0
- karrio/server/providers/migrations/0053_locate2usettings.py +281 -0
- karrio/server/providers/migrations/0054_zoom2usettings.py +280 -0
- karrio/server/providers/migrations/0055_rename_amazonmwssettings_amazonshippingsettings_and_more.py +44 -0
- karrio/server/providers/migrations/0056_asendiaussettings_geodissettings_code_client_and_more.py +75 -0
- karrio/server/providers/migrations/0057_alter_servicelevel_weight_unit_belgianpostsettings.py +51 -0
- karrio/server/providers/migrations/0058_alliedexpresssettings.py +38 -0
- karrio/server/providers/migrations/0059_ratesheet.py +81 -0
- karrio/server/providers/migrations/0060_belgianpostsettings_rate_sheet_and_more.py +73 -0
- karrio/server/providers/migrations/0061_alliedexpresssettings_service_type.py +17 -0
- karrio/server/providers/migrations/0062_sendlesettings_account_country_code.py +257 -0
- karrio/server/providers/migrations/0063_servicelevel_metadata.py +25 -0
- karrio/server/providers/migrations/0064_alliedexpresslocalsettings.py +43 -0
- karrio/server/providers/migrations/0065_servicelevel_carrier_service_code_and_more.py +66 -0
- karrio/server/providers/migrations/0066_rename_fedexsettings_fedexwssettings_and_more.py +28 -0
- karrio/server/providers/migrations/0067_fedexsettings.py +283 -0
- karrio/server/providers/migrations/0068_fedexsettings_track_api_key_and_more.py +38 -0
- karrio/server/providers/migrations/0069_alter_canadapostsettings_contract_id_and_more.py +23 -0
- karrio/server/providers/migrations/0070_tgesettings_alter_carrier_capabilities.py +65 -0
- karrio/server/providers/migrations/0071_alter_tgesettings_my_toll_token.py +18 -0
- karrio/server/providers/migrations/0072_rename_eshippersettings_eshipperxmlsettings_and_more.py +28 -0
- karrio/server/providers/migrations/0073_delete_eshipperxmlsettings.py +41 -0
- karrio/server/providers/migrations/0074_eshippersettings.py +38 -0
- karrio/server/providers/migrations/0075_haypostsettings.py +40 -0
- karrio/server/providers/migrations/0076_rename_customer_registration_id_uspsinternationalsettings_account_number_and_more.py +125 -0
- karrio/server/providers/migrations/0077_uspswtinternationalsettings_uspswtsettings_and_more.py +165 -0
- karrio/server/providers/migrations/0078_auto_20240813_1552.py +120 -0
- karrio/server/providers/migrations/0079_alter_carrier_options_alter_ratesheet_created_by.py +31 -0
- karrio/server/providers/migrations/0080_alter_aramexsettings_account_country_code_and_more.py +3025 -0
- karrio/server/providers/migrations/0081_remove_alliedexpresssettings_carrier_ptr_and_more.py +338 -0
- karrio/server/providers/migrations/0082_add_zone_identifiers.py +50 -0
- karrio/server/providers/migrations/0083_add_optimized_rate_sheet_structure.py +33 -0
- karrio/server/providers/migrations/0084_alter_servicelevel_currency.py +168 -0
- karrio/server/providers/migrations/0085_populate_dhl_parcel_de_oauth_credentials.py +82 -0
- karrio/server/providers/migrations/0086_rename_dhl_parcel_de_customer_number_to_billing_number.py +71 -0
- karrio/server/providers/migrations/__init__.py +0 -0
- karrio/server/providers/models/__init__.py +16 -0
- karrio/server/providers/models/carrier.py +387 -0
- karrio/server/providers/models/config.py +30 -0
- karrio/server/providers/models/service.py +192 -0
- karrio/server/providers/models/sheet.py +287 -0
- karrio/server/providers/models/template.py +39 -0
- karrio/server/providers/models/utils.py +58 -0
- karrio/server/providers/router.py +3 -0
- karrio/server/providers/serializers/__init__.py +3 -0
- karrio/server/providers/serializers/base.py +538 -0
- karrio/server/providers/signals.py +25 -0
- karrio/server/providers/templates/providers/oauth_callback.html +105 -0
- karrio/server/providers/tests/__init__.py +5 -0
- karrio/server/providers/tests/test_connections.py +895 -0
- karrio/server/providers/urls.py +11 -0
- karrio/server/providers/views/__init__.py +0 -0
- karrio/server/providers/views/carriers.py +267 -0
- karrio/server/providers/views/connections.py +496 -0
- karrio/server/samples.py +352 -0
- karrio/server/serializers/__init__.py +2 -0
- karrio/server/serializers/abstract.py +602 -0
- karrio/server/tracing/__init__.py +0 -0
- karrio/server/tracing/admin.py +63 -0
- karrio/server/tracing/apps.py +8 -0
- karrio/server/tracing/migrations/0001_initial.py +41 -0
- karrio/server/tracing/migrations/0002_auto_20220710_1307.py +22 -0
- karrio/server/tracing/migrations/0003_auto_20221105_0317.py +43 -0
- karrio/server/tracing/migrations/0004_tracingrecord_carrier_account_idx.py +24 -0
- karrio/server/tracing/migrations/0005_optimise_tracingrecord_request_log_idx.py +25 -0
- karrio/server/tracing/migrations/0006_alter_tracingrecord_options_and_more.py +49 -0
- karrio/server/tracing/migrations/0007_tracingrecord_tracing_created_at_idx.py +19 -0
- karrio/server/tracing/migrations/__init__.py +0 -0
- karrio/server/tracing/models.py +82 -0
- karrio/server/tracing/tests.py +3 -0
- karrio/server/tracing/utils.py +109 -0
- karrio/server/user/__init__.py +0 -0
- karrio/server/user/admin.py +96 -0
- karrio/server/user/apps.py +7 -0
- karrio/server/user/forms.py +35 -0
- karrio/server/user/migrations/0001_initial.py +41 -0
- karrio/server/user/migrations/0002_token.py +29 -0
- karrio/server/user/migrations/0003_token_test_mode.py +20 -0
- karrio/server/user/migrations/0004_group.py +26 -0
- karrio/server/user/migrations/0005_token_label.py +21 -0
- karrio/server/user/migrations/0006_workspaceconfig.py +63 -0
- karrio/server/user/migrations/0007_user_metadata.py +25 -0
- karrio/server/user/migrations/__init__.py +0 -0
- karrio/server/user/models.py +218 -0
- karrio/server/user/serializers.py +47 -0
- karrio/server/user/templates/registration/login.html +108 -0
- karrio/server/user/templates/registration/registration_confirm_email.html +10 -0
- karrio/server/user/templates/registration/registration_confirm_email.txt +3 -0
- karrio/server/user/tests.py +3 -0
- karrio/server/user/urls.py +10 -0
- karrio/server/user/utils.py +60 -0
- karrio/server/user/views.py +9 -0
- karrio_server_core-2025.5.dist-info/METADATA +32 -0
- karrio_server_core-2025.5.dist-info/RECORD +213 -0
- karrio_server_core-2025.5.dist-info/WHEEL +5 -0
- karrio_server_core-2025.5.dist-info/top_level.txt +2 -0
karrio/server/openapi.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from drf_spectacular.types import *
|
|
3
|
+
from drf_spectacular.utils import *
|
|
4
|
+
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class JWTAuthentication(OpenApiAuthenticationExtension):
|
|
8
|
+
target_class = "karrio.server.core.authentication.JWTAuthentication"
|
|
9
|
+
name = "JWT"
|
|
10
|
+
|
|
11
|
+
def get_security_definition(self, auto_schema):
|
|
12
|
+
return {
|
|
13
|
+
"in": "header",
|
|
14
|
+
"type": "apiKey",
|
|
15
|
+
"scheme": "bearer",
|
|
16
|
+
"bearerFormat": "JWT",
|
|
17
|
+
"name": "Authorization",
|
|
18
|
+
"description": "Authorization: Bearer xxx.xxx.xxx",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TokenAuthentication(OpenApiAuthenticationExtension):
|
|
23
|
+
target_class = "karrio.server.core.authentication.TokenAuthentication"
|
|
24
|
+
name = "Token"
|
|
25
|
+
|
|
26
|
+
def get_security_definition(self, auto_schema):
|
|
27
|
+
return {
|
|
28
|
+
"type": "apiKey",
|
|
29
|
+
"in": "header",
|
|
30
|
+
"name": "Authorization",
|
|
31
|
+
"description": "Authorization: Token key_xxxxxxxx",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TokenBasicAuthentication(OpenApiAuthenticationExtension):
|
|
36
|
+
target_class = "karrio.server.core.authentication.TokenBasicAuthentication"
|
|
37
|
+
name = "TokenBasic"
|
|
38
|
+
|
|
39
|
+
def get_security_definition(self, auto_schema):
|
|
40
|
+
return {
|
|
41
|
+
"type": "http",
|
|
42
|
+
"scheme": "basic",
|
|
43
|
+
"name": "Authorization",
|
|
44
|
+
"description": "-u key_xxxxxxxx:",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class OAuth2Authentication(OpenApiAuthenticationExtension):
|
|
49
|
+
target_class = "karrio.server.core.authentication.OAuth2Authentication"
|
|
50
|
+
name = "OAuth2"
|
|
51
|
+
|
|
52
|
+
def get_security_definition(self, auto_schema):
|
|
53
|
+
return {
|
|
54
|
+
"type": "oauth2",
|
|
55
|
+
"in": "header",
|
|
56
|
+
"name": "Authorization",
|
|
57
|
+
"flows": {
|
|
58
|
+
"authorizationCode": {
|
|
59
|
+
"authorizationUrl": "/oauth/authorize/",
|
|
60
|
+
"tokenUrl": "/oauth/token/",
|
|
61
|
+
"scopes": settings.OAUTH2_PROVIDER["SCOPES"],
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"description": "Authorization: Bearer xxxxxxxx",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def custom_postprocessing_hook(result, generator, request, public):
|
|
69
|
+
if "docs" in request.query_params:
|
|
70
|
+
for path in result["paths"].values():
|
|
71
|
+
for method in path.values():
|
|
72
|
+
if "x-operationId" in method:
|
|
73
|
+
method["operationId"] = method["x-operationId"]
|
|
74
|
+
|
|
75
|
+
return result
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from django import forms
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.contrib import admin
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.contrib.auth import get_user_model
|
|
7
|
+
from django.utils.translation import gettext_lazy as _
|
|
8
|
+
|
|
9
|
+
import karrio.lib as lib
|
|
10
|
+
import karrio.references as ref
|
|
11
|
+
import karrio.server.core.utils as utils
|
|
12
|
+
import karrio.server.serializers as serializers
|
|
13
|
+
import karrio.server.core.dataunits as dataunits
|
|
14
|
+
import karrio.server.providers.models as providers
|
|
15
|
+
|
|
16
|
+
User = get_user_model()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def model_admin(ext: str, carrierProxy):
|
|
20
|
+
references = dataunits.contextual_reference(reduced=False)
|
|
21
|
+
class_name = carrierProxy.__name__
|
|
22
|
+
connection_fields = references["connection_fields"].get(ext) or {}
|
|
23
|
+
connection_configs = references["connection_configs"].get(ext) or {}
|
|
24
|
+
carrier_services = (references["services"].get(ext) or {}).keys()
|
|
25
|
+
carrier_options = (references["options"].get(ext) or {}).keys()
|
|
26
|
+
|
|
27
|
+
class _Form(forms.ModelForm):
|
|
28
|
+
|
|
29
|
+
for key, field in connection_fields.items():
|
|
30
|
+
if field["type"] == "boolean":
|
|
31
|
+
locals()[key] = forms.NullBooleanField(
|
|
32
|
+
required=field.get("required", False),
|
|
33
|
+
initial=None,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
elif field["type"] == "integer":
|
|
37
|
+
locals()[key] = forms.IntegerField(
|
|
38
|
+
required=field.get("required", False),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
elif field["type"] == "float":
|
|
42
|
+
locals()[key] = forms.FloatField(
|
|
43
|
+
required=field.get("required", False),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
elif field["type"] == "string" and any(field.get("enum", [])):
|
|
47
|
+
locals()[key] = forms.ChoiceField(
|
|
48
|
+
choices=[(None, ""), *[(_, _) for _ in field.get("enum", [])]],
|
|
49
|
+
widget=forms.Select(attrs={"class": "vTextField"}),
|
|
50
|
+
required=field.get("required", False),
|
|
51
|
+
initial=field.get("default"),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
elif field["type"] == "string":
|
|
55
|
+
locals()[key] = forms.CharField(
|
|
56
|
+
required=field.get("required", False),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
for key, field in connection_configs.items():
|
|
63
|
+
|
|
64
|
+
if key == "shipping_services":
|
|
65
|
+
shipping_services = forms.MultipleChoiceField(
|
|
66
|
+
choices=[(_, _) for _ in carrier_services],
|
|
67
|
+
widget=forms.SelectMultiple(attrs={"class": "vTextField"}),
|
|
68
|
+
required=False,
|
|
69
|
+
initial=None,
|
|
70
|
+
)
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
if key == "shipping_options":
|
|
74
|
+
shipping_options = forms.MultipleChoiceField(
|
|
75
|
+
choices=[(_, _) for _ in carrier_options],
|
|
76
|
+
widget=forms.SelectMultiple(attrs={"class": "vTextField"}),
|
|
77
|
+
required=False,
|
|
78
|
+
initial=None,
|
|
79
|
+
)
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
if field["type"] == "boolean":
|
|
83
|
+
locals()[key] = forms.NullBooleanField(
|
|
84
|
+
required=False,
|
|
85
|
+
initial=None,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
elif field["type"] == "integer":
|
|
89
|
+
locals()[key] = forms.IntegerField(
|
|
90
|
+
required=False,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
elif field["type"] == "float":
|
|
94
|
+
locals()[key] = forms.FloatField(
|
|
95
|
+
required=False,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
elif field["type"] == "string" and any(field.get("enum", [])):
|
|
99
|
+
locals()[key] = forms.ChoiceField(
|
|
100
|
+
choices=[(None, ""), *[(_, _) for _ in field.get("enum", [])]],
|
|
101
|
+
widget=forms.Select(attrs={"class": "vTextField"}),
|
|
102
|
+
required=field.get("required", False),
|
|
103
|
+
initial=field.get("default"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
elif field["type"] == "string":
|
|
107
|
+
locals()[key] = forms.CharField(
|
|
108
|
+
required=False,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
class Meta:
|
|
115
|
+
model = carrierProxy
|
|
116
|
+
fields = "__all__"
|
|
117
|
+
|
|
118
|
+
def __init__(self, *args, instance: providers.Carrier = None, **kwargs):
|
|
119
|
+
if instance is not None:
|
|
120
|
+
kwargs.update({"instance": instance})
|
|
121
|
+
credentials = instance.credentials
|
|
122
|
+
config = providers.Carrier.resolve_config(
|
|
123
|
+
instance, is_system_config=True
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
for key in [
|
|
127
|
+
_ for _ in self.base_fields.keys() if _ in connection_fields.keys()
|
|
128
|
+
]:
|
|
129
|
+
self.base_fields[key].initial = (
|
|
130
|
+
None if credentials is None else credentials.get(key)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
for key in [
|
|
134
|
+
_ for _ in self.base_fields.keys() if _ in connection_configs.keys()
|
|
135
|
+
]:
|
|
136
|
+
self.base_fields[key].initial = (
|
|
137
|
+
None if config is None else config.config.get(key)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
super(_Form, self).__init__(*args, **kwargs)
|
|
141
|
+
|
|
142
|
+
def save(self, commit: bool = True):
|
|
143
|
+
config_data = lib.to_dict(
|
|
144
|
+
{key: self.cleaned_data.get(key) for key in connection_configs.keys()}
|
|
145
|
+
)
|
|
146
|
+
credentials_data = lib.to_dict(
|
|
147
|
+
{key: self.cleaned_data.get(key) for key in connection_fields.keys()}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
for key in connection_fields.keys():
|
|
151
|
+
if key in self.cleaned_data:
|
|
152
|
+
self.cleaned_data.pop(key)
|
|
153
|
+
|
|
154
|
+
for key in connection_configs.keys():
|
|
155
|
+
if key in self.cleaned_data:
|
|
156
|
+
self.cleaned_data.pop(key)
|
|
157
|
+
|
|
158
|
+
carrier = super(_Form, self).save(commit)
|
|
159
|
+
|
|
160
|
+
if any(connection_fields.keys()) and (commit or carrier.pk is not None):
|
|
161
|
+
carrier.credentials = serializers.process_dictionaries_mutations(
|
|
162
|
+
["credentials"], credentials_data, carrier
|
|
163
|
+
)
|
|
164
|
+
carrier.save()
|
|
165
|
+
|
|
166
|
+
if any(connection_configs.keys()) and (commit or carrier.pk is not None):
|
|
167
|
+
config = providers.Carrier.resolve_config(
|
|
168
|
+
carrier, is_system_config=True
|
|
169
|
+
)
|
|
170
|
+
created_by = getattr(config, "created_by", self.request.user)
|
|
171
|
+
config_value = lib.to_dict(
|
|
172
|
+
serializers.process_dictionaries_mutations(
|
|
173
|
+
["config"], config_data, config
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if config is None and len(config_value.keys()) == 0:
|
|
178
|
+
# Skip configuration persistence...
|
|
179
|
+
return carrier
|
|
180
|
+
|
|
181
|
+
# Save or update the carrier config...
|
|
182
|
+
lib.identity(
|
|
183
|
+
providers.CarrierConfig.objects.create(
|
|
184
|
+
created_by=created_by,
|
|
185
|
+
carrier=carrier,
|
|
186
|
+
config=config_value,
|
|
187
|
+
)
|
|
188
|
+
if config is None
|
|
189
|
+
else providers.CarrierConfig.objects.filter(carrier=carrier).update(
|
|
190
|
+
config=config_value
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return carrier
|
|
195
|
+
|
|
196
|
+
_form: forms.ModelForm = type(f"{class_name}AdminForm", (_Form,), {})
|
|
197
|
+
_fields = _form.base_fields.keys()
|
|
198
|
+
|
|
199
|
+
class _Admin(admin.ModelAdmin):
|
|
200
|
+
form = _form
|
|
201
|
+
inlines = []
|
|
202
|
+
list_display = ("__str__", "test_mode", "active")
|
|
203
|
+
exclude = ["active_users"]
|
|
204
|
+
fieldsets = [
|
|
205
|
+
(
|
|
206
|
+
None,
|
|
207
|
+
{
|
|
208
|
+
"fields": [
|
|
209
|
+
_
|
|
210
|
+
for _ in _fields
|
|
211
|
+
if _
|
|
212
|
+
not in [
|
|
213
|
+
*connection_fields.keys(),
|
|
214
|
+
*connection_configs.keys(),
|
|
215
|
+
"carrier_code",
|
|
216
|
+
"credentials",
|
|
217
|
+
"active_users",
|
|
218
|
+
"is_system",
|
|
219
|
+
"rate_sheet",
|
|
220
|
+
"metadata",
|
|
221
|
+
]
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
),
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
if any(connection_fields.keys()):
|
|
228
|
+
fieldsets += [
|
|
229
|
+
( # type: ignore
|
|
230
|
+
"Connection Fields",
|
|
231
|
+
{
|
|
232
|
+
"fields": [_ for _ in connection_fields.keys() if _ in _fields],
|
|
233
|
+
},
|
|
234
|
+
),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
if any(connection_configs.keys()):
|
|
238
|
+
fieldsets += [
|
|
239
|
+
( # type: ignore
|
|
240
|
+
"Connection Config",
|
|
241
|
+
{
|
|
242
|
+
"fields": [
|
|
243
|
+
_ for _ in connection_configs.keys() if _ in _fields
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
),
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
formfield_overrides = {
|
|
250
|
+
models.CharField: {
|
|
251
|
+
"widget": forms.TextInput(
|
|
252
|
+
attrs={
|
|
253
|
+
"type": "text",
|
|
254
|
+
"readonly": "true",
|
|
255
|
+
"class": "vTextField",
|
|
256
|
+
"data - lpignore": "true",
|
|
257
|
+
"autocomplete": "keep-off",
|
|
258
|
+
"onfocus": "this.removeAttribute('readonly');",
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
},
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if settings.MULTI_ORGANIZATIONS:
|
|
265
|
+
|
|
266
|
+
class ActiveOrgInline(admin.TabularInline):
|
|
267
|
+
model = carrierProxy.active_orgs.through
|
|
268
|
+
verbose_name = "Activated for organization"
|
|
269
|
+
extra = 0
|
|
270
|
+
|
|
271
|
+
def get_formset(self, request, obj, **kwargs):
|
|
272
|
+
from karrio.server.orgs.models import Organization
|
|
273
|
+
|
|
274
|
+
initial = []
|
|
275
|
+
orgs = Organization.objects.filter(
|
|
276
|
+
users__id=request.user.id
|
|
277
|
+
).distinct()
|
|
278
|
+
self.max_num = orgs.count()
|
|
279
|
+
|
|
280
|
+
if obj is None and request.method == "GET":
|
|
281
|
+
self.extra = orgs.count()
|
|
282
|
+
initial += [{"organization": o.id} for o in orgs]
|
|
283
|
+
|
|
284
|
+
formset = super().get_formset(request, obj, **kwargs)
|
|
285
|
+
formset.__init__ = functools.partialmethod(
|
|
286
|
+
formset.__init__, initial=initial
|
|
287
|
+
)
|
|
288
|
+
organization_field = formset.form.base_fields["organization"]
|
|
289
|
+
organization_field.queryset = orgs
|
|
290
|
+
organization_field.widget.can_add_related = False
|
|
291
|
+
organization_field.widget.can_change_related = False
|
|
292
|
+
|
|
293
|
+
return formset
|
|
294
|
+
|
|
295
|
+
inlines += [ActiveOrgInline]
|
|
296
|
+
|
|
297
|
+
else:
|
|
298
|
+
|
|
299
|
+
class ActiveUserInline(admin.TabularInline):
|
|
300
|
+
model = carrierProxy.active_users.through
|
|
301
|
+
exta = 0
|
|
302
|
+
verbose_name = "Activated for user"
|
|
303
|
+
|
|
304
|
+
def get_formset(self, request, obj, **kwargs):
|
|
305
|
+
initial = []
|
|
306
|
+
users = User.objects.all()
|
|
307
|
+
self.max_num = users.count()
|
|
308
|
+
|
|
309
|
+
if obj is None and request.method == "GET":
|
|
310
|
+
self.extra = users.count()
|
|
311
|
+
initial += [{"user": o.id} for o in users]
|
|
312
|
+
|
|
313
|
+
formset = super().get_formset(request, obj, **kwargs)
|
|
314
|
+
formset.__init__ = functools.partialmethod(
|
|
315
|
+
formset.__init__, initial=initial
|
|
316
|
+
)
|
|
317
|
+
user_field = formset.form.base_fields["user"]
|
|
318
|
+
user_field.queryset = users
|
|
319
|
+
user_field.widget.can_add_related = False
|
|
320
|
+
user_field.widget.can_change_related = False
|
|
321
|
+
|
|
322
|
+
return formset
|
|
323
|
+
|
|
324
|
+
inlines += [ActiveUserInline]
|
|
325
|
+
|
|
326
|
+
def get_queryset(self, request):
|
|
327
|
+
query = super().get_queryset(request)
|
|
328
|
+
return query.filter(models.Q(is_system=True) | models.Q(created_by=None))
|
|
329
|
+
|
|
330
|
+
def get_form(self, request, *args, **kwargs):
|
|
331
|
+
form = super(_Admin, self).get_form(request, *args, **kwargs)
|
|
332
|
+
form.request = request
|
|
333
|
+
|
|
334
|
+
# Customize capabilities options specific to a carrier.
|
|
335
|
+
raw_capabilities = ref.get_carrier_capabilities(ext)
|
|
336
|
+
form.base_fields["capabilities"].choices = [
|
|
337
|
+
(c, c) for c in raw_capabilities
|
|
338
|
+
]
|
|
339
|
+
form.base_fields["capabilities"].initial = raw_capabilities
|
|
340
|
+
|
|
341
|
+
return form
|
|
342
|
+
|
|
343
|
+
def save_model(self, request, obj, form, change):
|
|
344
|
+
obj.is_system = True
|
|
345
|
+
obj.carrier_code = ext
|
|
346
|
+
return super().save_model(request, obj, form, change)
|
|
347
|
+
|
|
348
|
+
return type(f"{class_name}Admin", (_Admin,), {})
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@admin.register(providers.LabelTemplate)
|
|
352
|
+
class LabelTemplateAdmin(admin.ModelAdmin):
|
|
353
|
+
def has_module_permission(self, request):
|
|
354
|
+
return False
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@utils.skip_on_commands(["loaddata", "migrate", "makemigrations", "shell"])
|
|
358
|
+
def register_carrier_admins():
|
|
359
|
+
for carrier_name, display_name in ref.REFERENCES["carriers"].items():
|
|
360
|
+
proxy = providers.create_carrier_proxy(carrier_name, display_name)
|
|
361
|
+
admin.site.register(proxy, model_admin(carrier_name, proxy))
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
register_carrier_admins()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from django.core.management.base import BaseCommand
|
|
2
|
+
from karrio.server.providers.models import RateSheet
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Command(BaseCommand):
|
|
6
|
+
help = 'Migrate existing rate sheets from legacy format to optimized zone reuse structure'
|
|
7
|
+
|
|
8
|
+
def add_arguments(self, parser):
|
|
9
|
+
parser.add_argument(
|
|
10
|
+
'--dry-run',
|
|
11
|
+
action='store_true',
|
|
12
|
+
help='Show what would be migrated without making changes',
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
'--force',
|
|
16
|
+
action='store_true',
|
|
17
|
+
help='Force migration even if rate sheet already has optimized structure',
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def handle(self, *args, **options):
|
|
21
|
+
dry_run = options['dry_run']
|
|
22
|
+
force = options['force']
|
|
23
|
+
|
|
24
|
+
rate_sheets = RateSheet.objects.all()
|
|
25
|
+
|
|
26
|
+
if not rate_sheets.exists():
|
|
27
|
+
self.stdout.write(self.style.WARNING('No rate sheets found.'))
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
migrated_count = 0
|
|
31
|
+
skipped_count = 0
|
|
32
|
+
error_count = 0
|
|
33
|
+
|
|
34
|
+
for rate_sheet in rate_sheets:
|
|
35
|
+
try:
|
|
36
|
+
# Check if already migrated
|
|
37
|
+
if not force and (rate_sheet.zones or rate_sheet.service_rates):
|
|
38
|
+
self.stdout.write(
|
|
39
|
+
self.style.WARNING(f'Skipping {rate_sheet.name} - already has optimized structure')
|
|
40
|
+
)
|
|
41
|
+
skipped_count += 1
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
# Check if has services with zones to migrate
|
|
45
|
+
has_zones = any(
|
|
46
|
+
service.zones for service in rate_sheet.services.all()
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if not has_zones:
|
|
50
|
+
self.stdout.write(
|
|
51
|
+
self.style.WARNING(f'Skipping {rate_sheet.name} - no zones to migrate')
|
|
52
|
+
)
|
|
53
|
+
skipped_count += 1
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
if dry_run:
|
|
57
|
+
self.stdout.write(
|
|
58
|
+
self.style.SUCCESS(f'Would migrate: {rate_sheet.name}')
|
|
59
|
+
)
|
|
60
|
+
migrated_count += 1
|
|
61
|
+
else:
|
|
62
|
+
# Perform migration
|
|
63
|
+
old_zones_count = sum(
|
|
64
|
+
len(service.zones or []) for service in rate_sheet.services.all()
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
rate_sheet.migrate_from_legacy_format()
|
|
68
|
+
|
|
69
|
+
new_zones_count = len(rate_sheet.zones or [])
|
|
70
|
+
new_rates_count = len(rate_sheet.service_rates or [])
|
|
71
|
+
|
|
72
|
+
self.stdout.write(
|
|
73
|
+
self.style.SUCCESS(
|
|
74
|
+
f'Migrated {rate_sheet.name}: '
|
|
75
|
+
f'{old_zones_count} duplicated zones → '
|
|
76
|
+
f'{new_zones_count} shared zones + {new_rates_count} rates'
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
migrated_count += 1
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
self.stdout.write(
|
|
83
|
+
self.style.ERROR(f'Error migrating {rate_sheet.name}: {str(e)}')
|
|
84
|
+
)
|
|
85
|
+
error_count += 1
|
|
86
|
+
|
|
87
|
+
# Summary
|
|
88
|
+
if dry_run:
|
|
89
|
+
self.stdout.write(
|
|
90
|
+
self.style.SUCCESS(
|
|
91
|
+
f'\nDry run complete: {migrated_count} rate sheets would be migrated, '
|
|
92
|
+
f'{skipped_count} skipped, {error_count} errors'
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
self.stdout.write(
|
|
97
|
+
self.style.SUCCESS(
|
|
98
|
+
f'\nMigration complete: {migrated_count} rate sheets migrated, '
|
|
99
|
+
f'{skipped_count} skipped, {error_count} errors'
|
|
100
|
+
)
|
|
101
|
+
)
|