karrio-server-graph 2026.1__py3-none-any.whl → 2026.1.3__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/graph/migrations/0003_remove_template_customs.py +15 -0
- karrio/server/graph/models.py +2 -5
- karrio/server/graph/schemas/base/__init__.py +68 -48
- karrio/server/graph/schemas/base/inputs.py +286 -71
- karrio/server/graph/schemas/base/mutations.py +365 -84
- karrio/server/graph/schemas/base/types.py +940 -248
- karrio/server/graph/serializers.py +25 -59
- karrio/server/graph/tests/base.py +33 -6
- karrio/server/graph/tests/test_carrier_connections.py +33 -4
- karrio/server/graph/tests/test_pickups.py +333 -0
- karrio/server/graph/tests/test_rate_sheets.py +0 -1
- karrio/server/graph/tests/test_templates.py +328 -510
- karrio/server/graph/utils.py +68 -1
- {karrio_server_graph-2026.1.dist-info → karrio_server_graph-2026.1.3.dist-info}/METADATA +1 -1
- {karrio_server_graph-2026.1.dist-info → karrio_server_graph-2026.1.3.dist-info}/RECORD +17 -15
- {karrio_server_graph-2026.1.dist-info → karrio_server_graph-2026.1.3.dist-info}/WHEEL +1 -1
- {karrio_server_graph-2026.1.dist-info → karrio_server_graph-2026.1.3.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
8
8
|
from django_email_verification import confirm as email_verification
|
|
9
9
|
from django_otp.plugins.otp_email import models as otp
|
|
10
10
|
from django.utils.translation import gettext_lazy as _
|
|
11
|
-
from django.db import transaction
|
|
11
|
+
from django.db import transaction, models
|
|
12
12
|
|
|
13
13
|
from karrio.server.core.utils import ConfirmationToken, send_email
|
|
14
14
|
from karrio.server.user.serializers import TokenSerializer
|
|
@@ -490,7 +490,12 @@ class DeleteMutation(utils.BaseMutation):
|
|
|
490
490
|
if validator:
|
|
491
491
|
validator(instance, context=info.context)
|
|
492
492
|
|
|
493
|
+
# Get the shipment FK if it exists and is a direct relationship
|
|
494
|
+
# (not a GenericRelation/RelatedManager)
|
|
493
495
|
shipment = getattr(instance, "shipment", None)
|
|
496
|
+
if shipment is not None and not isinstance(shipment, manager.Shipment):
|
|
497
|
+
shipment = None
|
|
498
|
+
|
|
494
499
|
instance.delete(keep_parents=True)
|
|
495
500
|
|
|
496
501
|
if shipment is not None:
|
|
@@ -514,6 +519,7 @@ class CreateRateSheetMutation(utils.BaseMutation):
|
|
|
514
519
|
zones_data = data.pop("zones", [])
|
|
515
520
|
surcharges_data = data.pop("surcharges", [])
|
|
516
521
|
service_rates_data = data.pop("service_rates", [])
|
|
522
|
+
# Note: origin_countries stays in data - saved via serializer
|
|
517
523
|
services_data = [
|
|
518
524
|
(svc.copy() if isinstance(svc, dict) else dict(svc))
|
|
519
525
|
for svc in data.get("services", [])
|
|
@@ -542,7 +548,7 @@ class CreateRateSheetMutation(utils.BaseMutation):
|
|
|
542
548
|
|
|
543
549
|
# Link carriers
|
|
544
550
|
if any(carriers):
|
|
545
|
-
providers.
|
|
551
|
+
providers.CarrierConnection.access_by(info.context.request).filter(
|
|
546
552
|
carrier_code=rate_sheet.carrier_name,
|
|
547
553
|
id__in=carriers,
|
|
548
554
|
).update(rate_sheet=rate_sheet)
|
|
@@ -573,6 +579,7 @@ class UpdateRateSheetMutation(utils.BaseMutation):
|
|
|
573
579
|
)
|
|
574
580
|
data = input.copy()
|
|
575
581
|
carriers = data.pop("carriers", [])
|
|
582
|
+
# Note: origin_countries stays in data - saved via serializer
|
|
576
583
|
|
|
577
584
|
serializer = serializers.RateSheetModelSerializer(
|
|
578
585
|
instance,
|
|
@@ -595,7 +602,7 @@ class UpdateRateSheetMutation(utils.BaseMutation):
|
|
|
595
602
|
|
|
596
603
|
# Link/unlink carriers
|
|
597
604
|
if any(carriers):
|
|
598
|
-
carrier_qs = providers.
|
|
605
|
+
carrier_qs = providers.CarrierConnection.access_by(info.context.request).filter(
|
|
599
606
|
carrier_code=rate_sheet.carrier_name
|
|
600
607
|
)
|
|
601
608
|
carrier_qs.filter(id__in=carriers).update(rate_sheet=rate_sheet)
|
|
@@ -987,72 +994,348 @@ class ChangeShipmentStatusMutation(utils.BaseMutation):
|
|
|
987
994
|
return ChangeShipmentStatusMutation(shipment=shipment) # type:ignore
|
|
988
995
|
|
|
989
996
|
|
|
990
|
-
def
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
)
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
997
|
+
def _clear_default_address_templates(info, exclude_id=None):
|
|
998
|
+
"""Clear is_default flag on all address templates except the specified one."""
|
|
999
|
+
queryset = manager.Address.access_by(info.context.request).filter(
|
|
1000
|
+
meta__is_default=True,
|
|
1001
|
+
meta__label__isnull=False,
|
|
1002
|
+
)
|
|
1003
|
+
if exclude_id:
|
|
1004
|
+
queryset = queryset.exclude(id=exclude_id)
|
|
1005
|
+
|
|
1006
|
+
for address in queryset:
|
|
1007
|
+
address.meta = {**(address.meta or {}), "is_default": False}
|
|
1008
|
+
address.save(update_fields=["meta"])
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def _clear_default_parcel_templates(info, exclude_id=None):
|
|
1012
|
+
"""Clear is_default flag on all parcel templates except the specified one."""
|
|
1013
|
+
queryset = manager.Parcel.access_by(info.context.request).filter(
|
|
1014
|
+
meta__is_default=True,
|
|
1015
|
+
meta__label__isnull=False,
|
|
1016
|
+
)
|
|
1017
|
+
if exclude_id:
|
|
1018
|
+
queryset = queryset.exclude(id=exclude_id)
|
|
1019
|
+
|
|
1020
|
+
for parcel in queryset:
|
|
1021
|
+
parcel.meta = {**(parcel.meta or {}), "is_default": False}
|
|
1022
|
+
parcel.save(update_fields=["meta"])
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
@strawberry.type
|
|
1026
|
+
class CreateAddressMutation(utils.BaseMutation):
|
|
1027
|
+
"""Create a saved address using Address model with meta.label."""
|
|
1028
|
+
|
|
1029
|
+
address: typing.Optional[types.AddressTemplateType] = None
|
|
1030
|
+
|
|
1031
|
+
@staticmethod
|
|
1032
|
+
@utils.authentication_required
|
|
1033
|
+
@transaction.atomic
|
|
1034
|
+
def mutate(info: Info, **input) -> "CreateAddressMutation":
|
|
1035
|
+
data = input.copy()
|
|
1036
|
+
|
|
1037
|
+
# Extract meta from input (flat structure)
|
|
1038
|
+
meta_input = data.pop("meta", {})
|
|
1039
|
+
meta = {
|
|
1040
|
+
k: v
|
|
1041
|
+
for k, v in meta_input.items()
|
|
1042
|
+
if not utils.is_unset(v)
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
# If setting as default, clear existing default
|
|
1046
|
+
if meta.get("is_default"):
|
|
1047
|
+
_clear_default_address_templates(info)
|
|
1048
|
+
|
|
1049
|
+
# Ensure is_default has a value
|
|
1050
|
+
if "is_default" not in meta:
|
|
1051
|
+
meta["is_default"] = False
|
|
1052
|
+
|
|
1053
|
+
serializer = serializers.AddressModelSerializer(
|
|
1054
|
+
data={**data, "meta": meta},
|
|
1055
|
+
context=info.context.request,
|
|
1056
|
+
)
|
|
1057
|
+
serializer.is_valid(raise_exception=True)
|
|
1058
|
+
address = serializer.save()
|
|
1059
|
+
|
|
1060
|
+
return CreateAddressMutation(address=address) # type:ignore
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
@strawberry.type
|
|
1064
|
+
class UpdateAddressMutation(utils.BaseMutation):
|
|
1065
|
+
"""Update a saved address."""
|
|
1066
|
+
|
|
1067
|
+
address: typing.Optional[types.AddressTemplateType] = None
|
|
1068
|
+
|
|
1069
|
+
@staticmethod
|
|
1070
|
+
@utils.authentication_required
|
|
1071
|
+
@transaction.atomic
|
|
1072
|
+
def mutate(info: Info, **input) -> "UpdateAddressMutation":
|
|
1073
|
+
data = input.copy()
|
|
1074
|
+
id = data.pop("id")
|
|
1075
|
+
|
|
1076
|
+
# Extract meta from input (flat structure)
|
|
1077
|
+
meta_input = data.pop("meta", {})
|
|
1078
|
+
meta_updates = {
|
|
1079
|
+
k: v
|
|
1080
|
+
for k, v in meta_input.items()
|
|
1081
|
+
if not utils.is_unset(v)
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
instance = manager.Address.access_by(info.context.request).get(id=id)
|
|
1085
|
+
|
|
1086
|
+
# Verify this is a template (has meta.label)
|
|
1087
|
+
if not (instance.meta or {}).get("label"):
|
|
1088
|
+
raise utils.ValidationError(
|
|
1089
|
+
"This address is not a template. Only templates can be updated here."
|
|
1010
1090
|
)
|
|
1011
|
-
customs_data = data.get("customs", {})
|
|
1012
|
-
|
|
1013
|
-
if "commodities" in customs_data and instance is not None:
|
|
1014
|
-
save_many_to_many_data(
|
|
1015
|
-
"commodities",
|
|
1016
|
-
serializers.CommodityModelSerializer,
|
|
1017
|
-
getattr(instance, "customs", None),
|
|
1018
|
-
payload=customs_data,
|
|
1019
|
-
context=info.context.request,
|
|
1020
|
-
)
|
|
1021
1091
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1092
|
+
# Update meta field
|
|
1093
|
+
meta = {**(instance.meta or {}), **meta_updates}
|
|
1094
|
+
|
|
1095
|
+
# If setting as default, clear existing default
|
|
1096
|
+
if meta_updates.get("is_default"):
|
|
1097
|
+
_clear_default_address_templates(info, exclude_id=id)
|
|
1098
|
+
|
|
1099
|
+
serializer = serializers.AddressModelSerializer(
|
|
1100
|
+
instance,
|
|
1101
|
+
data={**data, "meta": meta},
|
|
1102
|
+
context=info.context.request,
|
|
1103
|
+
partial=True,
|
|
1104
|
+
)
|
|
1105
|
+
serializer.is_valid(raise_exception=True)
|
|
1106
|
+
address = serializer.save()
|
|
1107
|
+
|
|
1108
|
+
return UpdateAddressMutation(address=address) # type:ignore
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
@strawberry.type
|
|
1112
|
+
class CreateParcelMutation(utils.BaseMutation):
|
|
1113
|
+
"""Create a saved parcel using Parcel model with meta.label."""
|
|
1114
|
+
|
|
1115
|
+
parcel: typing.Optional[types.ParcelTemplateType] = None
|
|
1116
|
+
|
|
1117
|
+
@staticmethod
|
|
1118
|
+
@utils.authentication_required
|
|
1119
|
+
@transaction.atomic
|
|
1120
|
+
def mutate(info: Info, **input) -> "CreateParcelMutation":
|
|
1121
|
+
data = input.copy()
|
|
1122
|
+
|
|
1123
|
+
# Extract meta from input (flat structure)
|
|
1124
|
+
meta_input = data.pop("meta", {})
|
|
1125
|
+
meta = {
|
|
1126
|
+
k: v
|
|
1127
|
+
for k, v in meta_input.items()
|
|
1128
|
+
if not utils.is_unset(v)
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
# If setting as default, clear existing default
|
|
1132
|
+
if meta.get("is_default"):
|
|
1133
|
+
_clear_default_parcel_templates(info)
|
|
1134
|
+
|
|
1135
|
+
# Ensure is_default has a value
|
|
1136
|
+
if "is_default" not in meta:
|
|
1137
|
+
meta["is_default"] = False
|
|
1138
|
+
|
|
1139
|
+
serializer = serializers.ParcelModelSerializer(
|
|
1140
|
+
data={**data, "meta": meta},
|
|
1141
|
+
context=info.context.request,
|
|
1142
|
+
)
|
|
1143
|
+
serializer.is_valid(raise_exception=True)
|
|
1144
|
+
parcel = serializer.save()
|
|
1145
|
+
|
|
1146
|
+
return CreateParcelMutation(parcel=parcel) # type:ignore
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
@strawberry.type
|
|
1150
|
+
class UpdateParcelMutation(utils.BaseMutation):
|
|
1151
|
+
"""Update a saved parcel."""
|
|
1152
|
+
|
|
1153
|
+
parcel: typing.Optional[types.ParcelTemplateType] = None
|
|
1154
|
+
|
|
1155
|
+
@staticmethod
|
|
1156
|
+
@utils.authentication_required
|
|
1157
|
+
@transaction.atomic
|
|
1158
|
+
def mutate(info: Info, **input) -> "UpdateParcelMutation":
|
|
1159
|
+
data = input.copy()
|
|
1160
|
+
id = data.pop("id")
|
|
1161
|
+
|
|
1162
|
+
# Extract meta from input (flat structure)
|
|
1163
|
+
meta_input = data.pop("meta", {})
|
|
1164
|
+
meta_updates = {
|
|
1165
|
+
k: v
|
|
1166
|
+
for k, v in meta_input.items()
|
|
1167
|
+
if not utils.is_unset(v)
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
instance = manager.Parcel.access_by(info.context.request).get(id=id)
|
|
1171
|
+
|
|
1172
|
+
# Verify this is a template (has meta.label)
|
|
1173
|
+
if not (instance.meta or {}).get("label"):
|
|
1174
|
+
raise utils.ValidationError(
|
|
1175
|
+
"This parcel is not a template. Only templates can be updated here."
|
|
1027
1176
|
)
|
|
1028
1177
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1178
|
+
# Update meta field
|
|
1179
|
+
meta = {**(instance.meta or {}), **meta_updates}
|
|
1031
1180
|
|
|
1032
|
-
|
|
1181
|
+
# If setting as default, clear existing default
|
|
1182
|
+
if meta_updates.get("is_default"):
|
|
1183
|
+
_clear_default_parcel_templates(info, exclude_id=id)
|
|
1033
1184
|
|
|
1034
|
-
|
|
1185
|
+
serializer = serializers.ParcelModelSerializer(
|
|
1186
|
+
instance,
|
|
1187
|
+
data={**data, "meta": meta},
|
|
1188
|
+
context=info.context.request,
|
|
1189
|
+
partial=True,
|
|
1190
|
+
)
|
|
1191
|
+
serializer.is_valid(raise_exception=True)
|
|
1192
|
+
parcel = serializer.save()
|
|
1035
1193
|
|
|
1194
|
+
return UpdateParcelMutation(parcel=parcel) # type:ignore
|
|
1036
1195
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
)
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
)
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1196
|
+
|
|
1197
|
+
@strawberry.type
|
|
1198
|
+
class DeleteAddressMutation(utils.BaseMutation):
|
|
1199
|
+
"""Delete a saved address."""
|
|
1200
|
+
|
|
1201
|
+
id: str = strawberry.UNSET
|
|
1202
|
+
|
|
1203
|
+
@staticmethod
|
|
1204
|
+
@utils.authentication_required
|
|
1205
|
+
def mutate(info: Info, **input) -> "DeleteAddressMutation":
|
|
1206
|
+
id = input.get("id")
|
|
1207
|
+
instance = manager.Address.access_by(info.context.request).get(id=id)
|
|
1208
|
+
|
|
1209
|
+
# Verify this is a saved address (has meta.label)
|
|
1210
|
+
if not (instance.meta or {}).get("label"):
|
|
1211
|
+
raise utils.ValidationError(
|
|
1212
|
+
"This address is not a saved address. Only saved addresses can be deleted here."
|
|
1213
|
+
)
|
|
1214
|
+
|
|
1215
|
+
instance.delete(keep_parents=True)
|
|
1216
|
+
return DeleteAddressMutation(id=id) # type:ignore
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
@strawberry.type
|
|
1220
|
+
class DeleteParcelMutation(utils.BaseMutation):
|
|
1221
|
+
"""Delete a saved parcel."""
|
|
1222
|
+
|
|
1223
|
+
id: str = strawberry.UNSET
|
|
1224
|
+
|
|
1225
|
+
@staticmethod
|
|
1226
|
+
@utils.authentication_required
|
|
1227
|
+
def mutate(info: Info, **input) -> "DeleteParcelMutation":
|
|
1228
|
+
id = input.get("id")
|
|
1229
|
+
instance = manager.Parcel.access_by(info.context.request).get(id=id)
|
|
1230
|
+
|
|
1231
|
+
# Verify this is a saved parcel (has meta.label)
|
|
1232
|
+
if not (instance.meta or {}).get("label"):
|
|
1233
|
+
raise utils.ValidationError(
|
|
1234
|
+
"This parcel is not a saved parcel. Only saved parcels can be deleted here."
|
|
1235
|
+
)
|
|
1236
|
+
|
|
1237
|
+
instance.delete(keep_parents=True)
|
|
1238
|
+
return DeleteParcelMutation(id=id) # type:ignore
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
def _clear_default_product_templates(info, exclude_id=None):
|
|
1242
|
+
"""Clear is_default flag on all product templates except the specified one."""
|
|
1243
|
+
queryset = manager.Commodity.access_by(info.context.request).filter(
|
|
1244
|
+
meta__is_default=True,
|
|
1245
|
+
meta__label__isnull=False,
|
|
1246
|
+
)
|
|
1247
|
+
if exclude_id:
|
|
1248
|
+
queryset = queryset.exclude(id=exclude_id)
|
|
1249
|
+
|
|
1250
|
+
for commodity in queryset:
|
|
1251
|
+
commodity.meta = {**(commodity.meta or {}), "is_default": False}
|
|
1252
|
+
commodity.save(update_fields=["meta"])
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
@strawberry.type
|
|
1256
|
+
class CreateProductMutation(utils.BaseMutation):
|
|
1257
|
+
"""Create a saved product using Commodity model with meta.label."""
|
|
1258
|
+
|
|
1259
|
+
product: typing.Optional[types.ProductTemplateType] = None
|
|
1260
|
+
|
|
1261
|
+
@staticmethod
|
|
1262
|
+
@utils.authentication_required
|
|
1263
|
+
@transaction.atomic
|
|
1264
|
+
def mutate(info: Info, **input) -> "CreateProductMutation":
|
|
1265
|
+
data = input.copy()
|
|
1266
|
+
|
|
1267
|
+
# Extract meta from input (flat structure)
|
|
1268
|
+
meta_input = data.pop("meta", {})
|
|
1269
|
+
meta = {
|
|
1270
|
+
k: v
|
|
1271
|
+
for k, v in meta_input.items()
|
|
1272
|
+
if not utils.is_unset(v)
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
# If setting as default, clear existing default
|
|
1276
|
+
if meta.get("is_default"):
|
|
1277
|
+
_clear_default_product_templates(info)
|
|
1278
|
+
|
|
1279
|
+
# Ensure is_default has a value
|
|
1280
|
+
if "is_default" not in meta:
|
|
1281
|
+
meta["is_default"] = False
|
|
1282
|
+
|
|
1283
|
+
serializer = serializers.CommodityModelSerializer(
|
|
1284
|
+
data={**data, "meta": meta},
|
|
1285
|
+
context=info.context.request,
|
|
1286
|
+
)
|
|
1287
|
+
serializer.is_valid(raise_exception=True)
|
|
1288
|
+
product = serializer.save()
|
|
1289
|
+
|
|
1290
|
+
return CreateProductMutation(product=product) # type:ignore
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
@strawberry.type
|
|
1294
|
+
class UpdateProductMutation(utils.BaseMutation):
|
|
1295
|
+
"""Update a saved product."""
|
|
1296
|
+
|
|
1297
|
+
product: typing.Optional[types.ProductTemplateType] = None
|
|
1298
|
+
|
|
1299
|
+
@staticmethod
|
|
1300
|
+
@utils.authentication_required
|
|
1301
|
+
@transaction.atomic
|
|
1302
|
+
def mutate(info: Info, **input) -> "UpdateProductMutation":
|
|
1303
|
+
data = input.copy()
|
|
1304
|
+
id = data.pop("id")
|
|
1305
|
+
|
|
1306
|
+
# Extract meta from input (flat structure)
|
|
1307
|
+
meta_input = data.pop("meta", {})
|
|
1308
|
+
meta_updates = {
|
|
1309
|
+
k: v
|
|
1310
|
+
for k, v in meta_input.items()
|
|
1311
|
+
if not utils.is_unset(v)
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
instance = manager.Commodity.access_by(info.context.request).get(id=id)
|
|
1315
|
+
|
|
1316
|
+
# Verify this is a template (has meta.label)
|
|
1317
|
+
if not instance.is_template:
|
|
1318
|
+
raise utils.ValidationError(
|
|
1319
|
+
"This commodity is not a template. Only templates can be updated here."
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
# Update meta field
|
|
1323
|
+
meta = {**(instance.meta or {}), **meta_updates}
|
|
1324
|
+
|
|
1325
|
+
# If setting as default, clear existing default
|
|
1326
|
+
if meta_updates.get("is_default"):
|
|
1327
|
+
_clear_default_product_templates(info, exclude_id=id)
|
|
1328
|
+
|
|
1329
|
+
serializer = serializers.CommodityModelSerializer(
|
|
1330
|
+
instance,
|
|
1331
|
+
data={**data, "meta": meta},
|
|
1332
|
+
context=info.context.request,
|
|
1333
|
+
partial=True,
|
|
1334
|
+
)
|
|
1335
|
+
serializer.is_valid(raise_exception=True)
|
|
1336
|
+
product = serializer.save()
|
|
1337
|
+
|
|
1338
|
+
return UpdateProductMutation(product=product) # type:ignore
|
|
1056
1339
|
|
|
1057
1340
|
|
|
1058
1341
|
@strawberry.type
|
|
@@ -1089,7 +1372,7 @@ class UpdateCarrierConnectionMutation(utils.BaseMutation):
|
|
|
1089
1372
|
def mutate(info: Info, **input) -> "UpdateCarrierConnectionMutation":
|
|
1090
1373
|
data = input.copy()
|
|
1091
1374
|
id = data.get("id")
|
|
1092
|
-
instance = providers.
|
|
1375
|
+
instance = providers.CarrierConnection.access_by(info.context.request).get(id=id)
|
|
1093
1376
|
connection = lib.identity(
|
|
1094
1377
|
providers_serializers.CarrierConnectionModelSerializer.map(
|
|
1095
1378
|
instance,
|
|
@@ -1115,37 +1398,35 @@ class SystemCarrierMutation(utils.BaseMutation):
|
|
|
1115
1398
|
def mutate(
|
|
1116
1399
|
info: Info, **input: inputs.SystemCarrierMutationInput
|
|
1117
1400
|
) -> "SystemCarrierMutation":
|
|
1401
|
+
from karrio.server.providers.serializers import BrokeredConnectionModelSerializer
|
|
1402
|
+
|
|
1118
1403
|
pk = input.get("id")
|
|
1119
1404
|
context = info.context.request
|
|
1120
|
-
|
|
1405
|
+
system_connection = providers.SystemConnection.objects.get(pk=pk)
|
|
1406
|
+
|
|
1407
|
+
# Build serializer data from input
|
|
1408
|
+
# Map 'config' to 'config_overrides' and 'enable' to 'is_enabled'
|
|
1409
|
+
data = {"system_connection_id": pk}
|
|
1121
1410
|
|
|
1122
1411
|
if "enable" in input:
|
|
1123
|
-
|
|
1124
|
-
if hasattr(carrier, "active_orgs"):
|
|
1125
|
-
carrier.active_orgs.add(info.context.request.org)
|
|
1126
|
-
else:
|
|
1127
|
-
carrier.active_users.add(info.context.request.user)
|
|
1128
|
-
else:
|
|
1129
|
-
if hasattr(carrier, "active_orgs"):
|
|
1130
|
-
carrier.active_orgs.remove(info.context.request.org)
|
|
1131
|
-
else:
|
|
1132
|
-
carrier.active_users.remove(info.context.request.user)
|
|
1412
|
+
data["is_enabled"] = input.get("enable")
|
|
1133
1413
|
|
|
1134
1414
|
if "config" in input:
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1415
|
+
data["config_overrides"] = input.get("config") or {}
|
|
1416
|
+
|
|
1417
|
+
# Use the serializer to create or update the BrokeredConnection
|
|
1418
|
+
# The @owned_model_serializer decorator handles org linking automatically
|
|
1419
|
+
brokered = (
|
|
1420
|
+
BrokeredConnectionModelSerializer.map(
|
|
1421
|
+
data=data,
|
|
1138
1422
|
context=context,
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
),
|
|
1144
|
-
},
|
|
1145
|
-
).save()
|
|
1423
|
+
)
|
|
1424
|
+
.save()
|
|
1425
|
+
.instance
|
|
1426
|
+
)
|
|
1146
1427
|
|
|
1147
1428
|
return SystemCarrierMutation(
|
|
1148
|
-
carrier=
|
|
1429
|
+
carrier=system_connection
|
|
1149
1430
|
) # type: ignore
|
|
1150
1431
|
|
|
1151
1432
|
|