karrio-server-graph 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 (39) hide show
  1. karrio/server/graph/__init__.py +1 -0
  2. karrio/server/graph/admin.py +3 -0
  3. karrio/server/graph/apps.py +5 -0
  4. karrio/server/graph/forms.py +57 -0
  5. karrio/server/graph/management/__init__.py +0 -0
  6. karrio/server/graph/management/commands/__init__.py +0 -0
  7. karrio/server/graph/management/commands/export_schema.py +9 -0
  8. karrio/server/graph/migrations/0001_initial.py +37 -0
  9. karrio/server/graph/migrations/0002_auto_20210512_1353.py +22 -0
  10. karrio/server/graph/migrations/__init__.py +0 -0
  11. karrio/server/graph/models.py +44 -0
  12. karrio/server/graph/schema.py +44 -0
  13. karrio/server/graph/schemas/__init__.py +2 -0
  14. karrio/server/graph/schemas/base/__init__.py +385 -0
  15. karrio/server/graph/schemas/base/inputs.py +612 -0
  16. karrio/server/graph/schemas/base/mutations.py +1033 -0
  17. karrio/server/graph/schemas/base/types.py +1406 -0
  18. karrio/server/graph/serializers.py +388 -0
  19. karrio/server/graph/templates/graphql/graphiql.html +142 -0
  20. karrio/server/graph/templates/karrio/email_change_email.html +13 -0
  21. karrio/server/graph/templates/karrio/email_change_email.txt +13 -0
  22. karrio/server/graph/templates/karrio/password_reset_email.html +14 -0
  23. karrio/server/graph/tests/__init__.py +9 -0
  24. karrio/server/graph/tests/base.py +153 -0
  25. karrio/server/graph/tests/test_carrier_connections.py +239 -0
  26. karrio/server/graph/tests/test_metafield.py +404 -0
  27. karrio/server/graph/tests/test_partial_shipments.py +603 -0
  28. karrio/server/graph/tests/test_rate_sheets.py +354 -0
  29. karrio/server/graph/tests/test_registration.py +209 -0
  30. karrio/server/graph/tests/test_templates.py +677 -0
  31. karrio/server/graph/tests/test_user_info.py +71 -0
  32. karrio/server/graph/urls.py +10 -0
  33. karrio/server/graph/utils.py +308 -0
  34. karrio/server/graph/views.py +91 -0
  35. karrio/server/settings/graph.py +7 -0
  36. karrio_server_graph-2025.5.dist-info/METADATA +29 -0
  37. karrio_server_graph-2025.5.dist-info/RECORD +39 -0
  38. karrio_server_graph-2025.5.dist-info/WHEEL +5 -0
  39. karrio_server_graph-2025.5.dist-info/top_level.txt +2 -0
@@ -0,0 +1,1406 @@
1
+ import typing
2
+ import datetime
3
+ import strawberry
4
+ import django.db.models as models
5
+ import django.db.models.functions as functions
6
+ from django.conf import settings
7
+ from strawberry.types import Info
8
+ from django.contrib.auth import get_user_model
9
+ from django.utils.translation import gettext_lazy as _
10
+
11
+ import karrio.lib as lib
12
+ import karrio.server.conf as conf
13
+ import karrio.server.user.models as auth
14
+ import karrio.server.core.models as core
15
+ import karrio.server.graph.utils as utils
16
+ import karrio.server.graph.models as graph
17
+ import karrio.server.core.filters as filters
18
+ import karrio.server.orders.models as orders
19
+ import karrio.server.manager.models as manager
20
+ import karrio.server.tracing.models as tracing
21
+ import karrio.server.providers.models as providers
22
+ import karrio.server.orders.filters as order_filters
23
+ import karrio.server.user.serializers as user_serializers
24
+ import karrio.server.graph.schemas.base.inputs as inputs
25
+ from karrio.server.core.logging import logger
26
+
27
+ User = get_user_model()
28
+
29
+
30
+ @strawberry.type
31
+ class UserType:
32
+ email: str
33
+ full_name: str
34
+ is_staff: bool
35
+ is_active: bool
36
+ date_joined: datetime.datetime
37
+ is_superuser: typing.Optional[bool] = strawberry.UNSET
38
+ last_login: typing.Optional[datetime.datetime] = strawberry.UNSET
39
+
40
+ @strawberry.field
41
+ def permissions(self: User, info) -> typing.Optional[typing.List[str]]:
42
+ # Return permissions from token if exists
43
+ if hasattr(getattr(info.context.request, "token", None), "permissions"):
44
+ return info.context.request.token.permissions
45
+
46
+ # Return permissions from user
47
+ return info.context.request.user.permissions
48
+
49
+ @staticmethod
50
+ @utils.authentication_required
51
+ def resolve(info) -> typing.Optional["UserType"]:
52
+ return User.objects.get(id=info.context.request.user.id)
53
+
54
+
55
+ @strawberry.type
56
+ class WorkspaceConfigType:
57
+ object_type: str
58
+
59
+ @property
60
+ def config(self: auth.WorkspaceConfig) -> dict:
61
+ try:
62
+ return lib.to_dict(self.config)
63
+ except:
64
+ return self.config
65
+
66
+ # general preferences
67
+ # region
68
+
69
+ @strawberry.field
70
+ def default_currency(
71
+ self: auth.WorkspaceConfig,
72
+ ) -> typing.Optional[utils.CurrencyCodeEnum]:
73
+ return self.config.get("default_currency")
74
+
75
+ @strawberry.field
76
+ def default_country_code(
77
+ self: auth.WorkspaceConfig,
78
+ ) -> typing.Optional[utils.CountryCodeEnum]:
79
+ return self.config.get("default_country_code")
80
+
81
+ @strawberry.field
82
+ def default_weight_unit(
83
+ self: auth.WorkspaceConfig,
84
+ ) -> typing.Optional[utils.WeightUnitEnum]:
85
+ return self.config.get("default_weight_unit")
86
+
87
+ @strawberry.field
88
+ def default_dimension_unit(
89
+ self: auth.WorkspaceConfig,
90
+ ) -> typing.Optional[utils.DimensionUnitEnum]:
91
+ return self.config.get("default_dimension_unit")
92
+
93
+ @strawberry.field
94
+ def state_tax_id(self: auth.WorkspaceConfig) -> typing.Optional[str]:
95
+ return self.config.get("state_tax_id")
96
+
97
+ @strawberry.field
98
+ def federal_tax_id(self: auth.WorkspaceConfig) -> typing.Optional[str]:
99
+ return self.config.get("federal_tax_id")
100
+
101
+ @strawberry.field
102
+ def default_label_type(
103
+ self: auth.WorkspaceConfig,
104
+ ) -> typing.Optional[utils.LabelTypeEnum]:
105
+ return self.config.get("default_label_type")
106
+
107
+ # endregion
108
+
109
+ # default options preferences
110
+ # region
111
+
112
+ @strawberry.field
113
+ def insured_by_default(
114
+ self: auth.WorkspaceConfig,
115
+ ) -> typing.Optional[bool]:
116
+ return self.config.get("insured_by_default")
117
+
118
+ # endregion
119
+
120
+ # customs identifiers
121
+ # region
122
+
123
+ @strawberry.field
124
+ def customs_aes(self: auth.WorkspaceConfig) -> typing.Optional[str]:
125
+ return self.config.get("customs_aes")
126
+
127
+ @strawberry.field
128
+ def customs_eel_pfc(self: auth.WorkspaceConfig) -> typing.Optional[str]:
129
+ return self.config.get("customs_eel_pfc")
130
+
131
+ @strawberry.field
132
+ def customs_license_number(self: auth.WorkspaceConfig) -> typing.Optional[str]:
133
+ return self.config.get("customs_license_number")
134
+
135
+ @strawberry.field
136
+ def customs_certificate_number(self: auth.WorkspaceConfig) -> typing.Optional[str]:
137
+ return self.config.get("customs_certificate_number")
138
+
139
+ @strawberry.field
140
+ def customs_nip_number(self: auth.WorkspaceConfig) -> typing.Optional[str]:
141
+ return self.config.get("customs_nip_number")
142
+
143
+ @strawberry.field
144
+ def customs_eori_number(self: auth.WorkspaceConfig) -> typing.Optional[str]:
145
+ return self.config.get("customs_eori_number")
146
+
147
+ @strawberry.field
148
+ def customs_vat_registration_number(
149
+ self: auth.WorkspaceConfig,
150
+ ) -> typing.Optional[str]:
151
+ return self.config.get("customs_vat_registration_number")
152
+
153
+ # endregion
154
+
155
+ # label printing
156
+ # region
157
+
158
+ @strawberry.field
159
+ def label_message_1(self: auth.WorkspaceConfig) -> typing.Optional[str]:
160
+ return self.config.get("label_message_1")
161
+
162
+ @strawberry.field
163
+ def label_message_2(self: auth.WorkspaceConfig) -> typing.Optional[str]:
164
+ return self.config.get("label_message_2")
165
+
166
+ @strawberry.field
167
+ def label_message_3(self: auth.WorkspaceConfig) -> typing.Optional[str]:
168
+ return self.config.get("label_message_3")
169
+
170
+ @strawberry.field
171
+ def label_logo(self: auth.WorkspaceConfig) -> typing.Optional[str]:
172
+ return self.config.get("label_logo")
173
+
174
+ # endregion
175
+
176
+ @staticmethod
177
+ @utils.authentication_required
178
+ def resolve(info) -> typing.Optional["WorkspaceConfigType"]:
179
+ workspace_config = auth.WorkspaceConfig.access_by(info.context.request).first()
180
+
181
+ # Create a default workspace config if none exists
182
+ if workspace_config is None:
183
+ workspace_config = auth.WorkspaceConfig.objects.create(
184
+ created_by=info.context.request.user, config={}
185
+ )
186
+
187
+ return workspace_config
188
+
189
+
190
+ @strawberry.type
191
+ class SystemUsageType:
192
+ total_errors: typing.Optional[int] = None
193
+ order_volume: typing.Optional[float] = None
194
+ total_requests: typing.Optional[int] = None
195
+ total_trackers: typing.Optional[int] = None
196
+ total_shipments: typing.Optional[int] = None
197
+ organization_count: typing.Optional[int] = None
198
+ user_count: typing.Optional[int] = None
199
+ total_shipping_spend: typing.Optional[float] = None
200
+ api_errors: typing.Optional[typing.List[utils.UsageStatType]] = None
201
+ api_requests: typing.Optional[typing.List[utils.UsageStatType]] = None
202
+ order_volumes: typing.Optional[typing.List[utils.UsageStatType]] = None
203
+ shipment_count: typing.Optional[typing.List[utils.UsageStatType]] = None
204
+ shipping_spend: typing.Optional[typing.List[utils.UsageStatType]] = None
205
+ tracker_count: typing.Optional[typing.List[utils.UsageStatType]] = None
206
+
207
+ @staticmethod
208
+ @utils.authentication_required
209
+ def resolve(
210
+ info,
211
+ filter: typing.Optional[utils.UsageFilter] = strawberry.UNSET,
212
+ ) -> "SystemUsageType":
213
+ _test_mode = info.context.request.test_mode
214
+ _test_filter = dict(test_mode=_test_mode)
215
+ _filter = {
216
+ "date_before": datetime.datetime.now(),
217
+ "date_after": (datetime.datetime.now() - datetime.timedelta(days=30)),
218
+ **(filter if not utils.is_unset(filter) else utils.UsageFilter()).to_dict(),
219
+ }
220
+
221
+ api_requests = (
222
+ filters.LogFilter(
223
+ _filter,
224
+ core.APILogIndex.objects.filter(**_test_filter),
225
+ )
226
+ .qs.annotate(date=functions.TruncDay("requested_at"))
227
+ .values("date")
228
+ .annotate(count=models.Count("id"))
229
+ .order_by("-date")
230
+ )
231
+ api_errors = (
232
+ filters.LogFilter(
233
+ {**_filter, "status": "failed"},
234
+ core.APILogIndex.objects.filter(**_test_filter),
235
+ )
236
+ .qs.annotate(date=functions.TruncDay("requested_at"))
237
+ .values("date")
238
+ .annotate(count=models.Count("id"))
239
+ .order_by("-date")
240
+ )
241
+ order_volumes = (
242
+ order_filters.OrderFilters(
243
+ dict(
244
+ created_before=_filter["date_before"],
245
+ created_after=_filter["date_after"],
246
+ ),
247
+ orders.Order.objects.filter(**_test_filter).exclude(
248
+ status__in=["cancelled", "unfulfilled"]
249
+ ),
250
+ )
251
+ .qs.annotate(date=functions.TruncDay("created_at"))
252
+ .values("date")
253
+ .annotate(
254
+ count=models.Sum(
255
+ models.F("line_items__value_amount")
256
+ * models.F("line_items__quantity")
257
+ )
258
+ )
259
+ .order_by("-date")
260
+ )
261
+ shipment_count = (
262
+ filters.ShipmentFilters(
263
+ dict(
264
+ created_before=_filter["date_before"],
265
+ created_after=_filter["date_after"],
266
+ ),
267
+ manager.Shipment.objects.filter(**_test_filter),
268
+ )
269
+ .qs.annotate(date=functions.TruncDay("created_at"))
270
+ .values("date")
271
+ .annotate(count=models.Count("id"))
272
+ .order_by("-date")
273
+ )
274
+ shipping_spend = (
275
+ filters.ShipmentFilters(
276
+ dict(
277
+ created_before=_filter["date_before"],
278
+ created_after=_filter["date_after"],
279
+ ),
280
+ manager.Shipment.objects.filter(**_test_filter).exclude(
281
+ status__in=["cancelled", "draft"]
282
+ ),
283
+ )
284
+ .qs.annotate(date=functions.TruncDay("created_at"))
285
+ .values("date")
286
+ .annotate(
287
+ count=models.Count("id"),
288
+ amount=models.Sum(
289
+ functions.Cast("selected_rate__total_charge", models.FloatField())
290
+ ),
291
+ )
292
+ .order_by("-date")
293
+ )
294
+ tracker_count = (
295
+ filters.TrackerFilters(
296
+ dict(
297
+ created_before=_filter["date_before"],
298
+ created_after=_filter["date_after"],
299
+ ),
300
+ manager.Tracking.objects.filter(**_test_filter),
301
+ )
302
+ .qs.annotate(date=functions.TruncDay("created_at"))
303
+ .values("date")
304
+ .annotate(count=models.Count("id"))
305
+ .order_by("-date")
306
+ )
307
+
308
+ total_errors = sum([item["count"] for item in api_errors], 0)
309
+ total_requests = sum([item["count"] for item in api_requests], 0)
310
+ total_trackers = sum([item["count"] for item in tracker_count], 0)
311
+ total_shipments = sum([item["count"] for item in shipment_count], 0)
312
+ order_volume = lib.to_decimal(
313
+ sum(
314
+ [item["count"] for item in order_volumes if item["count"] is not None],
315
+ 0.0,
316
+ )
317
+ )
318
+ total_shipping_spend = lib.to_decimal(
319
+ sum(
320
+ [
321
+ item["amount"]
322
+ for item in shipping_spend
323
+ if item["amount"] is not None
324
+ ],
325
+ 0.0,
326
+ )
327
+ )
328
+ user_count = User.objects.count()
329
+ organization_count = 1
330
+
331
+ if conf.settings.MULTI_ORGANIZATIONS:
332
+ import karrio.server.orgs.models as orgs
333
+
334
+ organization_count = orgs.Organization.objects.count()
335
+
336
+ return SystemUsageType(
337
+ user_count=user_count,
338
+ order_volume=order_volume,
339
+ total_errors=total_errors,
340
+ total_requests=total_requests,
341
+ total_trackers=total_trackers,
342
+ total_shipments=total_shipments,
343
+ organization_count=organization_count,
344
+ total_shipping_spend=total_shipping_spend,
345
+ api_errors=[
346
+ utils.UsageStatType.parse(item, label="api_errors")
347
+ for item in api_errors
348
+ ],
349
+ api_requests=[
350
+ utils.UsageStatType.parse(item, label="api_requests")
351
+ for item in api_requests
352
+ ],
353
+ order_volumes=[
354
+ utils.UsageStatType.parse(item, label="order_volumes")
355
+ for item in order_volumes
356
+ ],
357
+ shipment_count=[
358
+ utils.UsageStatType.parse(item, label="shipment_count")
359
+ for item in shipment_count
360
+ ],
361
+ shipping_spend=[
362
+ utils.UsageStatType.parse(item, label="shipping_spend")
363
+ for item in shipping_spend
364
+ ],
365
+ tracker_count=[
366
+ utils.UsageStatType.parse(item, label="tracker_count")
367
+ for item in tracker_count
368
+ ],
369
+ )
370
+
371
+
372
+ @strawberry.type
373
+ class MetafieldType:
374
+ object_type: str
375
+ id: str
376
+ key: str
377
+ is_required: bool
378
+ type: utils.MetafieldTypeEnum
379
+ value: typing.Optional[utils.JSON] = None
380
+
381
+ @strawberry.field
382
+ def parsed_value(self: core.Metafield) -> typing.Optional[utils.JSON]:
383
+ """Return the value parsed according to its type."""
384
+ return self.get_parsed_value()
385
+
386
+ @staticmethod
387
+ @utils.authentication_required
388
+ def resolve(info, id: str) -> typing.Optional["MetafieldType"]:
389
+ return core.Metafield.access_by(info.context.request).filter(id=id).first()
390
+
391
+ @staticmethod
392
+ @utils.authentication_required
393
+ def resolve_list(
394
+ info,
395
+ filter: typing.Optional[inputs.MetafieldFilter] = strawberry.UNSET,
396
+ ) -> utils.Connection["MetafieldType"]:
397
+ _filter = filter if not utils.is_unset(filter) else inputs.MetafieldFilter()
398
+ queryset = core.Metafield.access_by(info.context.request)
399
+
400
+ # Apply filters
401
+ if not utils.is_unset(_filter.key):
402
+ queryset = queryset.filter(key__icontains=_filter.key)
403
+ if not utils.is_unset(_filter.type):
404
+ queryset = queryset.filter(type=_filter.type)
405
+ if not utils.is_unset(_filter.is_required):
406
+ queryset = queryset.filter(is_required=_filter.is_required)
407
+
408
+ return utils.paginated_connection(queryset, **_filter.pagination())
409
+
410
+
411
+ @strawberry.type
412
+ class LogType:
413
+ object_type: str
414
+ id: int
415
+ user: typing.Optional[UserType]
416
+ requested_at: typing.Optional[datetime.datetime]
417
+ response_ms: typing.Optional[int]
418
+ path: typing.Optional[str]
419
+ remote_addr: typing.Optional[str]
420
+ host: typing.Optional[str]
421
+ method: typing.Optional[str]
422
+ status_code: typing.Optional[int]
423
+ test_mode: typing.Optional[bool]
424
+
425
+ @strawberry.field
426
+ def data(self: core.APILog) -> typing.Optional[utils.JSON]:
427
+ try:
428
+ return lib.to_dict(self.data)
429
+ except:
430
+ return self.data
431
+
432
+ @strawberry.field
433
+ def response(self: core.APILog) -> typing.Optional[utils.JSON]:
434
+ try:
435
+ return lib.to_dict(self.response)
436
+ except:
437
+ return self.response
438
+
439
+ @strawberry.field
440
+ def query_params(self: core.APILog) -> typing.Optional[utils.JSON]:
441
+ try:
442
+ return lib.to_dict(self.query_params)
443
+ except:
444
+ return self.query_params
445
+
446
+ @strawberry.field
447
+ def records(
448
+ self: tracing.TracingRecord, info: Info
449
+ ) -> typing.List["TracingRecordType"]:
450
+ queryset = tracing.TracingRecord.objects.filter(meta__request_log_id=self.id)
451
+
452
+ if User.objects.filter(
453
+ id=info.context.request.user.id, is_staff=False
454
+ ).exists():
455
+ # exclude system carriers records if user is not staff
456
+ system_carriers = [
457
+ item["id"]
458
+ for item in providers.Carrier.system_carriers.all().values("id")
459
+ ]
460
+ queryset = queryset.exclude(meta__carrier_account_id__in=system_carriers)
461
+
462
+ return queryset
463
+
464
+ @staticmethod
465
+ @utils.authentication_required
466
+ def resolve(info, id: int) -> typing.Optional["LogType"]:
467
+ return core.APILogIndex.access_by(info.context.request).filter(id=id).first()
468
+
469
+ @staticmethod
470
+ @utils.authentication_required
471
+ def resolve_list(
472
+ info,
473
+ filter: typing.Optional[inputs.LogFilter] = strawberry.UNSET,
474
+ ) -> utils.Connection["LogType"]:
475
+ _filter = filter if not utils.is_unset(filter) else inputs.LogFilter()
476
+ queryset = filters.LogFilter(
477
+ _filter.to_dict(), core.APILogIndex.access_by(info.context.request)
478
+ ).qs
479
+ return utils.paginated_connection(queryset, **_filter.pagination())
480
+
481
+
482
+ @strawberry.type
483
+ class TracingRecordType:
484
+ object_type: str
485
+ id: typing.Optional[str]
486
+ key: typing.Optional[str]
487
+ timestamp: typing.Optional[float]
488
+ test_mode: typing.Optional[bool]
489
+ created_by: typing.Optional[UserType]
490
+ created_at: typing.Optional[datetime.datetime]
491
+ updated_at: typing.Optional[datetime.datetime]
492
+
493
+ @strawberry.field
494
+ def record(self: tracing.TracingRecord) -> typing.Optional[utils.JSON]:
495
+ try:
496
+ return lib.to_dict(self.record)
497
+ except:
498
+ return self.record
499
+
500
+ @strawberry.field
501
+ def meta(self: tracing.TracingRecord) -> typing.Optional[utils.JSON]:
502
+ try:
503
+ return lib.to_dict(self.meta)
504
+ except:
505
+ return self.meta
506
+
507
+ @staticmethod
508
+ @utils.authentication_required
509
+ def resolve(info, id: str) -> typing.Optional["TracingRecordType"]:
510
+ return (
511
+ tracing.TracingRecord.access_by(info.context.request).filter(id=id).first()
512
+ )
513
+
514
+ @staticmethod
515
+ @utils.authentication_required
516
+ def resolve_list(
517
+ info,
518
+ filter: typing.Optional[inputs.TracingRecordFilter] = strawberry.UNSET,
519
+ ) -> utils.Connection["TracingRecordType"]:
520
+ _filter = filter if not utils.is_unset(filter) else inputs.TracingRecordFilter()
521
+ queryset = filters.TracingRecordFilter(
522
+ _filter.to_dict(), tracing.TracingRecord.access_by(info.context.request)
523
+ ).qs
524
+ return utils.paginated_connection(queryset, **_filter.pagination())
525
+
526
+
527
+ @strawberry.type
528
+ class TokenType:
529
+ object_type: str
530
+ key: str
531
+ label: str
532
+ test_mode: bool
533
+ created: datetime.datetime
534
+
535
+ @strawberry.field
536
+ def permissions(self: auth.Token, info) -> typing.Optional[typing.List[str]]:
537
+ return self.permissions
538
+
539
+ @staticmethod
540
+ @utils.authentication_required
541
+ def resolve(info, org_id: typing.Optional[str] = strawberry.UNSET) -> "TokenType":
542
+ return user_serializers.TokenSerializer.retrieve_token(
543
+ info.context.request,
544
+ **({"org_id": org_id} if org_id is not strawberry.UNSET else {}),
545
+ )
546
+
547
+
548
+ @strawberry.type
549
+ class APIKeyType:
550
+ object_type: str
551
+ key: str
552
+ label: str
553
+ test_mode: bool
554
+ created: datetime.datetime
555
+
556
+ @strawberry.field
557
+ def permissions(self: auth.Token, info) -> typing.Optional[typing.List[str]]:
558
+ return self.permissions
559
+
560
+ @staticmethod
561
+ @utils.authentication_required
562
+ def resolve_list(
563
+ info,
564
+ ) -> typing.List["APIKeyType"]:
565
+ _filters = {
566
+ "user__id": info.context.request.user.id,
567
+ "test_mode": info.context.request.test_mode,
568
+ **(
569
+ {"org__id": info.context.request.org.id}
570
+ if getattr(info.context.request, "org", None) is not None
571
+ and settings.MULTI_ORGANIZATIONS
572
+ else {}
573
+ ),
574
+ }
575
+ keys = auth.Token.objects.filter(**_filters)
576
+
577
+ if keys.exists():
578
+ return keys
579
+
580
+ user_serializers.TokenSerializer.map(
581
+ data={}, context=info.context.request
582
+ ).save()
583
+
584
+ return auth.Token.objects.filter(**_filters)
585
+
586
+
587
+ @strawberry.type
588
+ class MessageType:
589
+ carrier_name: typing.Optional[str]
590
+ carrier_id: typing.Optional[str]
591
+ message: typing.Optional[str]
592
+ code: typing.Optional[str]
593
+ details: typing.Optional[utils.JSON] = None
594
+
595
+ @staticmethod
596
+ def parse(charge: dict):
597
+ return MessageType(
598
+ **{k: v for k, v in charge.items() if k in MessageType.__annotations__}
599
+ )
600
+
601
+
602
+ @strawberry.type
603
+ class ChargeType:
604
+ name: typing.Optional[str] = None
605
+ amount: typing.Optional[float] = None
606
+ currency: utils.CurrencyCodeEnum = None
607
+ id: typing.Optional[str] = None
608
+
609
+ @staticmethod
610
+ def parse(charge: dict):
611
+ return ChargeType(
612
+ **{k: v for k, v in charge.items() if k in ChargeType.__annotations__}
613
+ )
614
+
615
+
616
+ @strawberry.type
617
+ class RateType:
618
+ id: str
619
+ object_type: str
620
+ carrier_name: str
621
+ carrier_id: str
622
+ service: str
623
+ test_mode: bool
624
+ total_charge: float
625
+ currency: utils.CurrencyCodeEnum
626
+ extra_charges: typing.List[ChargeType]
627
+ meta: typing.Optional[utils.JSON] = None
628
+ transit_days: typing.Optional[int] = None
629
+
630
+ @staticmethod
631
+ def parse(rate: dict):
632
+ return RateType(
633
+ **{
634
+ "object_type": "rate",
635
+ **{k: v for k, v in rate.items() if k in RateType.__annotations__},
636
+ "extra_charges": [
637
+ ChargeType.parse(charge)
638
+ for charge in (rate.get("extra_charges") or [])
639
+ ],
640
+ }
641
+ )
642
+
643
+
644
+ @strawberry.type
645
+ class CommodityType:
646
+ id: str
647
+ object_type: str
648
+ weight: float
649
+ quantity: int
650
+ metadata: utils.JSON
651
+ sku: typing.Optional[str]
652
+ title: typing.Optional[str]
653
+ hs_code: typing.Optional[str]
654
+ description: typing.Optional[str]
655
+ value_amount: typing.Optional[float]
656
+ weight_unit: typing.Optional[utils.WeightUnitEnum]
657
+ origin_country: typing.Optional[utils.CountryCodeEnum]
658
+ value_currency: typing.Optional[utils.CurrencyCodeEnum]
659
+ created_at: typing.Optional[datetime.datetime]
660
+ updated_at: typing.Optional[datetime.datetime]
661
+ created_by: typing.Optional[UserType]
662
+ parent_id: typing.Optional[str] = None
663
+ parent: typing.Optional["CommodityType"] = None
664
+ unfulfilled_quantity: typing.Optional[int] = None
665
+
666
+
667
+ @strawberry.type
668
+ class AddressType:
669
+ id: str
670
+ object_type: str
671
+ postal_code: typing.Optional[str]
672
+ city: typing.Optional[str]
673
+ federal_tax_id: typing.Optional[str]
674
+ state_tax_id: typing.Optional[str]
675
+ person_name: typing.Optional[str]
676
+ company_name: typing.Optional[str]
677
+ country_code: utils.CountryCodeEnum
678
+ email: typing.Optional[str]
679
+ phone_number: typing.Optional[str]
680
+ state_code: typing.Optional[str]
681
+ residential: typing.Optional[bool]
682
+ street_number: typing.Optional[str]
683
+ address_line1: typing.Optional[str]
684
+ address_line2: typing.Optional[str]
685
+ created_at: typing.Optional[datetime.datetime]
686
+ updated_at: typing.Optional[datetime.datetime]
687
+ created_by: typing.Optional[UserType]
688
+ validate_location: typing.Optional[bool]
689
+ validation: typing.Optional[utils.JSON] = None
690
+
691
+
692
+ @strawberry.type
693
+ class ParcelType:
694
+ id: str
695
+ object_type: str
696
+ weight: typing.Optional[float]
697
+ width: typing.Optional[float]
698
+ height: typing.Optional[float]
699
+ length: typing.Optional[float]
700
+ packaging_type: typing.Optional[str]
701
+ package_preset: typing.Optional[str]
702
+ description: typing.Optional[str]
703
+ content: typing.Optional[str]
704
+ is_document: typing.Optional[bool]
705
+ weight_unit: typing.Optional[utils.WeightUnitEnum]
706
+ dimension_unit: typing.Optional[utils.DimensionUnitEnum]
707
+ freight_class: typing.Optional[str]
708
+ reference_number: typing.Optional[str]
709
+ created_at: datetime.datetime
710
+ updated_at: datetime.datetime
711
+ created_by: UserType
712
+
713
+ @strawberry.field
714
+ def items(self: manager.Parcel) -> typing.List[CommodityType]:
715
+ return self.items.all()
716
+
717
+
718
+ @strawberry.type
719
+ class DutyType:
720
+ paid_by: typing.Optional[utils.PaidByEnum] = None
721
+ currency: typing.Optional[utils.CurrencyCodeEnum] = None
722
+ account_number: typing.Optional[str] = None
723
+ declared_value: typing.Optional[float] = None
724
+ bill_to: typing.Optional[AddressType] = None
725
+
726
+
727
+ @strawberry.type
728
+ class CustomsType:
729
+ id: str
730
+ object_type: str
731
+ certify: typing.Optional[bool] = strawberry.UNSET
732
+ commercial_invoice: typing.Optional[bool] = strawberry.UNSET
733
+ content_type: typing.Optional[utils.CustomsContentTypeEnum] = strawberry.UNSET
734
+ content_description: typing.Optional[str] = strawberry.UNSET
735
+ incoterm: typing.Optional[utils.IncotermCodeEnum] = strawberry.UNSET
736
+ invoice: typing.Optional[str] = strawberry.UNSET
737
+ invoice_date: typing.Optional[str] = strawberry.UNSET
738
+ signer: typing.Optional[str] = strawberry.UNSET
739
+ created_at: typing.Optional[datetime.datetime] = strawberry.UNSET
740
+ updated_at: typing.Optional[datetime.datetime] = strawberry.UNSET
741
+ created_by: typing.Optional[UserType] = strawberry.UNSET
742
+ options: typing.Optional[utils.JSON] = strawberry.UNSET
743
+ duty_billing_address: typing.Optional[AddressType] = strawberry.UNSET
744
+
745
+ @strawberry.field
746
+ def duty(self: manager) -> typing.Optional[DutyType]:
747
+ if self.duty is None:
748
+ return None
749
+
750
+ return DutyType(**self.duty)
751
+
752
+ @strawberry.field
753
+ def commodities(self: manager.Customs) -> typing.List[CommodityType]:
754
+ return self.commodities.all()
755
+
756
+
757
+ @strawberry.type
758
+ class AddressTemplateType:
759
+ id: str
760
+ object_type: str
761
+ label: str
762
+ address: AddressType
763
+ is_default: typing.Optional[bool] = None
764
+
765
+ @staticmethod
766
+ @utils.authentication_required
767
+ def resolve_list(
768
+ info,
769
+ filter: typing.Optional[inputs.AddressFilter] = strawberry.UNSET,
770
+ ) -> utils.Connection["AddressTemplateType"]:
771
+ _filter = inputs.AddressFilter() if utils.is_unset(filter) else filter
772
+ _search = _filter.to_dict()
773
+ _query = models.Q()
774
+
775
+ if any(_search.get("label") or ""):
776
+ _value = _search.get("label")
777
+ _query = _query | models.Q(label__icontains=_value)
778
+
779
+ if any(_search.get("address") or ""):
780
+ _value = _search.get("address")
781
+ _query = (
782
+ _query
783
+ | models.Q(address__address_line1__icontains=_value)
784
+ | models.Q(address__address_line2__icontains=_value)
785
+ | models.Q(address__postal_code__icontains=_value)
786
+ | models.Q(address__person_name__icontains=_value)
787
+ | models.Q(address__company_name__icontains=_value)
788
+ | models.Q(address__country_code__icontains=_value)
789
+ | models.Q(address__city__icontains=_value)
790
+ | models.Q(address__email__icontains=_value)
791
+ | models.Q(address__phone_number__icontains=_value)
792
+ )
793
+
794
+ if any(_search.get("keyword") or ""):
795
+ _value = _search.get("keyword")
796
+ _query = (
797
+ _query
798
+ | models.Q(label__icontains=_value)
799
+ | models.Q(address__address_line1__icontains=_value)
800
+ | models.Q(address__address_line2__icontains=_value)
801
+ | models.Q(address__postal_code__icontains=_value)
802
+ | models.Q(address__person_name__icontains=_value)
803
+ | models.Q(address__company_name__icontains=_value)
804
+ | models.Q(address__country_code__icontains=_value)
805
+ | models.Q(address__city__icontains=_value)
806
+ | models.Q(address__email__icontains=_value)
807
+ | models.Q(address__phone_number__icontains=_value)
808
+ )
809
+
810
+ _queryset = graph.Template.access_by(info.context.request).filter(
811
+ _query, address__isnull=False
812
+ )
813
+
814
+ return utils.paginated_connection(_queryset, **_filter.pagination())
815
+
816
+
817
+ @strawberry.type
818
+ class ParcelTemplateType:
819
+ id: str
820
+ object_type: str
821
+ label: str
822
+ parcel: ParcelType
823
+ is_default: typing.Optional[bool]
824
+
825
+ @staticmethod
826
+ @utils.authentication_required
827
+ def resolve_list(
828
+ info,
829
+ filter: typing.Optional[inputs.TemplateFilter] = strawberry.UNSET,
830
+ ) -> utils.Connection["ParcelTemplateType"]:
831
+ _filter = inputs.TemplateFilter() if filter == strawberry.UNSET else filter
832
+ _search = _filter.to_dict()
833
+ _query = models.Q()
834
+
835
+ if any(_search.get("label") or ""):
836
+ _value = _search.get("label")
837
+ _query = _query | models.Q(label__icontains=_value)
838
+
839
+ if any(_search.get("keyword") or ""):
840
+ _value = _search.get("keyword")
841
+ _query = _query | models.Q(label__icontains=_value)
842
+
843
+ queryset = graph.Template.access_by(info.context.request).filter(
844
+ _query,
845
+ parcel__isnull=False,
846
+ )
847
+
848
+ return utils.paginated_connection(queryset, **_filter.pagination())
849
+
850
+
851
+ @strawberry.type
852
+ class CustomsTemplateType:
853
+ id: str
854
+ object_type: str
855
+ label: str
856
+ customs: CustomsType
857
+ is_default: typing.Optional[bool]
858
+
859
+ @staticmethod
860
+ @utils.authentication_required
861
+ def resolve_list(
862
+ info,
863
+ filter: typing.Optional[inputs.TemplateFilter] = strawberry.UNSET,
864
+ ) -> utils.Connection["CustomsTemplateType"]:
865
+ _filter = filter if not utils.is_unset(filter) else inputs.TemplateFilter()
866
+
867
+ queryset = graph.Template.access_by(info.context.request).filter(
868
+ customs__isnull=False,
869
+ **(
870
+ {"label__icontain": _filter.label}
871
+ if _filter.label is not strawberry.UNSET
872
+ else {}
873
+ ),
874
+ )
875
+ return utils.paginated_connection(queryset, **_filter.pagination())
876
+
877
+
878
+ @strawberry.type
879
+ class DefaultTemplatesType:
880
+ default_address: typing.Optional[AddressTemplateType] = None
881
+ default_customs: typing.Optional[CustomsTemplateType] = None
882
+ default_parcel: typing.Optional[ParcelTemplateType] = None
883
+
884
+ @staticmethod
885
+ @utils.authentication_required
886
+ def resolve(info) -> "DefaultTemplatesType":
887
+ templates = graph.Template.access_by(info.context.request).filter(
888
+ is_default=True
889
+ )
890
+
891
+ return DefaultTemplatesType( # type: ignore
892
+ default_address=templates.filter(address__isnull=False).first(),
893
+ default_customs=templates.filter(customs__isnull=False).first(),
894
+ default_parcel=templates.filter(parcel__isnull=False).first(),
895
+ )
896
+
897
+
898
+ @strawberry.type
899
+ class TrackingEventType:
900
+ description: typing.Optional[str] = None
901
+ location: typing.Optional[str] = None
902
+ code: typing.Optional[str] = None
903
+ date: typing.Optional[str] = None
904
+ time: typing.Optional[str] = None
905
+ latitude: typing.Optional[float] = None
906
+ longitude: typing.Optional[float] = None
907
+
908
+ @staticmethod
909
+ def parse(charge: dict):
910
+ return TrackingEventType(
911
+ **{
912
+ k: v
913
+ for k, v in charge.items()
914
+ if k in TrackingEventType.__annotations__
915
+ }
916
+ )
917
+
918
+
919
+ @strawberry.type
920
+ class TrackingInfoType:
921
+ carrier_tracking_link: typing.Optional[str] = None
922
+ customer_name: typing.Optional[str] = None
923
+ expected_delivery: typing.Optional[str] = None
924
+ note: typing.Optional[str] = None
925
+ order_date: typing.Optional[str] = None
926
+ order_id: typing.Optional[str] = None
927
+ package_weight: typing.Optional[str] = None
928
+ package_weight_unit: typing.Optional[str] = None
929
+ shipment_package_count: typing.Optional[str] = None
930
+ shipment_pickup_date: typing.Optional[str] = None
931
+ shipment_delivery_date: typing.Optional[str] = None
932
+ shipment_service: typing.Optional[str] = None
933
+ shipment_origin_country: typing.Optional[str] = None
934
+ shipment_origin_postal_code: typing.Optional[str] = None
935
+ shipment_destination_country: typing.Optional[str] = None
936
+ shipment_destination_postal_code: typing.Optional[str] = None
937
+ shipping_date: typing.Optional[str] = None
938
+ signed_by: typing.Optional[str] = None
939
+ source: typing.Optional[str] = None
940
+
941
+ @staticmethod
942
+ def parse(charge: dict):
943
+ return TrackingInfoType(
944
+ **{k: v for k, v in charge.items() if k in TrackingInfoType.__annotations__}
945
+ )
946
+
947
+
948
+ @strawberry.type
949
+ class TrackerType:
950
+ id: str
951
+ object_type: str
952
+ tracking_number: str
953
+ test_mode: bool
954
+ metadata: utils.JSON
955
+ status: utils.TrackerStatusEnum
956
+ delivered: typing.Optional[bool]
957
+ estimated_delivery: typing.Optional[datetime.date]
958
+ document_image_url: typing.Optional[str]
959
+ signature_image_url: typing.Optional[str]
960
+ options: typing.Optional[utils.JSON]
961
+ meta: typing.Optional[utils.JSON]
962
+ shipment: typing.Optional["ShipmentType"]
963
+ created_at: datetime.datetime
964
+ updated_at: datetime.datetime
965
+ created_by: UserType
966
+
967
+ @strawberry.field
968
+ def carrier_id(self: manager.Tracking) -> str:
969
+ return getattr(self.tracking_carrier, "carrier_id", None)
970
+
971
+ @strawberry.field
972
+ def carrier_name(self: manager.Tracking) -> str:
973
+ return getattr(self.tracking_carrier, "carrier_name", None)
974
+
975
+ @strawberry.field
976
+ def info(self: manager.Tracking) -> typing.Optional[TrackingInfoType]:
977
+ return TrackingInfoType.parse(self.info) if self.info else None
978
+
979
+ @strawberry.field
980
+ def events(self: manager.Tracking) -> typing.List[TrackingEventType]:
981
+ return [TrackingEventType.parse(msg) for msg in self.events or []]
982
+
983
+ @strawberry.field
984
+ def messages(self: manager.Tracking) -> typing.List[MessageType]:
985
+ return [MessageType.parse(msg) for msg in self.messages or []]
986
+
987
+ @strawberry.field
988
+ def tracking_carrier(
989
+ self: manager.Tracking,
990
+ ) -> typing.Optional["CarrierConnectionType"]:
991
+ return self.tracking_carrier
992
+
993
+ @staticmethod
994
+ @utils.authentication_required
995
+ def resolve(info, id: str) -> typing.Optional["TrackerType"]:
996
+ return manager.Tracking.access_by(info.context.request).filter(id=id).first()
997
+
998
+ @staticmethod
999
+ @utils.authentication_required
1000
+ def resolve_list(
1001
+ info,
1002
+ filter: typing.Optional[inputs.TrackerFilter] = strawberry.UNSET,
1003
+ ) -> utils.Connection["TrackerType"]:
1004
+ _filter = filter if not utils.is_unset(filter) else inputs.TrackerFilter()
1005
+ queryset = filters.TrackerFilters(
1006
+ _filter.to_dict(), manager.Tracking.access_by(info.context.request)
1007
+ ).qs
1008
+ return utils.paginated_connection(queryset, **_filter.pagination())
1009
+
1010
+
1011
+ @strawberry.type
1012
+ class ManifestType:
1013
+ id: str
1014
+ object_type: str
1015
+ test_mode: bool
1016
+ metadata: utils.JSON
1017
+ meta: utils.JSON
1018
+ options: utils.JSON
1019
+ address: AddressType
1020
+ shipment_identifiers: typing.List[str]
1021
+ manifest_url: typing.Optional[str]
1022
+ reference: typing.Optional[str]
1023
+ created_at: datetime.datetime
1024
+ updated_at: datetime.datetime
1025
+
1026
+ @strawberry.field
1027
+ def carrier_id(self: manager.Manifest) -> str:
1028
+ return getattr(self.manifest_carrier, "carrier_id", None)
1029
+
1030
+ @strawberry.field
1031
+ def carrier_name(self: manager.Manifest) -> str:
1032
+ return getattr(self.manifest_carrier, "carrier_name", None)
1033
+
1034
+ @strawberry.field
1035
+ def messages(self: manager.Manifest) -> typing.List[MessageType]:
1036
+ return [MessageType.parse(msg) for msg in self.messages or []]
1037
+
1038
+ @strawberry.field
1039
+ def manifest_carrier(
1040
+ self: manager.Manifest,
1041
+ ) -> typing.Optional["CarrierConnectionType"]:
1042
+ return self.manifest_carrier
1043
+
1044
+ @staticmethod
1045
+ @utils.authentication_required
1046
+ @utils.authorization_required(["manage_shipments"])
1047
+ def resolve(info, id: str) -> typing.Optional["ManifestType"]:
1048
+ return manager.Manifest.access_by(info.context.request).filter(id=id).first()
1049
+
1050
+ @staticmethod
1051
+ @utils.authentication_required
1052
+ @utils.authorization_required(["manage_shipments"])
1053
+ def resolve_list(
1054
+ info,
1055
+ filter: typing.Optional[inputs.ManifestFilter] = strawberry.UNSET,
1056
+ ) -> utils.Connection["ManifestType"]:
1057
+ _filter = filter if not utils.is_unset(filter) else inputs.ManifestFilter()
1058
+ queryset = filters.ManifestFilters(
1059
+ _filter.to_dict(), manager.Manifest.access_by(info.context.request)
1060
+ ).qs
1061
+ return utils.paginated_connection(queryset, **_filter.pagination())
1062
+
1063
+
1064
+ @strawberry.type
1065
+ class PaymentType:
1066
+ account_number: typing.Optional[str] = None
1067
+ paid_by: typing.Optional[utils.PaidByEnum] = None
1068
+ currency: typing.Optional[utils.CurrencyCodeEnum] = None
1069
+
1070
+ @strawberry.type
1071
+ class ShipmentType:
1072
+ id: str
1073
+ object_type: str
1074
+ test_mode: bool
1075
+ shipper: AddressType
1076
+ recipient: AddressType
1077
+ options: utils.JSON
1078
+ metadata: utils.JSON
1079
+ status: utils.ShipmentStatusEnum
1080
+ return_address: typing.Optional[AddressType]
1081
+ billing_address: typing.Optional[AddressType]
1082
+ meta: typing.Optional[utils.JSON]
1083
+ label_type: typing.Optional[utils.LabelTypeEnum]
1084
+ tracking_number: typing.Optional[str]
1085
+ shipment_identifier: typing.Optional[str]
1086
+ tracking_url: typing.Optional[str]
1087
+ reference: typing.Optional[str]
1088
+ customs: typing.Optional[CustomsType]
1089
+ services: typing.Optional[typing.List[str]]
1090
+ service: typing.Optional[str]
1091
+ carrier_ids: typing.List[str]
1092
+ selected_rate_id: typing.Optional[str]
1093
+ tracker_id: typing.Optional[str]
1094
+ label_url: typing.Optional[str]
1095
+ invoice_url: typing.Optional[str]
1096
+ tracker: typing.Optional[TrackerType]
1097
+ created_at: datetime.datetime
1098
+ updated_at: datetime.datetime
1099
+ created_by: UserType
1100
+
1101
+ @strawberry.field
1102
+ def carrier_id(self: manager.Shipment) -> typing.Optional[str]:
1103
+ return getattr(self.selected_rate_carrier, "carrier_id", None)
1104
+
1105
+ @strawberry.field
1106
+ def carrier_name(self: manager.Shipment) -> typing.Optional[str]:
1107
+ return getattr(self.selected_rate_carrier, "carrier_name", None)
1108
+
1109
+ @strawberry.field
1110
+ def parcels(self: manager.Shipment) -> typing.List[ParcelType]:
1111
+ return self.parcels.all()
1112
+
1113
+ @strawberry.field
1114
+ def rates(self: manager.Shipment) -> typing.List[RateType]:
1115
+ return [RateType.parse(rate) for rate in self.rates or []]
1116
+
1117
+ @strawberry.field
1118
+ def selected_rate(self: manager.Shipment) -> typing.Optional[RateType]:
1119
+ return RateType.parse(self.selected_rate) if self.selected_rate else None
1120
+
1121
+ @strawberry.field
1122
+ def selected_rate_carrier(
1123
+ self: manager.Shipment,
1124
+ ) -> typing.Optional["CarrierConnectionType"]:
1125
+ return self.selected_rate_carrier
1126
+
1127
+ @strawberry.field
1128
+ def payment(self: manager.Shipment) -> typing.Optional[PaymentType]:
1129
+ return PaymentType(**self.payment) if self.payment else None
1130
+
1131
+ @strawberry.field
1132
+ def messages(self: manager.Shipment) -> typing.List[MessageType]:
1133
+ return [MessageType.parse(msg) for msg in self.messages or []]
1134
+
1135
+ @staticmethod
1136
+ @utils.authentication_required
1137
+ @utils.authorization_required(["manage_shipments"])
1138
+ def resolve(info, id: str) -> typing.Optional["ShipmentType"]:
1139
+ return manager.Shipment.access_by(info.context.request).filter(id=id).first()
1140
+
1141
+ @staticmethod
1142
+ @utils.authentication_required
1143
+ @utils.authorization_required(["manage_shipments"])
1144
+ def resolve_list(
1145
+ info,
1146
+ filter: typing.Optional[inputs.ShipmentFilter] = strawberry.UNSET,
1147
+ ) -> utils.Connection["ShipmentType"]:
1148
+ _filter = filter if not utils.is_unset(filter) else inputs.ShipmentFilter()
1149
+ queryset = filters.ShipmentFilters(
1150
+ _filter.to_dict(), manager.Shipment.access_by(info.context.request)
1151
+ ).qs
1152
+ return utils.paginated_connection(queryset, **_filter.pagination())
1153
+
1154
+
1155
+ @strawberry.type
1156
+ class ServiceZoneType:
1157
+ object_type: str
1158
+ id: typing.Optional[str] = None
1159
+ label: typing.Optional[str] = None
1160
+ rate: typing.Optional[float] = None
1161
+
1162
+ min_weight: typing.Optional[float] = None
1163
+ max_weight: typing.Optional[float] = None
1164
+
1165
+ transit_days: typing.Optional[int] = None
1166
+ transit_time: typing.Optional[float] = None
1167
+
1168
+ radius: typing.Optional[float] = None
1169
+ latitude: typing.Optional[float] = None
1170
+ longitude: typing.Optional[float] = None
1171
+
1172
+ cities: typing.Optional[typing.List[str]] = None
1173
+ postal_codes: typing.Optional[typing.List[str]] = None
1174
+ country_codes: typing.Optional[typing.List[utils.CountryCodeEnum]] = None
1175
+
1176
+ @staticmethod
1177
+ def parse(zone: dict):
1178
+ return ServiceZoneType(
1179
+ **{
1180
+ "object_type": "zone",
1181
+ **{
1182
+ k: v
1183
+ for k, v in zone.items()
1184
+ if k in ServiceZoneType.__annotations__
1185
+ },
1186
+ }
1187
+ )
1188
+
1189
+
1190
+ @strawberry.type
1191
+ class ServiceLevelType:
1192
+ id: str
1193
+ object_type: str
1194
+ service_name: typing.Optional[str]
1195
+ service_code: typing.Optional[str]
1196
+ carrier_service_code: typing.Optional[str]
1197
+ description: typing.Optional[str]
1198
+ active: typing.Optional[bool]
1199
+
1200
+ currency: typing.Optional[utils.CurrencyCodeEnum]
1201
+ transit_days: typing.Optional[int]
1202
+ transit_time: typing.Optional[float]
1203
+
1204
+ max_width: typing.Optional[float]
1205
+ max_height: typing.Optional[float]
1206
+ max_length: typing.Optional[float]
1207
+ dimension_unit: typing.Optional[utils.DimensionUnitEnum]
1208
+
1209
+ max_weight: typing.Optional[float]
1210
+ weight_unit: typing.Optional[utils.WeightUnitEnum]
1211
+
1212
+ domicile: typing.Optional[bool]
1213
+ international: typing.Optional[bool]
1214
+
1215
+ @strawberry.field
1216
+ def zones(self: providers.ServiceLevel) -> typing.List[ServiceZoneType]:
1217
+ # Use computed_zones for backward compatibility with optimized structure
1218
+ return [ServiceZoneType.parse(zone) for zone in self.computed_zones]
1219
+
1220
+ @strawberry.field
1221
+ def metadata(self: providers.RateSheet) -> typing.Optional[utils.JSON]:
1222
+ try:
1223
+ return lib.to_dict(self.metadata)
1224
+ except:
1225
+ return self.metadata
1226
+
1227
+
1228
+ @strawberry.type
1229
+ class LabelTemplateType:
1230
+ id: str
1231
+ object_type: str
1232
+ slug: typing.Optional[str]
1233
+ template: typing.Optional[str]
1234
+ width: typing.Optional[int]
1235
+ height: typing.Optional[int]
1236
+ shipment_sample: typing.Optional[utils.JSON]
1237
+ template_type: typing.Optional[utils.LabelTemplateTypeEnum]
1238
+
1239
+
1240
+ @strawberry.type
1241
+ class RateSheetType:
1242
+ object_type: str
1243
+ id: str
1244
+ name: str
1245
+ slug: str
1246
+ carrier_name: utils.CarrierNameEnum
1247
+
1248
+ @strawberry.field
1249
+ def metadata(self: providers.RateSheet) -> typing.Optional[utils.JSON]:
1250
+ try:
1251
+ return lib.to_dict(self.metadata)
1252
+ except:
1253
+ return self.metadata
1254
+
1255
+ @strawberry.field
1256
+ def carriers(self: providers.RateSheet) -> typing.List["CarrierConnectionType"]:
1257
+ return self.carriers
1258
+
1259
+ @strawberry.field
1260
+ def services(self: providers.RateSheet) -> typing.List[ServiceLevelType]:
1261
+ return self.services.all()
1262
+
1263
+ @staticmethod
1264
+ @utils.authentication_required
1265
+ @utils.authorization_required(["manage_carriers"])
1266
+ def resolve(info, id: str) -> typing.Optional["RateSheetType"]:
1267
+ return providers.RateSheet.access_by(info.context.request).filter(id=id).first()
1268
+
1269
+ @staticmethod
1270
+ @utils.authentication_required
1271
+ @utils.authorization_required(["manage_carriers"])
1272
+ def resolve_list(
1273
+ info,
1274
+ filter: typing.Optional[inputs.RateSheetFilter] = strawberry.UNSET,
1275
+ ) -> utils.Connection["RateSheetType"]:
1276
+ _filter = filter if not utils.is_unset(filter) else inputs.RateSheetFilter()
1277
+ queryset = filters.RateSheetFilter(
1278
+ _filter.to_dict(), providers.RateSheet.access_by(info.context.request)
1279
+ ).qs
1280
+ return utils.paginated_connection(queryset, **_filter.pagination())
1281
+
1282
+
1283
+ @strawberry.type
1284
+ class SystemConnectionType:
1285
+ id: str
1286
+ active: bool
1287
+ carrier_id: str
1288
+ display_name: str
1289
+ test_mode: bool
1290
+ capabilities: typing.List[str]
1291
+ created_at: typing.Optional[datetime.datetime]
1292
+ updated_at: typing.Optional[datetime.datetime]
1293
+
1294
+ @strawberry.field
1295
+ def carrier_name(self: providers.Carrier) -> str:
1296
+ return getattr(self, "settings", self).carrier_name
1297
+
1298
+ @strawberry.field
1299
+ def enabled(self: providers.Carrier, info: Info) -> bool:
1300
+ if hasattr(self, "active_orgs"):
1301
+ return self.active_orgs.filter(id=info.context.request.org.id).exists()
1302
+
1303
+ return self.active_users.filter(id=info.context.request.user.id).exists()
1304
+
1305
+ @strawberry.field
1306
+ def config(self: providers.Carrier, info: Info) -> typing.Optional[utils.JSON]:
1307
+ return getattr(self, "config", None)
1308
+
1309
+ @staticmethod
1310
+ @utils.authentication_required
1311
+ def resolve_list(
1312
+ info,
1313
+ filter: typing.Optional[inputs.CarrierFilter] = strawberry.UNSET,
1314
+ ) -> utils.Connection["SystemConnectionType"]:
1315
+ _filter = filter if not utils.is_unset(filter) else inputs.CarrierFilter()
1316
+ connections = filters.CarrierFilters(
1317
+ _filter.to_dict(),
1318
+ providers.Carrier.system_carriers.resolve_config_for(
1319
+ info.context.request
1320
+ ).filter(
1321
+ active=True,
1322
+ test_mode=getattr(info.context.request, "test_mode", False),
1323
+ ),
1324
+ ).qs
1325
+ return utils.paginated_connection(connections, **_filter.pagination())
1326
+
1327
+
1328
+ @strawberry.type
1329
+ class CarrierConnectionType:
1330
+ id: str
1331
+ carrier_id: str
1332
+ carrier_name: str
1333
+ display_name: str
1334
+ active: bool
1335
+ is_system: bool
1336
+ test_mode: bool
1337
+ credentials: utils.JSON
1338
+ capabilities: typing.List[str]
1339
+ rate_sheet: typing.Optional[RateSheetType] = None
1340
+
1341
+ @strawberry.field
1342
+ def metadata(self: providers.Carrier, info: Info) -> typing.Optional[utils.JSON]:
1343
+ return getattr(self, "metadata", None)
1344
+
1345
+ @strawberry.field
1346
+ def config(self: providers.Carrier, info: Info) -> typing.Optional[utils.JSON]:
1347
+ return getattr(self, "config", None)
1348
+
1349
+ def rate_sheet(
1350
+ self: providers.Carrier, info: Info
1351
+ ) -> typing.Optional[RateSheetType]:
1352
+ return getattr(self, "rate_sheet", None)
1353
+
1354
+ @staticmethod
1355
+ @utils.utils.error_wrapper
1356
+ @utils.authentication_required
1357
+ @utils.authorization_required(["manage_carriers"])
1358
+ def resolve_list_legacy(
1359
+ info,
1360
+ filter: typing.Optional[inputs.CarrierFilter] = strawberry.UNSET,
1361
+ ) -> typing.List["CarrierConnectionType"]:
1362
+ _filter = filter if not utils.is_unset(filter) else inputs.CarrierFilter()
1363
+ connections = filters.CarrierFilters(
1364
+ _filter.to_dict(),
1365
+ providers.Carrier.access_by(info.context.request).filter(is_system=False),
1366
+ ).qs
1367
+ return connections
1368
+
1369
+ @staticmethod
1370
+ @utils.utils.error_wrapper
1371
+ @utils.authentication_required
1372
+ @utils.authorization_required(["read_carriers"])
1373
+ def resolve(
1374
+ info,
1375
+ id: str,
1376
+ ) -> typing.Optional["CarrierConnectionType"]:
1377
+ connection = (
1378
+ providers.Carrier.access_by(info.context.request).filter(id=id).first()
1379
+ )
1380
+ return connection
1381
+
1382
+ @staticmethod
1383
+ @utils.utils.error_wrapper
1384
+ @utils.authentication_required
1385
+ @utils.authorization_required(["read_carriers"])
1386
+ def resolve_list(
1387
+ info,
1388
+ filter: typing.Optional[inputs.CarrierFilter] = strawberry.UNSET,
1389
+ ) -> utils.Connection["CarrierConnectionType"]:
1390
+ _filter = filter if not utils.is_unset(filter) else inputs.CarrierFilter()
1391
+ queryset = filters.CarrierFilters(
1392
+ _filter.to_dict(),
1393
+ providers.Carrier.access_by(info.context.request).filter(is_system=False),
1394
+ ).qs
1395
+ connections = utils.paginated_connection(queryset, **_filter.pagination())
1396
+
1397
+ return utils.Connection(
1398
+ page_info=connections.page_info,
1399
+ edges=[
1400
+ utils.Edge(
1401
+ node=edge.node,
1402
+ cursor=edge.cursor,
1403
+ )
1404
+ for edge in connections.edges
1405
+ ],
1406
+ )