karrio-server-manager 2025.5rc1__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 (102) hide show
  1. karrio/server/manager/__init__.py +1 -0
  2. karrio/server/manager/admin.py +1 -0
  3. karrio/server/manager/apps.py +13 -0
  4. karrio/server/manager/migrations/0001_initial.py +1358 -0
  5. karrio/server/manager/migrations/0002_auto_20201127_0721.py +61 -0
  6. karrio/server/manager/migrations/0003_auto_20201230_0820.py +34 -0
  7. karrio/server/manager/migrations/0004_auto_20210125_2125.py +18 -0
  8. karrio/server/manager/migrations/0005_auto_20210216_0758.py +27 -0
  9. karrio/server/manager/migrations/0006_auto_20210307_0438.py +24 -0
  10. karrio/server/manager/migrations/0006_auto_20210308_0302.py +53 -0
  11. karrio/server/manager/migrations/0007_merge_20210311_1428.py +14 -0
  12. karrio/server/manager/migrations/0008_remove_shipment_doc_images.py +17 -0
  13. karrio/server/manager/migrations/0009_auto_20210326_1425.py +28 -0
  14. karrio/server/manager/migrations/0010_auto_20210403_1404.py +28 -0
  15. karrio/server/manager/migrations/0011_auto_20210426_1924.py +48 -0
  16. karrio/server/manager/migrations/0012_auto_20210427_1319.py +24 -0
  17. karrio/server/manager/migrations/0013_customs_invoice_date.py +18 -0
  18. karrio/server/manager/migrations/0014_auto_20210515_0928.py +24 -0
  19. karrio/server/manager/migrations/0015_auto_20210601_0340.py +182 -0
  20. karrio/server/manager/migrations/0016_shipment_archived.py +18 -0
  21. karrio/server/manager/migrations/0017_auto_20210629_1650.py +22 -0
  22. karrio/server/manager/migrations/0018_auto_20210705_1049.py +23 -0
  23. karrio/server/manager/migrations/0019_auto_20210722_1131.py +43 -0
  24. karrio/server/manager/migrations/0020_tracking_messages.py +20 -0
  25. karrio/server/manager/migrations/0021_tracking_estimated_delivery.py +18 -0
  26. karrio/server/manager/migrations/0022_auto_20211122_2100.py +53 -0
  27. karrio/server/manager/migrations/0023_auto_20211227_2141.py +118 -0
  28. karrio/server/manager/migrations/0024_alter_parcel_items.py +18 -0
  29. karrio/server/manager/migrations/0025_auto_20220113_1158.py +25 -0
  30. karrio/server/manager/migrations/0026_parcel_reference_number.py +18 -0
  31. karrio/server/manager/migrations/0027_custom_migration_2021_1.py +47 -0
  32. karrio/server/manager/migrations/0028_auto_20220303_1153.py +39 -0
  33. karrio/server/manager/migrations/0029_auto_20220303_1249.py +55 -0
  34. karrio/server/manager/migrations/0030_alter_shipment_status.py +44 -0
  35. karrio/server/manager/migrations/0031_shipment_invoice.py +34 -0
  36. karrio/server/manager/migrations/0032_custom_migration_2022_3.py +26 -0
  37. karrio/server/manager/migrations/0033_auto_20220504_1335.py +57 -0
  38. karrio/server/manager/migrations/0034_commodity_hs_code.py +18 -0
  39. karrio/server/manager/migrations/0035_parcel_options.py +26 -0
  40. karrio/server/manager/migrations/0036_alter_tracking_shipment.py +24 -0
  41. karrio/server/manager/migrations/0037_auto_20220710_1350.py +28 -0
  42. karrio/server/manager/migrations/0038_alter_tracking_status.py +18 -0
  43. karrio/server/manager/migrations/0039_documentuploadrecord.py +43 -0
  44. karrio/server/manager/migrations/0040_parcel_freight_class.py +18 -0
  45. karrio/server/manager/migrations/0041_alter_commodity_options_alter_parcel_options.py +29 -0
  46. karrio/server/manager/migrations/0042_remove_shipment_shipment_tracking_number_idx_and_more.py +658 -0
  47. karrio/server/manager/migrations/0043_customs_duty_billing_address_and_more.py +62 -0
  48. karrio/server/manager/migrations/0044_address_address_line1_temp_and_more.py +326 -0
  49. karrio/server/manager/migrations/0045_alter_customs_duty_billing_address_and_more.py +45 -0
  50. karrio/server/manager/migrations/0046_auto_20230114_0930.py +78 -0
  51. karrio/server/manager/migrations/0047_remove_shipment_shipment_tracking_number_idx_and_more.py +595 -0
  52. karrio/server/manager/migrations/0048_commodity_title_alter_commodity_description_and_more.py +53 -0
  53. karrio/server/manager/migrations/0049_auto_20230318_0708.py +39 -0
  54. karrio/server/manager/migrations/0050_address_street_number_tracking_account_number_and_more.py +60 -0
  55. karrio/server/manager/migrations/0051_auto_20230330_0556.py +56 -0
  56. karrio/server/manager/migrations/0052_auto_20230520_0811.py +35 -0
  57. karrio/server/manager/migrations/0053_alter_commodity_weight_unit_alter_parcel_weight_unit.py +32 -0
  58. karrio/server/manager/migrations/0054_alter_address_company_name_alter_address_person_name.py +22 -0
  59. karrio/server/manager/migrations/0055_alter_tracking_status.py +32 -0
  60. karrio/server/manager/migrations/0056_tracking_delivery_image_tracking_signature_image.py +22 -0
  61. karrio/server/manager/migrations/0057_alter_customs_invoice_date.py +18 -0
  62. karrio/server/manager/migrations/0058_manifest_shipment_manifest.py +124 -0
  63. karrio/server/manager/migrations/0059_shipment_return_address.py +24 -0
  64. karrio/server/manager/migrations/0060_pickup_meta_alter_address_country_code_and_more.py +527 -0
  65. karrio/server/manager/migrations/0061_alter_customs_incoterm.py +37 -0
  66. karrio/server/manager/migrations/0062_alter_tracking_status.py +35 -0
  67. karrio/server/manager/migrations/__init__.py +0 -0
  68. karrio/server/manager/models.py +984 -0
  69. karrio/server/manager/router.py +3 -0
  70. karrio/server/manager/serializers/__init__.py +50 -0
  71. karrio/server/manager/serializers/address.py +82 -0
  72. karrio/server/manager/serializers/commodity.py +51 -0
  73. karrio/server/manager/serializers/customs.py +84 -0
  74. karrio/server/manager/serializers/document.py +113 -0
  75. karrio/server/manager/serializers/manifest.py +85 -0
  76. karrio/server/manager/serializers/parcel.py +84 -0
  77. karrio/server/manager/serializers/pickup.py +285 -0
  78. karrio/server/manager/serializers/rate.py +19 -0
  79. karrio/server/manager/serializers/shipment.py +869 -0
  80. karrio/server/manager/serializers/tracking.py +250 -0
  81. karrio/server/manager/signals.py +70 -0
  82. karrio/server/manager/tests/__init__.py +10 -0
  83. karrio/server/manager/tests/test_addresses.py +110 -0
  84. karrio/server/manager/tests/test_custom_infos.py +97 -0
  85. karrio/server/manager/tests/test_parcels.py +104 -0
  86. karrio/server/manager/tests/test_pickups.py +345 -0
  87. karrio/server/manager/tests/test_shipments.py +833 -0
  88. karrio/server/manager/tests/test_trackers.py +215 -0
  89. karrio/server/manager/urls.py +10 -0
  90. karrio/server/manager/views/__init__.py +9 -0
  91. karrio/server/manager/views/addresses.py +154 -0
  92. karrio/server/manager/views/customs.py +159 -0
  93. karrio/server/manager/views/documents.py +131 -0
  94. karrio/server/manager/views/manifests.py +160 -0
  95. karrio/server/manager/views/parcels.py +155 -0
  96. karrio/server/manager/views/pickups.py +182 -0
  97. karrio/server/manager/views/shipments.py +335 -0
  98. karrio/server/manager/views/trackers.py +364 -0
  99. karrio_server_manager-2025.5rc1.dist-info/METADATA +28 -0
  100. karrio_server_manager-2025.5rc1.dist-info/RECORD +102 -0
  101. karrio_server_manager-2025.5rc1.dist-info/WHEEL +5 -0
  102. karrio_server_manager-2025.5rc1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,160 @@
1
+ import io
2
+ import base64
3
+ import logging
4
+ import django_downloadview
5
+ import django.urls as urls
6
+ import django.core.files.base as base
7
+ import rest_framework.status as status
8
+ import rest_framework.request as request
9
+ import rest_framework.response as response
10
+ import rest_framework.pagination as pagination
11
+ import rest_framework.throttling as throttling
12
+ import django_filters.rest_framework as django_filters
13
+
14
+ import karrio.server.openapi as openapi
15
+ import karrio.server.core.views.api as api
16
+ import karrio.server.core.filters as filters
17
+ import karrio.server.manager.models as models
18
+ import karrio.server.manager.router as router
19
+ import karrio.server.manager.serializers as serializers
20
+
21
+ ENDPOINT_ID = "$$$$&&" # This endpoint id is used to make operation ids unique make sure not to duplicate
22
+ logger = logging.getLogger(__name__)
23
+ Manifests = serializers.PaginatedResult("ManifestList", serializers.Manifest)
24
+
25
+
26
+ class ManifestList(api.GenericAPIView):
27
+ throttle_scope = "carrier_request"
28
+ pagination_class = type(
29
+ "CustomPagination", (pagination.LimitOffsetPagination,), dict(default_limit=20)
30
+ )
31
+ filter_backends = (django_filters.DjangoFilterBackend,)
32
+ filterset_class = filters.ManifestFilters
33
+ serializer_class = Manifests
34
+ model = models.Manifest
35
+
36
+ def get_throttles(self):
37
+ if self.request.method == "POST":
38
+ return [throttling.ScopedRateThrottle()]
39
+ return super().get_throttles()
40
+
41
+ @openapi.extend_schema(
42
+ tags=["Manifests"],
43
+ operation_id=f"{ENDPOINT_ID}list",
44
+ extensions={"x-operationId": "listManifests"},
45
+ summary="List manifests",
46
+ responses={
47
+ 200: Manifests(),
48
+ 404: serializers.ErrorResponse(),
49
+ 500: serializers.ErrorResponse(),
50
+ },
51
+ parameters=filters.ManifestFilters.parameters,
52
+ )
53
+ def get(self, request: request.Request):
54
+ """
55
+ Retrieve all manifests.
56
+ """
57
+ manifests = self.filter_queryset(self.get_queryset())
58
+ response = self.paginate_queryset(
59
+ serializers.Manifest(manifests, many=True).data
60
+ )
61
+ return self.get_paginated_response(response)
62
+
63
+ @openapi.extend_schema(
64
+ tags=["Manifests"],
65
+ operation_id=f"{ENDPOINT_ID}create",
66
+ extensions={"x-operationId": "createManifest"},
67
+ summary="Create a manifest",
68
+ responses={
69
+ 201: serializers.Manifest(),
70
+ 400: serializers.ErrorResponse(),
71
+ 424: serializers.ErrorMessages(),
72
+ 500: serializers.ErrorResponse(),
73
+ },
74
+ request=serializers.ManifestData(),
75
+ )
76
+ def post(self, request: request.HttpRequest):
77
+ """Create a manifest for one or many shipments with labels already purchased."""
78
+
79
+ manifest = (
80
+ serializers.ManifestSerializer.map(data=request.data, context=request)
81
+ .save()
82
+ .instance
83
+ )
84
+
85
+ return response.Response(
86
+ serializers.Manifest(manifest).data, status=status.HTTP_201_CREATED
87
+ )
88
+
89
+
90
+ class ManifestDetails(api.APIView):
91
+
92
+ @openapi.extend_schema(
93
+ tags=["Manifests"],
94
+ operation_id=f"{ENDPOINT_ID}retrieve",
95
+ extensions={"x-operationId": "retrieveManifest"},
96
+ summary="Retrieve a manifest",
97
+ responses={
98
+ 200: serializers.Manifest(),
99
+ 404: serializers.ErrorResponse(),
100
+ 500: serializers.ErrorResponse(),
101
+ },
102
+ )
103
+ def get(self, request: request.Request, pk: str):
104
+ """Retrieve a shipping manifest."""
105
+ manifest = models.Manifest.access_by(request).get(pk=pk)
106
+ return response.Response(serializers.Manifest(manifest).data)
107
+
108
+
109
+ class ManifestDoc(django_downloadview.VirtualDownloadView):
110
+ @openapi.extend_schema(exclude=True)
111
+ def get(
112
+ self,
113
+ request: request.Request,
114
+ pk: str,
115
+ doc: str = "manifest",
116
+ format: str = "pdf",
117
+ **kwargs,
118
+ ):
119
+ """Retrieve a manifest file."""
120
+ self.manifest = models.Manifest.objects.get(pk=pk, manifest__isnull=False)
121
+ self.document = getattr(self.manifest, doc, None)
122
+ self.name = f"{doc}_{self.manifest.id}.{format}"
123
+
124
+ query_params = request.GET.dict()
125
+ self.preview = "preview" in query_params
126
+ self.attachment = "download" in query_params
127
+
128
+ response = super(ManifestDoc, self).get(request, pk, doc, format, **kwargs)
129
+ response["X-Frame-Options"] = "ALLOWALL"
130
+ return response
131
+
132
+ def get_file(self):
133
+ content = base64.b64decode(self.document or "")
134
+ buffer = io.BytesIO()
135
+ buffer.write(content)
136
+
137
+ return base.ContentFile(buffer.getvalue(), name=self.name)
138
+
139
+
140
+ router.router.urls.append(
141
+ urls.path(
142
+ "manifests",
143
+ ManifestList.as_view(),
144
+ name="manifest-list",
145
+ )
146
+ )
147
+ router.router.urls.append(
148
+ urls.path(
149
+ "manifests/<str:pk>",
150
+ ManifestDetails.as_view(),
151
+ name="manifest-details",
152
+ )
153
+ )
154
+ router.router.urls.append(
155
+ urls.re_path(
156
+ r"^manifests/(?P<pk>\w+)/(?P<doc>[a-z0-9]+).(?P<format>[a-z0-9]+)",
157
+ ManifestDoc.as_view(),
158
+ name="manifest-docs",
159
+ )
160
+ )
@@ -0,0 +1,155 @@
1
+ import logging
2
+
3
+ from django.urls import path
4
+ from rest_framework import status
5
+ from rest_framework.request import Request
6
+ from rest_framework.response import Response
7
+ from rest_framework.pagination import LimitOffsetPagination
8
+
9
+ from karrio.server.manager.router import router
10
+ from karrio.server.core.views.api import GenericAPIView, APIView
11
+ from karrio.server.manager.serializers import (
12
+ PaginatedResult,
13
+ ErrorResponse,
14
+ ParcelData,
15
+ Parcel,
16
+ ParcelSerializer,
17
+ can_mutate_parcel,
18
+ )
19
+ import karrio.server.manager.models as models
20
+ import karrio.server.openapi as openapi
21
+
22
+ logger = logging.getLogger(__name__)
23
+ ENDPOINT_ID = "$$$" # This endpoint id is used to make operation ids unique make sure not to duplicate
24
+ Parcels = PaginatedResult("ParcelList", Parcel)
25
+
26
+
27
+ class ParcelList(GenericAPIView):
28
+ queryset = models.Parcel.objects
29
+ pagination_class = type(
30
+ "CustomPagination", (LimitOffsetPagination,), dict(default_limit=20)
31
+ )
32
+ serializer_class = Parcels
33
+
34
+ @openapi.extend_schema(
35
+ tags=["Parcels"],
36
+ operation_id=f"{ENDPOINT_ID}list",
37
+ extensions={"x-operationId": "listParcels"},
38
+ summary="List all parcels",
39
+ responses={
40
+ 200: Parcels(),
41
+ 404: ErrorResponse(),
42
+ 500: ErrorResponse(),
43
+ },
44
+ )
45
+ def get(self, request: Request):
46
+ """
47
+ Retrieve all stored parcels.
48
+ """
49
+ parcels = models.Parcel.access_by(request).filter(
50
+ **{
51
+ f"{prop}__isnull": True
52
+ for prop in models.Parcel.HIDDEN_PROPS
53
+ if prop != "org"
54
+ }
55
+ )
56
+ serializer = Parcel(parcels, many=True)
57
+ response = self.paginate_queryset(serializer.data)
58
+
59
+ return self.get_paginated_response(response)
60
+
61
+ @openapi.extend_schema(
62
+ tags=["Parcels"],
63
+ operation_id=f"{ENDPOINT_ID}create",
64
+ extensions={"x-operationId": "createParcel"},
65
+ summary="Create a parcel",
66
+ request=ParcelData(),
67
+ responses={
68
+ 201: Parcel(),
69
+ 400: ErrorResponse(),
70
+ 500: ErrorResponse(),
71
+ },
72
+ )
73
+ def post(self, request: Request):
74
+ """
75
+ Create a new parcel.
76
+ """
77
+ parcel = (
78
+ ParcelSerializer.map(data=request.data, context=request).save().instance
79
+ )
80
+ return Response(Parcel(parcel).data, status=status.HTTP_201_CREATED)
81
+
82
+
83
+ class ParcelDetail(APIView):
84
+
85
+ @openapi.extend_schema(
86
+ tags=["Parcels"],
87
+ operation_id=f"{ENDPOINT_ID}retrieve",
88
+ extensions={"x-operationId": "retrieveParcel"},
89
+ summary="Retrieve a parcel",
90
+ responses={
91
+ 200: Parcel(),
92
+ 404: ErrorResponse(),
93
+ 500: ErrorResponse(),
94
+ },
95
+ )
96
+ def get(self, request: Request, pk: str):
97
+ """
98
+ Retrieve a parcel.
99
+ """
100
+ address = models.Parcel.access_by(request).get(pk=pk)
101
+ return Response(Parcel(address).data)
102
+
103
+ @openapi.extend_schema(
104
+ tags=["Parcels"],
105
+ operation_id=f"{ENDPOINT_ID}update",
106
+ extensions={"x-operationId": "updateParcel"},
107
+ summary="Update a parcel",
108
+ request=ParcelData(),
109
+ responses={
110
+ 200: Parcel(),
111
+ 400: ErrorResponse(),
112
+ 404: ErrorResponse(),
113
+ 409: ErrorResponse(),
114
+ 500: ErrorResponse(),
115
+ },
116
+ )
117
+ def patch(self, request: Request, pk: str):
118
+ """
119
+ modify an existing parcel's details.
120
+ """
121
+ parcel = models.Parcel.access_by(request).get(pk=pk)
122
+ can_mutate_parcel(parcel, update=True)
123
+
124
+ ParcelSerializer.map(parcel, data=request.data).save()
125
+
126
+ return Response(Parcel(parcel).data)
127
+
128
+ @openapi.extend_schema(
129
+ tags=["Parcels"],
130
+ operation_id=f"{ENDPOINT_ID}discard",
131
+ extensions={"x-operationId": "discardParcel"},
132
+ summary="Remove a parcel",
133
+ responses={
134
+ 200: Parcel(),
135
+ 404: ErrorResponse(),
136
+ 409: ErrorResponse(),
137
+ 500: ErrorResponse(),
138
+ },
139
+ )
140
+ def delete(self, request: Request, pk: str):
141
+ """
142
+ Remove a parcel.
143
+ """
144
+ parcel = models.Parcel.access_by(request).get(pk=pk)
145
+ can_mutate_parcel(parcel, update=True, delete=True)
146
+
147
+ parcel.delete(keep_parents=True)
148
+
149
+ return Response(Parcel(parcel).data)
150
+
151
+
152
+ router.urls.append(path("parcels", ParcelList.as_view(), name="parcel-list"))
153
+ router.urls.append(
154
+ path("parcels/<str:pk>", ParcelDetail.as_view(), name="parcel-details")
155
+ )
@@ -0,0 +1,182 @@
1
+ import logging
2
+
3
+ from django.urls import path
4
+ from rest_framework import status
5
+ from rest_framework.pagination import LimitOffsetPagination
6
+ from rest_framework.response import Response
7
+ from rest_framework.request import Request
8
+ from django_filters.rest_framework import DjangoFilterBackend
9
+
10
+ from karrio.server.core.views.api import GenericAPIView, APIView
11
+ from karrio.server.core.filters import PickupFilters
12
+ from karrio.server.manager.router import router
13
+ from karrio.server.manager.serializers import (
14
+ PaginatedResult,
15
+ Pickup,
16
+ ErrorResponse,
17
+ ErrorMessages,
18
+ PickupData,
19
+ PickupUpdateData,
20
+ PickupCancelData,
21
+ )
22
+ import karrio.server.manager.models as models
23
+ import karrio.server.openapi as openapi
24
+
25
+ ENDPOINT_ID = "$$$$" # This endpoint id is used to make operation ids unique make sure not to duplicate
26
+ logger = logging.getLogger(__name__)
27
+ Pickups = PaginatedResult("PickupList", Pickup)
28
+
29
+
30
+ class PickupList(GenericAPIView):
31
+ pagination_class = type(
32
+ "CustomPagination", (LimitOffsetPagination,), dict(default_limit=20)
33
+ )
34
+ filter_backends = (DjangoFilterBackend,)
35
+ filterset_class = PickupFilters
36
+ serializer_class = Pickups
37
+ model = models.Pickup
38
+
39
+ @openapi.extend_schema(
40
+ tags=["Pickups"],
41
+ operation_id=f"{ENDPOINT_ID}list",
42
+ extensions={"x-operationId": "listPickups"},
43
+ summary="List shipment pickups",
44
+ responses={
45
+ 200: Pickups(),
46
+ 404: ErrorResponse(),
47
+ 500: ErrorResponse(),
48
+ },
49
+ parameters=PickupFilters.parameters,
50
+ )
51
+ def get(self, request: Request):
52
+ """
53
+ Retrieve all scheduled pickups.
54
+ """
55
+ pickups = self.filter_queryset(self.get_queryset())
56
+ response = self.paginate_queryset(Pickup(pickups, many=True).data)
57
+ return self.get_paginated_response(response)
58
+
59
+
60
+ class PickupRequest(APIView):
61
+ throttle_scope = "carrier_request"
62
+
63
+ @openapi.extend_schema(
64
+ tags=["Pickups"],
65
+ operation_id=f"{ENDPOINT_ID}schedule",
66
+ extensions={"x-operationId": "schedulePickup"},
67
+ summary="Schedule a pickup",
68
+ responses={
69
+ 201: Pickup(),
70
+ 400: ErrorResponse(),
71
+ 424: ErrorMessages(),
72
+ 500: ErrorResponse(),
73
+ },
74
+ request=PickupData(),
75
+ )
76
+ def post(self, request: Request, carrier_name: str):
77
+ """
78
+ Schedule a pickup for one or many shipments with labels already purchased.
79
+ """
80
+ carrier_filter = {
81
+ "carrier_name": carrier_name,
82
+ }
83
+
84
+ pickup = (
85
+ PickupData.map(data=request.data, context=request)
86
+ .save(carrier_filter=carrier_filter)
87
+ .instance
88
+ )
89
+
90
+ return Response(Pickup(pickup).data, status=status.HTTP_201_CREATED)
91
+
92
+
93
+ class PickupDetails(APIView):
94
+ throttle_scope = "carrier_request"
95
+
96
+ @openapi.extend_schema(
97
+ tags=["Pickups"],
98
+ operation_id=f"{ENDPOINT_ID}retrieve",
99
+ extensions={"x-operationId": "retrievePickup"},
100
+ summary="Retrieve a pickup",
101
+ responses={
102
+ 200: Pickup(),
103
+ 404: ErrorResponse(),
104
+ 500: ErrorResponse(),
105
+ },
106
+ )
107
+ def get(self, request: Request, pk: str):
108
+ """Retrieve a scheduled pickup."""
109
+ pickup = models.Pickup.access_by(request).get(pk=pk)
110
+ return Response(Pickup(pickup).data)
111
+
112
+ @openapi.extend_schema(
113
+ tags=["Pickups"],
114
+ operation_id=f"{ENDPOINT_ID}update",
115
+ extensions={"x-operationId": "updatePickup"},
116
+ summary="Update a pickup",
117
+ responses={
118
+ 200: Pickup(),
119
+ 404: ErrorResponse(),
120
+ 400: ErrorResponse(),
121
+ 424: ErrorMessages(),
122
+ 500: ErrorResponse(),
123
+ },
124
+ request=PickupUpdateData(),
125
+ )
126
+ def post(self, request: Request, pk: str):
127
+ """
128
+ Modify a pickup for one or many shipments with labels already purchased.
129
+ """
130
+ pickup = models.Pickup.access_by(request).get(pk=pk)
131
+ instance = (
132
+ PickupUpdateData.map(pickup, data=request.data, context=request)
133
+ .save()
134
+ .instance
135
+ )
136
+
137
+ return Response(Pickup(instance).data, status=status.HTTP_200_OK)
138
+
139
+
140
+ class PickupCancel(APIView):
141
+
142
+ @openapi.extend_schema(
143
+ tags=["Pickups"],
144
+ operation_id=f"{ENDPOINT_ID}cancel",
145
+ extensions={"x-operationId": "cancelPickup"},
146
+ summary="Cancel a pickup",
147
+ responses={
148
+ 200: Pickup(),
149
+ 404: ErrorResponse(),
150
+ 409: ErrorResponse(),
151
+ 424: ErrorMessages(),
152
+ 500: ErrorResponse(),
153
+ },
154
+ request=PickupCancelData(),
155
+ )
156
+ def post(self, request: Request, pk: str):
157
+ """
158
+ Cancel a pickup of one or more shipments.
159
+ """
160
+ pickup = models.Pickup.access_by(request).get(pk=pk)
161
+
162
+ update = PickupCancelData.map(pickup, data=request.data).save().instance
163
+
164
+ return Response(Pickup(update).data)
165
+
166
+
167
+ router.urls.append(path("pickups", PickupList.as_view(), name="shipment-pickup-list"))
168
+ router.urls.append(
169
+ path("pickups/<str:pk>", PickupDetails.as_view(), name="shipment-pickup-details")
170
+ )
171
+ router.urls.append(
172
+ path(
173
+ "pickups/<str:pk>/cancel", PickupCancel.as_view(), name="shipment-pickup-cancel"
174
+ )
175
+ )
176
+ router.urls.append(
177
+ path(
178
+ "pickups/<str:carrier_name>/schedule",
179
+ PickupRequest.as_view(),
180
+ name="shipment-pickup-request",
181
+ )
182
+ )