karrio-server-manager 2026.1.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.
Files changed (43) hide show
  1. karrio/server/manager/migrations/0070_add_meta_and_product_fields.py +98 -0
  2. karrio/server/manager/migrations/0071_product_proxy.py +25 -0
  3. karrio/server/manager/migrations/0072_populate_json_fields.py +267 -0
  4. karrio/server/manager/migrations/0073_make_shipment_fk_nullable.py +36 -0
  5. karrio/server/manager/migrations/0074_clean_model_refactoring.py +207 -0
  6. karrio/server/manager/migrations/0075_populate_template_meta.py +69 -0
  7. karrio/server/manager/migrations/0076_remove_customs_model.py +66 -0
  8. karrio/server/manager/migrations/0077_add_carrier_snapshot_fields.py +83 -0
  9. karrio/server/manager/migrations/0078_populate_carrier_snapshots.py +112 -0
  10. karrio/server/manager/migrations/0079_remove_carrier_fk_fields.py +56 -0
  11. karrio/server/manager/migrations/0080_add_carrier_json_indexes.py +137 -0
  12. karrio/server/manager/migrations/0081_cleanup.py +62 -0
  13. karrio/server/manager/migrations/0082_shipment_fees.py +26 -0
  14. karrio/server/manager/models.py +421 -321
  15. karrio/server/manager/serializers/__init__.py +5 -4
  16. karrio/server/manager/serializers/address.py +8 -2
  17. karrio/server/manager/serializers/commodity.py +11 -4
  18. karrio/server/manager/serializers/document.py +29 -15
  19. karrio/server/manager/serializers/manifest.py +6 -3
  20. karrio/server/manager/serializers/parcel.py +5 -2
  21. karrio/server/manager/serializers/pickup.py +194 -67
  22. karrio/server/manager/serializers/shipment.py +226 -171
  23. karrio/server/manager/serializers/tracking.py +45 -12
  24. karrio/server/manager/tests/__init__.py +0 -1
  25. karrio/server/manager/tests/test_addresses.py +53 -0
  26. karrio/server/manager/tests/test_parcels.py +50 -0
  27. karrio/server/manager/tests/test_pickups.py +286 -50
  28. karrio/server/manager/tests/test_products.py +597 -0
  29. karrio/server/manager/tests/test_shipments.py +237 -92
  30. karrio/server/manager/tests/test_trackers.py +4 -3
  31. karrio/server/manager/views/__init__.py +1 -1
  32. karrio/server/manager/views/addresses.py +38 -2
  33. karrio/server/manager/views/documents.py +1 -1
  34. karrio/server/manager/views/parcels.py +25 -2
  35. karrio/server/manager/views/products.py +239 -0
  36. karrio/server/manager/views/trackers.py +69 -1
  37. {karrio_server_manager-2026.1.1.dist-info → karrio_server_manager-2026.1.3.dist-info}/METADATA +1 -1
  38. {karrio_server_manager-2026.1.1.dist-info → karrio_server_manager-2026.1.3.dist-info}/RECORD +40 -28
  39. {karrio_server_manager-2026.1.1.dist-info → karrio_server_manager-2026.1.3.dist-info}/WHEEL +1 -1
  40. karrio/server/manager/serializers/customs.py +0 -84
  41. karrio/server/manager/tests/test_custom_infos.py +0 -101
  42. karrio/server/manager/views/customs.py +0 -159
  43. {karrio_server_manager-2026.1.1.dist-info → karrio_server_manager-2026.1.3.dist-info}/top_level.txt +0 -0
@@ -1,101 +0,0 @@
1
- import json
2
- from unittest.mock import ANY
3
- from django.urls import reverse
4
- from rest_framework import status
5
- from karrio.server.core.tests import APITestCase
6
- from karrio.server.manager.models import Customs
7
-
8
-
9
- class TestCustomsInfo(APITestCase):
10
- def test_create_customs(self):
11
- url = reverse("karrio.server.manager:customs-list")
12
- data = CUSTOMS_DATA
13
-
14
- response = self.client.post(url, data)
15
- response_data = json.loads(response.content)
16
-
17
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
18
- self.assertDictEqual(response_data, CUSTOMS_RESPONSE)
19
-
20
-
21
- class TestCustomsInfoDetails(APITestCase):
22
- def setUp(self) -> None:
23
- super().setUp()
24
- self.customs: Customs = Customs.objects.create(**{"created_by": self.user})
25
-
26
- def test_update_customs(self):
27
- url = reverse(
28
- "karrio.server.manager:customs-details", kwargs=dict(pk=self.customs.pk)
29
- )
30
- data = CUSTOMS_UPDATE_DATA
31
-
32
- response = self.client.patch(url, data)
33
- response_data = json.loads(response.content)
34
-
35
- self.assertEqual(response.status_code, status.HTTP_200_OK)
36
- self.assertDictEqual(response_data, CUSTOMS_UPDATE_RESPONSE)
37
-
38
-
39
- CUSTOMS_DATA = {
40
- "incoterm": "DDU",
41
- "commodities": [
42
- {"title": "cn", "weight": 4.0, "weight_unit": "KG", "sku": "cc"},
43
- ],
44
- }
45
-
46
- CUSTOMS_RESPONSE = {
47
- "certify": None,
48
- "commercial_invoice": None,
49
- "commodities": [
50
- {
51
- "description": None,
52
- "title": "cn",
53
- "id": ANY,
54
- "object_type": "commodity",
55
- "origin_country": None,
56
- "parent_id": None,
57
- "product_id": None,
58
- "product_url": None,
59
- "image_url": None,
60
- "variant_id": None,
61
- "quantity": 1,
62
- "metadata": {},
63
- "sku": "cc",
64
- "hs_code": None,
65
- "value_amount": None,
66
- "value_currency": None,
67
- "weight": 4.0,
68
- "weight_unit": "KG",
69
- }
70
- ],
71
- "content_description": None,
72
- "content_type": None,
73
- "duty": None,
74
- "duty_billing_address": None,
75
- "id": ANY,
76
- "object_type": "customs_info",
77
- "incoterm": "DDU",
78
- "invoice": None,
79
- "invoice_date": None,
80
- "options": {},
81
- "signer": None,
82
- }
83
-
84
- CUSTOMS_UPDATE_DATA = {"incoterm": "DDP"}
85
-
86
- CUSTOMS_UPDATE_RESPONSE = {
87
- "certify": None,
88
- "commercial_invoice": None,
89
- "commodities": [],
90
- "content_description": None,
91
- "content_type": None,
92
- "duty": None,
93
- "duty_billing_address": None,
94
- "id": ANY,
95
- "object_type": "customs_info",
96
- "incoterm": "DDP",
97
- "invoice": None,
98
- "invoice_date": None,
99
- "options": {},
100
- "signer": None,
101
- }
@@ -1,159 +0,0 @@
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.core.views.api import GenericAPIView, APIView
10
- from karrio.server.manager.serializers import (
11
- PaginatedResult,
12
- ErrorResponse,
13
- CustomsData,
14
- Customs,
15
- CustomsSerializer,
16
- can_mutate_customs,
17
- )
18
- from karrio.server.manager.router import router
19
- import karrio.server.manager.models as models
20
- import karrio.server.openapi as openapi
21
-
22
- ENDPOINT_ID = "$$" # This endpoint id is used to make operation ids unique make sure not to duplicate
23
- logger = logging.getLogger(__name__)
24
- CustomsInfoList = PaginatedResult("CustomsList", Customs)
25
-
26
-
27
- class CustomsList(GenericAPIView):
28
- queryset = models.Customs.objects
29
- pagination_class = type(
30
- "CustomPagination", (LimitOffsetPagination,), dict(default_limit=20)
31
- )
32
- serializer_class = CustomsInfoList
33
-
34
- @openapi.extend_schema(
35
- exclude=True,
36
- tags=["Customs"],
37
- operation_id=f"{ENDPOINT_ID}list",
38
- extensions={"x-operationId": "listCustomsInfo"},
39
- summary="List all customs info",
40
- responses={
41
- 200: CustomsInfoList(),
42
- 404: ErrorResponse(),
43
- 500: ErrorResponse(),
44
- },
45
- )
46
- def get(self, request: Request):
47
- """
48
- Retrieve all stored customs declarations.
49
- """
50
- customs_info = models.Customs.access_by(request).filter(
51
- **{
52
- f"{prop}__isnull": True
53
- for prop in models.Customs.HIDDEN_PROPS
54
- if prop != "org"
55
- }
56
- )
57
- serializer = Customs(customs_info, many=True)
58
- response = self.paginate_queryset(serializer.data)
59
- return self.get_paginated_response(response)
60
-
61
- @openapi.extend_schema(
62
- exclude=True,
63
- tags=["Customs"],
64
- operation_id=f"{ENDPOINT_ID}create",
65
- extensions={"x-operationId": "createCustomsInfo"},
66
- summary="Create a customs info",
67
- request=CustomsData(),
68
- responses={
69
- 201: Customs(),
70
- 400: ErrorResponse(),
71
- 500: ErrorResponse(),
72
- },
73
- )
74
- def post(self, request: Request):
75
- """
76
- Create a new customs declaration.
77
- """
78
- customs = (
79
- CustomsSerializer.map(data=request.data, context=request).save().instance
80
- )
81
- return Response(Customs(customs).data, status=status.HTTP_201_CREATED)
82
-
83
-
84
- class CustomsDetail(APIView):
85
-
86
- @openapi.extend_schema(
87
- exclude=True,
88
- tags=["Customs"],
89
- operation_id=f"{ENDPOINT_ID}retrieve",
90
- extensions={"x-operationId": "retrieveCustomsInfo"},
91
- summary="Retrieve a customs info",
92
- responses={
93
- 200: Customs(),
94
- 404: ErrorResponse(),
95
- 500: ErrorResponse(),
96
- },
97
- )
98
- def get(self, request: Request, pk: str):
99
- """
100
- Retrieve customs declaration.
101
- """
102
- address = models.Customs.access_by(request).get(pk=pk)
103
- return Response(Customs(address).data)
104
-
105
- @openapi.extend_schema(
106
- exclude=True,
107
- tags=["Customs"],
108
- operation_id=f"{ENDPOINT_ID}update",
109
- extensions={"x-operationId": "updateCustomsInfo"},
110
- summary="Update a customs info",
111
- request=CustomsData(),
112
- responses={
113
- 200: Customs(),
114
- 400: ErrorResponse(),
115
- 404: ErrorResponse(),
116
- 409: ErrorResponse(),
117
- 500: ErrorResponse(),
118
- },
119
- )
120
- def patch(self, request: Request, pk: str):
121
- """
122
- modify an existing customs declaration.
123
- """
124
- customs = models.Customs.access_by(request).get(pk=pk)
125
- can_mutate_customs(customs)
126
-
127
- CustomsSerializer.map(customs, data=request.data, context=request).save()
128
-
129
- return Response(Customs(customs).data)
130
-
131
- @openapi.extend_schema(
132
- exclude=True,
133
- tags=["Customs"],
134
- operation_id=f"{ENDPOINT_ID}discard",
135
- extensions={"x-operationId": "discardCustomsInfo"},
136
- summary="Discard a customs info",
137
- responses={
138
- 200: Customs(),
139
- 404: ErrorResponse(),
140
- 409: ErrorResponse(),
141
- 500: ErrorResponse(),
142
- },
143
- )
144
- def delete(self, request: Request, pk: str):
145
- """
146
- Discard a customs declaration.
147
- """
148
- customs = models.Customs.access_by(request).get(pk=pk)
149
- can_mutate_customs(customs)
150
-
151
- customs.delete(keep_parents=True)
152
-
153
- return Response(Customs(customs).data)
154
-
155
-
156
- router.urls.append(path("customs_info", CustomsList.as_view(), name="customs-list"))
157
- router.urls.append(
158
- path("customs_info/<str:pk>", CustomsDetail.as_view(), name="customs-details")
159
- )