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
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import django.db.models as models
|
|
3
|
+
import django.utils.translation as translation
|
|
4
|
+
|
|
5
|
+
import django.conf as conf
|
|
6
|
+
import karrio.server.core.models as core
|
|
7
|
+
|
|
8
|
+
_ = translation.gettext_lazy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@core.register_model
|
|
12
|
+
class RateSheet(core.OwnedEntity):
|
|
13
|
+
class Meta:
|
|
14
|
+
db_table = "rate-sheet"
|
|
15
|
+
verbose_name = "Rate Sheet"
|
|
16
|
+
verbose_name_plural = "Rate Sheets"
|
|
17
|
+
ordering = ["-created_at"]
|
|
18
|
+
|
|
19
|
+
id = models.CharField(
|
|
20
|
+
max_length=50,
|
|
21
|
+
editable=False,
|
|
22
|
+
primary_key=True,
|
|
23
|
+
default=functools.partial(core.uuid, prefix="rsht_"),
|
|
24
|
+
)
|
|
25
|
+
name = models.CharField(_("name"), max_length=50, db_index=True)
|
|
26
|
+
slug = models.CharField(_("slug"), max_length=50, db_index=True)
|
|
27
|
+
carrier_name = models.CharField(max_length=50, db_index=True)
|
|
28
|
+
is_system = models.BooleanField(default=False, db_index=True)
|
|
29
|
+
services = models.ManyToManyField(
|
|
30
|
+
"ServiceLevel", blank=True, related_name="service_sheet"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# New optimized structure
|
|
34
|
+
zones = models.JSONField(
|
|
35
|
+
blank=True,
|
|
36
|
+
null=True,
|
|
37
|
+
default=core.field_default([]),
|
|
38
|
+
help_text="Shared zone definitions: [{'id': 'zone_1', 'label': 'Zone 1', 'cities': [...], 'country_codes': [...]}]"
|
|
39
|
+
)
|
|
40
|
+
service_rates = models.JSONField(
|
|
41
|
+
blank=True,
|
|
42
|
+
null=True,
|
|
43
|
+
default=core.field_default([]),
|
|
44
|
+
help_text="Service-zone rate mapping: [{'service_id': 'svc_1', 'zone_id': 'zone_1', 'rate': 10.50}]"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Keep old structure for backward compatibility during migration
|
|
48
|
+
metadata = models.JSONField(
|
|
49
|
+
blank=True,
|
|
50
|
+
null=True,
|
|
51
|
+
default=core.field_default({}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
created_by = models.ForeignKey(
|
|
55
|
+
conf.settings.AUTH_USER_MODEL,
|
|
56
|
+
blank=True,
|
|
57
|
+
null=True,
|
|
58
|
+
on_delete=models.CASCADE,
|
|
59
|
+
editable=False,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def delete(self, *args, **kwargs):
|
|
63
|
+
self.services.all().delete()
|
|
64
|
+
return super().delete(*args, **kwargs)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def object_type(self):
|
|
68
|
+
return "rate-sheet"
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def carriers(self):
|
|
72
|
+
import karrio.server.providers.models as providers
|
|
73
|
+
|
|
74
|
+
return providers.Carrier.objects.filter(
|
|
75
|
+
carrier_code=self.carrier_name, rate_sheet__id=self.id
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def get_service_zones_legacy(self, service_id: str):
|
|
79
|
+
"""
|
|
80
|
+
Backward compatible method - returns zones in old format for SDK compatibility
|
|
81
|
+
Combines shared zones with service-specific rates
|
|
82
|
+
"""
|
|
83
|
+
zones = self.zones or []
|
|
84
|
+
service_rates = self.service_rates or []
|
|
85
|
+
|
|
86
|
+
# Get rates for this service
|
|
87
|
+
service_rate_map = {
|
|
88
|
+
sr['zone_id']: sr for sr in service_rates
|
|
89
|
+
if sr.get('service_id') == service_id
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Combine zone definitions with service rates
|
|
93
|
+
legacy_zones = []
|
|
94
|
+
for zone in zones:
|
|
95
|
+
zone_id = zone.get('id')
|
|
96
|
+
rate_data = service_rate_map.get(zone_id, {})
|
|
97
|
+
|
|
98
|
+
legacy_zone = {
|
|
99
|
+
**zone, # Zone definition (label, cities, country_codes, etc.)
|
|
100
|
+
'rate': rate_data.get('rate', 0),
|
|
101
|
+
'min_weight': rate_data.get('min_weight'),
|
|
102
|
+
'max_weight': rate_data.get('max_weight'),
|
|
103
|
+
'transit_days': rate_data.get('transit_days'),
|
|
104
|
+
'transit_time': rate_data.get('transit_time'),
|
|
105
|
+
}
|
|
106
|
+
legacy_zones.append(legacy_zone)
|
|
107
|
+
|
|
108
|
+
return legacy_zones
|
|
109
|
+
|
|
110
|
+
def update_service_zone_rate(self, service_id: str, zone_id: str, field: str, value):
|
|
111
|
+
"""
|
|
112
|
+
Update a rate field for a specific service-zone combination
|
|
113
|
+
"""
|
|
114
|
+
allowed_fields = {
|
|
115
|
+
'rate': float,
|
|
116
|
+
'min_weight': float,
|
|
117
|
+
'max_weight': float,
|
|
118
|
+
'transit_days': int,
|
|
119
|
+
'transit_time': float,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if field not in allowed_fields:
|
|
123
|
+
raise ValueError(f"Field '{field}' is not allowed for rate updates")
|
|
124
|
+
|
|
125
|
+
# Validate value
|
|
126
|
+
try:
|
|
127
|
+
if value is not None and value != '':
|
|
128
|
+
value = allowed_fields[field](value)
|
|
129
|
+
except (ValueError, TypeError):
|
|
130
|
+
raise ValueError(f"Invalid value '{value}' for field '{field}'")
|
|
131
|
+
|
|
132
|
+
service_rates = list(self.service_rates or [])
|
|
133
|
+
|
|
134
|
+
# Find existing rate record
|
|
135
|
+
for rate_record in service_rates:
|
|
136
|
+
if (rate_record.get('service_id') == service_id and
|
|
137
|
+
rate_record.get('zone_id') == zone_id):
|
|
138
|
+
rate_record[field] = value
|
|
139
|
+
break
|
|
140
|
+
else:
|
|
141
|
+
# Create new rate record
|
|
142
|
+
service_rates.append({
|
|
143
|
+
'service_id': service_id,
|
|
144
|
+
'zone_id': zone_id,
|
|
145
|
+
field: value
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
self.service_rates = service_rates
|
|
149
|
+
self.save(update_fields=['service_rates'])
|
|
150
|
+
|
|
151
|
+
def batch_update_service_rates(self, updates):
|
|
152
|
+
"""
|
|
153
|
+
Batch update service rates
|
|
154
|
+
updates format: [{'service_id': str, 'zone_id': str, 'field': str, 'value': any}]
|
|
155
|
+
"""
|
|
156
|
+
allowed_fields = {
|
|
157
|
+
'rate': float,
|
|
158
|
+
'min_weight': float,
|
|
159
|
+
'max_weight': float,
|
|
160
|
+
'transit_days': int,
|
|
161
|
+
'transit_time': float,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
service_rates = list(self.service_rates or [])
|
|
165
|
+
service_rate_map = {}
|
|
166
|
+
|
|
167
|
+
# Create lookup map for existing rates
|
|
168
|
+
for i, rate in enumerate(service_rates):
|
|
169
|
+
key = f"{rate.get('service_id')}:{rate.get('zone_id')}"
|
|
170
|
+
service_rate_map[key] = i
|
|
171
|
+
|
|
172
|
+
for update in updates:
|
|
173
|
+
service_id = update.get('service_id')
|
|
174
|
+
zone_id = update.get('zone_id')
|
|
175
|
+
field = update.get('field')
|
|
176
|
+
value = update.get('value')
|
|
177
|
+
|
|
178
|
+
if field not in allowed_fields:
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
# Validate value
|
|
182
|
+
try:
|
|
183
|
+
if value is not None and value != '':
|
|
184
|
+
value = allowed_fields[field](value)
|
|
185
|
+
except (ValueError, TypeError):
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
key = f"{service_id}:{zone_id}"
|
|
189
|
+
|
|
190
|
+
if key in service_rate_map:
|
|
191
|
+
# Update existing rate
|
|
192
|
+
service_rates[service_rate_map[key]][field] = value
|
|
193
|
+
else:
|
|
194
|
+
# Create new rate record
|
|
195
|
+
new_rate = {
|
|
196
|
+
'service_id': service_id,
|
|
197
|
+
'zone_id': zone_id,
|
|
198
|
+
field: value
|
|
199
|
+
}
|
|
200
|
+
service_rates.append(new_rate)
|
|
201
|
+
service_rate_map[key] = len(service_rates) - 1
|
|
202
|
+
|
|
203
|
+
self.service_rates = service_rates
|
|
204
|
+
self.save(update_fields=['service_rates'])
|
|
205
|
+
|
|
206
|
+
def add_zone(self, zone_data):
|
|
207
|
+
"""
|
|
208
|
+
Add a new shared zone definition
|
|
209
|
+
"""
|
|
210
|
+
zones = list(self.zones or [])
|
|
211
|
+
|
|
212
|
+
# Generate zone ID if not provided
|
|
213
|
+
if not zone_data.get('id'):
|
|
214
|
+
zone_data['id'] = f"zone_{len(zones) + 1}"
|
|
215
|
+
|
|
216
|
+
zones.append(zone_data)
|
|
217
|
+
self.zones = zones
|
|
218
|
+
self.save(update_fields=['zones'])
|
|
219
|
+
return zone_data['id']
|
|
220
|
+
|
|
221
|
+
def remove_zone(self, zone_id: str):
|
|
222
|
+
"""
|
|
223
|
+
Remove a zone and all its associated rates
|
|
224
|
+
"""
|
|
225
|
+
# Remove zone definition
|
|
226
|
+
zones = [z for z in (self.zones or []) if z.get('id') != zone_id]
|
|
227
|
+
self.zones = zones
|
|
228
|
+
|
|
229
|
+
# Remove all rates for this zone
|
|
230
|
+
service_rates = [sr for sr in (self.service_rates or []) if sr.get('zone_id') != zone_id]
|
|
231
|
+
self.service_rates = service_rates
|
|
232
|
+
|
|
233
|
+
self.save(update_fields=['zones', 'service_rates'])
|
|
234
|
+
|
|
235
|
+
def migrate_from_legacy_format(self):
|
|
236
|
+
"""
|
|
237
|
+
Migrate from old format where zones are stored per service to new shared format
|
|
238
|
+
"""
|
|
239
|
+
if self.zones or self.service_rates:
|
|
240
|
+
# Already in new format
|
|
241
|
+
return
|
|
242
|
+
|
|
243
|
+
all_zones = {}
|
|
244
|
+
service_rates = []
|
|
245
|
+
zone_counter = 1
|
|
246
|
+
|
|
247
|
+
# Extract unique zones across all services
|
|
248
|
+
for service in self.services.all():
|
|
249
|
+
service_zones = service.zones or []
|
|
250
|
+
|
|
251
|
+
for zone_index, zone_data in enumerate(service_zones):
|
|
252
|
+
# Create zone signature for deduplication
|
|
253
|
+
zone_signature = {
|
|
254
|
+
'label': zone_data.get('label', f'Zone {zone_index + 1}'),
|
|
255
|
+
'cities': sorted(zone_data.get('cities', [])),
|
|
256
|
+
'postal_codes': sorted(zone_data.get('postal_codes', [])),
|
|
257
|
+
'country_codes': sorted(zone_data.get('country_codes', [])),
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# Use signature as key for deduplication
|
|
261
|
+
sig_key = str(zone_signature)
|
|
262
|
+
|
|
263
|
+
if sig_key not in all_zones:
|
|
264
|
+
zone_id = f"zone_{zone_counter}"
|
|
265
|
+
all_zones[sig_key] = {
|
|
266
|
+
'id': zone_id,
|
|
267
|
+
**zone_signature
|
|
268
|
+
}
|
|
269
|
+
zone_counter += 1
|
|
270
|
+
|
|
271
|
+
zone_id = all_zones[sig_key]['id']
|
|
272
|
+
|
|
273
|
+
# Store service rate
|
|
274
|
+
service_rates.append({
|
|
275
|
+
'service_id': service.id,
|
|
276
|
+
'zone_id': zone_id,
|
|
277
|
+
'rate': zone_data.get('rate', 0),
|
|
278
|
+
'min_weight': zone_data.get('min_weight'),
|
|
279
|
+
'max_weight': zone_data.get('max_weight'),
|
|
280
|
+
'transit_days': zone_data.get('transit_days'),
|
|
281
|
+
'transit_time': zone_data.get('transit_time'),
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
# Save optimized structure
|
|
285
|
+
self.zones = list(all_zones.values())
|
|
286
|
+
self.service_rates = service_rates
|
|
287
|
+
self.save(update_fields=['zones', 'service_rates'])
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import django.db.models as models
|
|
3
|
+
import django.core.validators as validators
|
|
4
|
+
import karrio.server.core.models as core
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
LABEL_TEMPLATE_TYPES = [
|
|
8
|
+
("SVG", "SVG"),
|
|
9
|
+
("ZPL", "ZPL"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@core.register_model
|
|
14
|
+
class LabelTemplate(core.OwnedEntity):
|
|
15
|
+
class Meta:
|
|
16
|
+
db_table = "label-template"
|
|
17
|
+
verbose_name = "Label Template"
|
|
18
|
+
verbose_name_plural = "Label Templates"
|
|
19
|
+
ordering = ["-created_at"]
|
|
20
|
+
|
|
21
|
+
id = models.CharField(
|
|
22
|
+
max_length=50,
|
|
23
|
+
primary_key=True,
|
|
24
|
+
default=functools.partial(core.uuid, prefix="tpl_"),
|
|
25
|
+
editable=False,
|
|
26
|
+
)
|
|
27
|
+
slug = models.SlugField(
|
|
28
|
+
max_length=30,
|
|
29
|
+
validators=[validators.RegexValidator(r"^[a-z0-9_]+$")],
|
|
30
|
+
)
|
|
31
|
+
template = models.TextField()
|
|
32
|
+
template_type = models.CharField(max_length=3, choices=LABEL_TEMPLATE_TYPES)
|
|
33
|
+
width = models.IntegerField(null=True, blank=True)
|
|
34
|
+
height = models.IntegerField(null=True, blank=True)
|
|
35
|
+
shipment_sample = models.JSONField(blank=True, null=True, default=dict)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def object_type(self):
|
|
39
|
+
return "label_template"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import django.db.models as models
|
|
2
|
+
import django.core.cache as caching
|
|
3
|
+
|
|
4
|
+
import karrio.lib as lib
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def has_rate_sheet(carrier_name: str):
|
|
8
|
+
def decorator(model: models.Model):
|
|
9
|
+
# Add a rate sheet relation to the model
|
|
10
|
+
model.add_to_class(
|
|
11
|
+
"rate_sheet",
|
|
12
|
+
models.ForeignKey(
|
|
13
|
+
"RateSheet",
|
|
14
|
+
null=True,
|
|
15
|
+
blank=True,
|
|
16
|
+
on_delete=models.SET_NULL,
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Add a service list property to the model
|
|
21
|
+
setattr(
|
|
22
|
+
model,
|
|
23
|
+
"service_list",
|
|
24
|
+
property(
|
|
25
|
+
lambda self: (
|
|
26
|
+
self.services
|
|
27
|
+
if self.rate_sheet is None and hasattr(self, "services")
|
|
28
|
+
else self.rate_sheet.services.all()
|
|
29
|
+
)
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Add a default services property to the model
|
|
34
|
+
# skip if it already exists (overridden)
|
|
35
|
+
if not hasattr(model, "default_services"):
|
|
36
|
+
setattr(
|
|
37
|
+
model,
|
|
38
|
+
"default_services",
|
|
39
|
+
property(
|
|
40
|
+
lambda self: lib.to_dict(
|
|
41
|
+
getattr(
|
|
42
|
+
getattr(
|
|
43
|
+
__import__(
|
|
44
|
+
f"karrio.mappers.{carrier_name}", fromlist=["units"]
|
|
45
|
+
),
|
|
46
|
+
"units",
|
|
47
|
+
None,
|
|
48
|
+
),
|
|
49
|
+
"DEFAULT_SERVICES",
|
|
50
|
+
[],
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return model
|
|
57
|
+
|
|
58
|
+
return decorator
|