karrio-server-documents 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.
Potentially problematic release.
This version of karrio-server-documents might be problematic. Click here for more details.
- karrio/server/documents/__init__.py +0 -0
- karrio/server/documents/admin.py +93 -0
- karrio/server/documents/apps.py +13 -0
- karrio/server/documents/filters.py +14 -0
- karrio/server/documents/generator.py +240 -0
- karrio/server/documents/migrations/0001_initial.py +69 -0
- karrio/server/documents/migrations/0002_alter_documenttemplate_related_objects.py +18 -0
- karrio/server/documents/migrations/0003_rename_related_objects_documenttemplate_related_object.py +18 -0
- karrio/server/documents/migrations/0004_documenttemplate_active.py +18 -0
- karrio/server/documents/migrations/0005_alter_documenttemplate_description_and_more.py +23 -0
- karrio/server/documents/migrations/0006_documenttemplate_metadata.py +25 -0
- karrio/server/documents/migrations/0007_alter_documenttemplate_related_object.py +18 -0
- karrio/server/documents/migrations/0008_documenttemplate_options.py +26 -0
- karrio/server/documents/migrations/__init__.py +0 -0
- karrio/server/documents/models.py +61 -0
- karrio/server/documents/serializers/__init__.py +4 -0
- karrio/server/documents/serializers/base.py +89 -0
- karrio/server/documents/serializers/documents.py +9 -0
- karrio/server/documents/signals.py +31 -0
- karrio/server/documents/tests/__init__.py +6 -0
- karrio/server/documents/tests/test_generator.py +455 -0
- karrio/server/documents/tests/test_templates.py +318 -0
- karrio/server/documents/urls.py +11 -0
- karrio/server/documents/utils.py +2486 -0
- karrio/server/documents/views/__init__.py +0 -0
- karrio/server/documents/views/printers.py +233 -0
- karrio/server/documents/views/templates.py +216 -0
- karrio/server/graph/schemas/__init__.py +1 -0
- karrio/server/graph/schemas/documents/__init__.py +46 -0
- karrio/server/graph/schemas/documents/inputs.py +38 -0
- karrio/server/graph/schemas/documents/mutations.py +51 -0
- karrio/server/graph/schemas/documents/types.py +43 -0
- karrio_server_documents-2025.5rc1.dist-info/METADATA +19 -0
- karrio_server_documents-2025.5rc1.dist-info/RECORD +36 -0
- karrio_server_documents-2025.5rc1.dist-info/WHEEL +5 -0
- karrio_server_documents-2025.5rc1.dist-info/top_level.txt +2 -0
|
File without changes
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import sys
|
|
3
|
+
import base64
|
|
4
|
+
import logging
|
|
5
|
+
from django.urls import re_path
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
from django.http import JsonResponse
|
|
8
|
+
from django.core.files.base import ContentFile
|
|
9
|
+
from django_downloadview import VirtualDownloadView
|
|
10
|
+
from rest_framework import status
|
|
11
|
+
|
|
12
|
+
import karrio.lib as lib
|
|
13
|
+
import karrio.server.openapi as openapi
|
|
14
|
+
import karrio.server.documents.models as models
|
|
15
|
+
import karrio.server.documents.generator as generator
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TemplateDocsPrinter(VirtualDownloadView):
|
|
21
|
+
def get(
|
|
22
|
+
self,
|
|
23
|
+
request,
|
|
24
|
+
pk: str,
|
|
25
|
+
slug: str,
|
|
26
|
+
**kwargs,
|
|
27
|
+
):
|
|
28
|
+
try:
|
|
29
|
+
"""Generate a document."""
|
|
30
|
+
template = models.DocumentTemplate.objects.get(pk=pk, slug=slug)
|
|
31
|
+
query_params = request.GET.dict()
|
|
32
|
+
|
|
33
|
+
self.document = generator.Documents.generate_template(
|
|
34
|
+
template, query_params, context=request
|
|
35
|
+
)
|
|
36
|
+
self.name = f"{slug}.pdf"
|
|
37
|
+
self.attachment = "download" in query_params
|
|
38
|
+
|
|
39
|
+
response = super(TemplateDocsPrinter, self).get(request, pk, slug, **kwargs)
|
|
40
|
+
response["X-Frame-Options"] = "ALLOWALL"
|
|
41
|
+
return response
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.exception(e)
|
|
44
|
+
_, __, exc_traceback = sys.exc_info()
|
|
45
|
+
trace = exc_traceback
|
|
46
|
+
while True:
|
|
47
|
+
trace = trace.tb_next
|
|
48
|
+
if "<template>" in str(trace.tb_frame) or not trace.tb_next:
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
return JsonResponse(
|
|
52
|
+
dict(error=str(e), line=getattr(trace, "tb_lineno", None)),
|
|
53
|
+
status=status.HTTP_409_CONFLICT,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def get_file(self):
|
|
57
|
+
return ContentFile(self.document.getvalue(), name=self.name)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ShipmentDocsPrinter(VirtualDownloadView):
|
|
61
|
+
@openapi.extend_schema(exclude=True)
|
|
62
|
+
def get(
|
|
63
|
+
self,
|
|
64
|
+
request,
|
|
65
|
+
doc: str = "label",
|
|
66
|
+
format: str = "pdf",
|
|
67
|
+
**kwargs,
|
|
68
|
+
):
|
|
69
|
+
"""Retrieve a shipment label."""
|
|
70
|
+
from karrio.server.manager.models import Shipment
|
|
71
|
+
|
|
72
|
+
if doc not in ["label", "invoice"]:
|
|
73
|
+
return JsonResponse(
|
|
74
|
+
dict(error=f"Invalid document type: {doc}"),
|
|
75
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
query_params = request.GET.dict()
|
|
79
|
+
self.attachment = "download" in query_params
|
|
80
|
+
ids = query_params.get("shipments", "").split(",")
|
|
81
|
+
|
|
82
|
+
self.format = (format or "").lower()
|
|
83
|
+
self.name = f"{doc}s - {timezone.now()}.{self.format}"
|
|
84
|
+
_queryset = Shipment.objects.filter(id__in=ids)
|
|
85
|
+
|
|
86
|
+
if doc == "label":
|
|
87
|
+
_queryset = _queryset.filter(
|
|
88
|
+
label__isnull=False,
|
|
89
|
+
label_type__contains=self.format.upper(),
|
|
90
|
+
)
|
|
91
|
+
if doc == "invoice":
|
|
92
|
+
_queryset = _queryset.filter(invoice__isnull=False)
|
|
93
|
+
|
|
94
|
+
self.documents = _queryset.values_list(doc, "label_type")
|
|
95
|
+
|
|
96
|
+
response = super(ShipmentDocsPrinter, self).get(
|
|
97
|
+
request, doc, self.format, **kwargs
|
|
98
|
+
)
|
|
99
|
+
response["X-Frame-Options"] = "ALLOWALL"
|
|
100
|
+
return response
|
|
101
|
+
|
|
102
|
+
def get_file(self):
|
|
103
|
+
content = base64.b64decode(
|
|
104
|
+
lib.bundle_base64([doc for doc, _ in self.documents], self.format.upper())
|
|
105
|
+
)
|
|
106
|
+
buffer = io.BytesIO()
|
|
107
|
+
buffer.write(content)
|
|
108
|
+
|
|
109
|
+
return ContentFile(buffer.getvalue(), name=self.name)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class OrderDocsPrinter(VirtualDownloadView):
|
|
113
|
+
@openapi.extend_schema(exclude=True)
|
|
114
|
+
def get(
|
|
115
|
+
self,
|
|
116
|
+
request,
|
|
117
|
+
doc: str = "label",
|
|
118
|
+
format: str = "pdf",
|
|
119
|
+
**kwargs,
|
|
120
|
+
):
|
|
121
|
+
"""Retrieve a shipment label."""
|
|
122
|
+
from karrio.server.orders.models import Order
|
|
123
|
+
|
|
124
|
+
if doc not in ["label", "invoice"]:
|
|
125
|
+
return JsonResponse(
|
|
126
|
+
dict(error=f"Invalid document type: {doc}"),
|
|
127
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
query_params = request.GET.dict()
|
|
131
|
+
self.attachment = "download" in query_params
|
|
132
|
+
ids = query_params.get("orders", "").split(",")
|
|
133
|
+
|
|
134
|
+
self.format = (format or "").lower()
|
|
135
|
+
self.name = f"{doc}s - {timezone.now()}.{self.format}"
|
|
136
|
+
_queryset = Order.objects.filter(
|
|
137
|
+
id__in=ids, shipments__id__isnull=False
|
|
138
|
+
).distinct()
|
|
139
|
+
|
|
140
|
+
if doc == "label":
|
|
141
|
+
_queryset = _queryset.filter(
|
|
142
|
+
shipments__label__isnull=False,
|
|
143
|
+
shipments__label_type__contains=self.format.upper(),
|
|
144
|
+
)
|
|
145
|
+
if doc == "invoice":
|
|
146
|
+
_queryset = _queryset.filter(shipments__invoice__isnull=False)
|
|
147
|
+
|
|
148
|
+
self.documents = list(
|
|
149
|
+
set(_queryset.values_list(f"shipments__{doc}", "shipments__label_type"))
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
response = super(OrderDocsPrinter, self).get(
|
|
153
|
+
request, doc, self.format, **kwargs
|
|
154
|
+
)
|
|
155
|
+
response["X-Frame-Options"] = "ALLOWALL"
|
|
156
|
+
return response
|
|
157
|
+
|
|
158
|
+
def get_file(self):
|
|
159
|
+
content = base64.b64decode(
|
|
160
|
+
lib.bundle_base64([doc for doc, _ in self.documents], self.format.upper())
|
|
161
|
+
)
|
|
162
|
+
buffer = io.BytesIO()
|
|
163
|
+
buffer.write(content)
|
|
164
|
+
|
|
165
|
+
return ContentFile(buffer.getvalue(), name=self.name)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class ManifestDocsPrinter(VirtualDownloadView):
|
|
169
|
+
@openapi.extend_schema(exclude=True)
|
|
170
|
+
def get(
|
|
171
|
+
self,
|
|
172
|
+
request,
|
|
173
|
+
doc: str = "manifest",
|
|
174
|
+
format: str = "pdf",
|
|
175
|
+
**kwargs,
|
|
176
|
+
):
|
|
177
|
+
"""Retrieve a shipment label."""
|
|
178
|
+
from karrio.server.manager.models import Manifest
|
|
179
|
+
|
|
180
|
+
if doc not in ["manifest"]:
|
|
181
|
+
return JsonResponse(
|
|
182
|
+
dict(error=f"Invalid document type: {doc}"),
|
|
183
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
query_params = request.GET.dict()
|
|
187
|
+
self.attachment = "download" in query_params
|
|
188
|
+
ids = query_params.get("manifests", "").split(",")
|
|
189
|
+
|
|
190
|
+
self.format = (format or "").lower()
|
|
191
|
+
self.name = f"{doc}s - {timezone.now()}.{self.format}"
|
|
192
|
+
queryset = Manifest.objects.filter(id__in=ids, manifest__isnull=False)
|
|
193
|
+
|
|
194
|
+
self.documents = queryset.values_list(doc, "reference")
|
|
195
|
+
|
|
196
|
+
response = super(ManifestDocsPrinter, self).get(
|
|
197
|
+
request, doc, self.format, **kwargs
|
|
198
|
+
)
|
|
199
|
+
response["X-Frame-Options"] = "ALLOWALL"
|
|
200
|
+
return response
|
|
201
|
+
|
|
202
|
+
def get_file(self):
|
|
203
|
+
content = base64.b64decode(
|
|
204
|
+
lib.bundle_base64([doc for doc, _ in self.documents], self.format.upper())
|
|
205
|
+
)
|
|
206
|
+
buffer = io.BytesIO()
|
|
207
|
+
buffer.write(content)
|
|
208
|
+
|
|
209
|
+
return ContentFile(buffer.getvalue(), name=self.name)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
urlpatterns = [
|
|
213
|
+
re_path(
|
|
214
|
+
r"^documents/templates/(?P<pk>\w+).(?P<slug>\w+)",
|
|
215
|
+
TemplateDocsPrinter.as_view(),
|
|
216
|
+
name="templates-documents-print",
|
|
217
|
+
),
|
|
218
|
+
re_path(
|
|
219
|
+
r"^documents/shipments/(?P<doc>[a-z0-9]+).(?P<format>[a-zA-Z0-9]+)",
|
|
220
|
+
ShipmentDocsPrinter.as_view(),
|
|
221
|
+
name="shipments-documents-print",
|
|
222
|
+
),
|
|
223
|
+
re_path(
|
|
224
|
+
r"^documents/orders/(?P<doc>[a-z0-9]+).(?P<format>[a-zA-Z0-9]+)",
|
|
225
|
+
OrderDocsPrinter.as_view(),
|
|
226
|
+
name="orders-documents-print",
|
|
227
|
+
),
|
|
228
|
+
re_path(
|
|
229
|
+
r"^documents/manifests/(?P<doc>[a-z0-9]+).(?P<format>[a-zA-Z0-9]+)",
|
|
230
|
+
ManifestDocsPrinter.as_view(),
|
|
231
|
+
name="manifests-documents-print",
|
|
232
|
+
),
|
|
233
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import logging
|
|
3
|
+
import django.urls as urls
|
|
4
|
+
from rest_framework import status
|
|
5
|
+
from rest_framework.request import Request
|
|
6
|
+
from rest_framework.response import Response
|
|
7
|
+
import rest_framework.pagination as pagination
|
|
8
|
+
|
|
9
|
+
import karrio.lib as lib
|
|
10
|
+
import karrio.server.openapi as openapi
|
|
11
|
+
import karrio.server.core.views.api as api
|
|
12
|
+
import karrio.server.documents.models as models
|
|
13
|
+
import karrio.server.documents.generator as generator
|
|
14
|
+
import karrio.server.documents.serializers as serializers
|
|
15
|
+
|
|
16
|
+
ENDPOINT_ID = "&&&&$$" # This endpoint id is used to make operation ids unique make sure not to duplicate
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
DocumentTemplates = serializers.PaginatedResult(
|
|
19
|
+
"DocumentTemplateList", serializers.DocumentTemplate
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DocumentTemplateList(api.GenericAPIView):
|
|
24
|
+
queryset = models.DocumentTemplate.objects
|
|
25
|
+
pagination_class = type(
|
|
26
|
+
"CustomPagination",
|
|
27
|
+
(pagination.LimitOffsetPagination,),
|
|
28
|
+
dict(default_limit=20),
|
|
29
|
+
)
|
|
30
|
+
serializer_class = DocumentTemplates
|
|
31
|
+
|
|
32
|
+
@openapi.extend_schema(
|
|
33
|
+
tags=["Documents"],
|
|
34
|
+
operation_id=f"{ENDPOINT_ID}list",
|
|
35
|
+
extensions={"x-operationId": "listDocumentTemplates"},
|
|
36
|
+
summary="List all templates",
|
|
37
|
+
responses={
|
|
38
|
+
200: DocumentTemplates(),
|
|
39
|
+
404: serializers.ErrorResponse(),
|
|
40
|
+
500: serializers.ErrorResponse(),
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
def get(self, request: Request):
|
|
44
|
+
"""
|
|
45
|
+
Retrieve all templates.
|
|
46
|
+
"""
|
|
47
|
+
templates = models.DocumentTemplate.access_by(request)
|
|
48
|
+
response = self.paginate_queryset(
|
|
49
|
+
serializers.DocumentTemplate(templates, many=True).data
|
|
50
|
+
)
|
|
51
|
+
return self.get_paginated_response(response)
|
|
52
|
+
|
|
53
|
+
@openapi.extend_schema(
|
|
54
|
+
tags=["Documents"],
|
|
55
|
+
operation_id=f"{ENDPOINT_ID}create",
|
|
56
|
+
extensions={"x-operationId": "createDocumentTemplate"},
|
|
57
|
+
summary="Create a template",
|
|
58
|
+
request=serializers.DocumentTemplateData(),
|
|
59
|
+
responses={
|
|
60
|
+
201: serializers.DocumentTemplate(),
|
|
61
|
+
400: serializers.ErrorResponse(),
|
|
62
|
+
500: serializers.ErrorResponse(),
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
def post(self, request: Request):
|
|
66
|
+
"""
|
|
67
|
+
Create a new template.
|
|
68
|
+
"""
|
|
69
|
+
template = (
|
|
70
|
+
serializers.DocumentTemplateModelSerializer.map(
|
|
71
|
+
data=request.data, context=request
|
|
72
|
+
)
|
|
73
|
+
.save()
|
|
74
|
+
.instance
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return Response(
|
|
78
|
+
serializers.DocumentTemplate(template).data,
|
|
79
|
+
status=status.HTTP_201_CREATED,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DocumentTemplateDetail(api.APIView):
|
|
84
|
+
|
|
85
|
+
@openapi.extend_schema(
|
|
86
|
+
tags=["Documents"],
|
|
87
|
+
operation_id=f"{ENDPOINT_ID}retrieve",
|
|
88
|
+
extensions={"x-operationId": "retrieveDocumentTemplate"},
|
|
89
|
+
summary="Retrieve a template",
|
|
90
|
+
responses={
|
|
91
|
+
200: serializers.DocumentTemplate(),
|
|
92
|
+
400: serializers.ErrorResponse(),
|
|
93
|
+
500: serializers.ErrorResponse(),
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
def get(self, request: Request, pk: str):
|
|
97
|
+
"""
|
|
98
|
+
Retrieve a template.
|
|
99
|
+
"""
|
|
100
|
+
template = models.DocumentTemplate.access_by(request).get(pk=pk)
|
|
101
|
+
return Response(serializers.DocumentTemplate(template).data)
|
|
102
|
+
|
|
103
|
+
@openapi.extend_schema(
|
|
104
|
+
tags=["Documents"],
|
|
105
|
+
operation_id=f"{ENDPOINT_ID}update",
|
|
106
|
+
extensions={"x-operationId": "updateDocumentTemplate"},
|
|
107
|
+
summary="Update a template",
|
|
108
|
+
request=serializers.DocumentTemplateData(),
|
|
109
|
+
responses={
|
|
110
|
+
200: serializers.DocumentTemplate(),
|
|
111
|
+
400: serializers.ErrorResponse(),
|
|
112
|
+
404: serializers.ErrorResponse(),
|
|
113
|
+
500: serializers.ErrorResponse(),
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
def patch(self, request: Request, pk: str):
|
|
117
|
+
"""
|
|
118
|
+
update a template.
|
|
119
|
+
"""
|
|
120
|
+
template = models.DocumentTemplate.access_by(request).get(pk=pk)
|
|
121
|
+
|
|
122
|
+
serializers.DocumentTemplateModelSerializer.map(
|
|
123
|
+
template,
|
|
124
|
+
data=request.data,
|
|
125
|
+
).save()
|
|
126
|
+
|
|
127
|
+
return Response(serializers.DocumentTemplate(template).data)
|
|
128
|
+
|
|
129
|
+
@openapi.extend_schema(
|
|
130
|
+
tags=["Documents"],
|
|
131
|
+
operation_id=f"{ENDPOINT_ID}discard",
|
|
132
|
+
extensions={"x-operationId": "discardDocumentTemplate"},
|
|
133
|
+
summary="Delete a template",
|
|
134
|
+
responses={
|
|
135
|
+
200: serializers.DocumentTemplate(),
|
|
136
|
+
404: serializers.ErrorResponse(),
|
|
137
|
+
409: serializers.ErrorResponse(),
|
|
138
|
+
500: serializers.ErrorResponse(),
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
def delete(self, request: Request, pk: str):
|
|
142
|
+
"""
|
|
143
|
+
Delete a template.
|
|
144
|
+
"""
|
|
145
|
+
template = models.DocumentTemplate.access_by(request).get(pk=pk)
|
|
146
|
+
|
|
147
|
+
template.delete(keep_parents=True)
|
|
148
|
+
|
|
149
|
+
return Response(serializers.DocumentTemplate(template).data)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class DocumentGenerator(api.APIView):
|
|
153
|
+
@openapi.extend_schema(
|
|
154
|
+
tags=["Documents"],
|
|
155
|
+
operation_id=f"{ENDPOINT_ID}generateDocument",
|
|
156
|
+
summary="Generate a document",
|
|
157
|
+
request=serializers.DocumentData(),
|
|
158
|
+
responses={
|
|
159
|
+
201: serializers.GeneratedDocument(),
|
|
160
|
+
400: serializers.ErrorResponse(),
|
|
161
|
+
404: serializers.ErrorResponse(),
|
|
162
|
+
500: serializers.ErrorResponse(),
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
def post(self, request: Request):
|
|
166
|
+
"""Generate any document.
|
|
167
|
+
This API is designed to be used to generate GS1 labels,
|
|
168
|
+
invoices and any document that requires external data.
|
|
169
|
+
"""
|
|
170
|
+
data = serializers.DocumentData.map(data=request.data).data
|
|
171
|
+
|
|
172
|
+
if data.get("template") is None and data.get("template_id") is None:
|
|
173
|
+
raise serializers.ValidationError("template or template_id is required")
|
|
174
|
+
|
|
175
|
+
document_template = lib.identity(
|
|
176
|
+
None
|
|
177
|
+
if data.get("template_id") is None
|
|
178
|
+
else models.DocumentTemplate.objects.get(pk=data.get("template_id"))
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
doc_file = generator.Documents.generate(
|
|
182
|
+
getattr(document_template, "template", data.get("template")),
|
|
183
|
+
related_object=getattr(document_template, "related_object", None),
|
|
184
|
+
metadata=getattr(document_template, "metadata", {}),
|
|
185
|
+
data=data.get("data"),
|
|
186
|
+
options=data.get("options"),
|
|
187
|
+
doc_name=data.get("doc_name"),
|
|
188
|
+
doc_fomat=data.get("doc_format"),
|
|
189
|
+
)
|
|
190
|
+
document = serializers.GeneratedDocument.map(
|
|
191
|
+
data={
|
|
192
|
+
**data,
|
|
193
|
+
"doc_file": base64.b64encode(doc_file.getvalue()).decode("utf-8"),
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return Response(document.data, status=status.HTTP_201_CREATED)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
urlpatterns = [
|
|
201
|
+
urls.path(
|
|
202
|
+
"documents/templates",
|
|
203
|
+
DocumentTemplateList.as_view(),
|
|
204
|
+
name="document-template-list",
|
|
205
|
+
),
|
|
206
|
+
urls.path(
|
|
207
|
+
"documents/templates/<str:pk>",
|
|
208
|
+
DocumentTemplateDetail.as_view(),
|
|
209
|
+
name="document-template-details",
|
|
210
|
+
),
|
|
211
|
+
urls.path(
|
|
212
|
+
"documents/generate",
|
|
213
|
+
DocumentGenerator.as_view(),
|
|
214
|
+
name="document-generator",
|
|
215
|
+
),
|
|
216
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import strawberry
|
|
2
|
+
from strawberry.types import Info
|
|
3
|
+
|
|
4
|
+
import karrio.server.graph.utils as utils
|
|
5
|
+
import karrio.server.graph.schemas.base as base
|
|
6
|
+
import karrio.server.graph.schemas.documents.mutations as mutations
|
|
7
|
+
import karrio.server.graph.schemas.documents.inputs as inputs
|
|
8
|
+
import karrio.server.graph.schemas.documents.types as types
|
|
9
|
+
import karrio.server.documents.models as models
|
|
10
|
+
|
|
11
|
+
extra_types: list = []
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@strawberry.type
|
|
15
|
+
class Query:
|
|
16
|
+
document_template: types.DocumentTemplateType = strawberry.field(
|
|
17
|
+
resolver=types.DocumentTemplateType.resolve
|
|
18
|
+
)
|
|
19
|
+
document_templates: utils.Connection[types.DocumentTemplateType] = strawberry.field(
|
|
20
|
+
resolver=types.DocumentTemplateType.resolve_list
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@strawberry.type
|
|
25
|
+
class Mutation:
|
|
26
|
+
@strawberry.mutation
|
|
27
|
+
def create_document_template(
|
|
28
|
+
self, info: Info, input: inputs.CreateDocumentTemplateMutationInput
|
|
29
|
+
) -> mutations.CreateDocumentTemplateMutation:
|
|
30
|
+
return mutations.CreateDocumentTemplateMutation.mutate(info, **input.to_dict())
|
|
31
|
+
|
|
32
|
+
@strawberry.mutation
|
|
33
|
+
def update_document_template(
|
|
34
|
+
self, info: Info, input: inputs.UpdateDocumentTemplateMutationInput
|
|
35
|
+
) -> mutations.UpdateDocumentTemplateMutation:
|
|
36
|
+
return mutations.UpdateDocumentTemplateMutation.mutate(info, **input.to_dict())
|
|
37
|
+
|
|
38
|
+
@strawberry.mutation
|
|
39
|
+
def delete_document_template(
|
|
40
|
+
self, info: Info, input: base.inputs.DeleteMutationInput
|
|
41
|
+
) -> base.mutations.DeleteMutation:
|
|
42
|
+
return base.mutations.DeleteMutation.mutate(
|
|
43
|
+
info,
|
|
44
|
+
model=models.DocumentTemplate,
|
|
45
|
+
**input.to_dict()
|
|
46
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import strawberry
|
|
3
|
+
|
|
4
|
+
import karrio.server.graph.utils as utils
|
|
5
|
+
import karrio.server.documents.serializers as serializers
|
|
6
|
+
|
|
7
|
+
TemplateRelatedObjectEnum: typing.Any = strawberry.enum(
|
|
8
|
+
serializers.TemplateRelatedObject
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@strawberry.input
|
|
13
|
+
class CreateDocumentTemplateMutationInput(utils.BaseInput):
|
|
14
|
+
slug: str
|
|
15
|
+
name: str
|
|
16
|
+
template: str
|
|
17
|
+
active: typing.Optional[bool] = True
|
|
18
|
+
description: typing.Optional[str] = strawberry.UNSET
|
|
19
|
+
metadata: typing.Optional[utils.JSON] = strawberry.UNSET
|
|
20
|
+
related_object: typing.Optional[TemplateRelatedObjectEnum] = strawberry.UNSET
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@strawberry.input
|
|
24
|
+
class UpdateDocumentTemplateMutationInput(utils.BaseInput):
|
|
25
|
+
id: str
|
|
26
|
+
slug: typing.Optional[str] = strawberry.UNSET
|
|
27
|
+
name: typing.Optional[str] = strawberry.UNSET
|
|
28
|
+
template: typing.Optional[str] = strawberry.UNSET
|
|
29
|
+
active: typing.Optional[bool] = strawberry.UNSET
|
|
30
|
+
description: typing.Optional[str] = strawberry.UNSET
|
|
31
|
+
related_object: typing.Optional[TemplateRelatedObjectEnum] = strawberry.UNSET
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@strawberry.input
|
|
35
|
+
class DocumentTemplateFilter(utils.Paginated):
|
|
36
|
+
name: typing.Optional[str] = strawberry.UNSET
|
|
37
|
+
active: typing.Optional[bool] = strawberry.UNSET
|
|
38
|
+
related_object: typing.Optional[TemplateRelatedObjectEnum] = strawberry.UNSET
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import strawberry
|
|
3
|
+
from strawberry.types import Info
|
|
4
|
+
|
|
5
|
+
import karrio.server.graph.utils as utils
|
|
6
|
+
import karrio.server.graph.schemas.documents.types as types
|
|
7
|
+
import karrio.server.graph.schemas.documents.inputs as inputs
|
|
8
|
+
import karrio.server.documents.serializers as serializers
|
|
9
|
+
import karrio.server.documents.models as models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@strawberry.type
|
|
13
|
+
class CreateDocumentTemplateMutation(utils.BaseMutation):
|
|
14
|
+
template: typing.Optional[types.DocumentTemplateType] = None
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
@utils.authentication_required
|
|
18
|
+
@utils.authorization_required(["DOCUMENTS_MANAGEMENT", "manage_data"])
|
|
19
|
+
def mutate(
|
|
20
|
+
info: Info, **input: inputs.CreateDocumentTemplateMutationInput
|
|
21
|
+
) -> "CreateDocumentTemplateMutation":
|
|
22
|
+
serializer = serializers.DocumentTemplateModelSerializer(
|
|
23
|
+
data=input,
|
|
24
|
+
context=info.context.request,
|
|
25
|
+
)
|
|
26
|
+
serializer.is_valid(raise_exception=True)
|
|
27
|
+
|
|
28
|
+
return CreateDocumentTemplateMutation(template=serializer.save()) # type:ignore
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@strawberry.type
|
|
32
|
+
class UpdateDocumentTemplateMutation(utils.BaseMutation):
|
|
33
|
+
template: typing.Optional[types.DocumentTemplateType] = None
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
@utils.authentication_required
|
|
37
|
+
@utils.authorization_required(["DOCUMENTS_MANAGEMENT", "manage_data"])
|
|
38
|
+
def mutate(
|
|
39
|
+
info: Info, **input: inputs.UpdateDocumentTemplateMutationInput
|
|
40
|
+
) -> "UpdateDocumentTemplateMutation":
|
|
41
|
+
instance = models.DocumentTemplate.access_by(info.context.request).get(id=input["id"])
|
|
42
|
+
|
|
43
|
+
serializer = serializers.DocumentTemplateModelSerializer(
|
|
44
|
+
instance,
|
|
45
|
+
data=input,
|
|
46
|
+
partial=True,
|
|
47
|
+
context=info.context.request,
|
|
48
|
+
)
|
|
49
|
+
serializer.is_valid(raise_exception=True)
|
|
50
|
+
|
|
51
|
+
return UpdateDocumentTemplateMutation(template=serializer.save()) # type:ignore
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import datetime
|
|
3
|
+
import strawberry
|
|
4
|
+
|
|
5
|
+
import karrio.lib as lib
|
|
6
|
+
import karrio.server.graph.utils as utils
|
|
7
|
+
import karrio.server.graph.schemas.base.types as base
|
|
8
|
+
import karrio.server.graph.schemas.documents.inputs as inputs
|
|
9
|
+
import karrio.server.documents.models as models
|
|
10
|
+
import karrio.server.documents.filters as filters
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@strawberry.type
|
|
14
|
+
class DocumentTemplateType:
|
|
15
|
+
object_type: str
|
|
16
|
+
preview_url: typing.Optional[str]
|
|
17
|
+
id: str
|
|
18
|
+
name: str
|
|
19
|
+
slug: str
|
|
20
|
+
template: str
|
|
21
|
+
active: bool
|
|
22
|
+
description: typing.Optional[str]
|
|
23
|
+
related_object: typing.Optional[inputs.TemplateRelatedObjectEnum]
|
|
24
|
+
created_at: typing.Optional[datetime.datetime]
|
|
25
|
+
updated_at: typing.Optional[datetime.datetime]
|
|
26
|
+
created_by: typing.Optional[base.UserType]
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
@utils.authentication_required
|
|
30
|
+
def resolve(info, id: str) -> typing.Optional["DocumentTemplateType"]:
|
|
31
|
+
return models.DocumentTemplate.access_by(info.context.request).filter(id=id).first()
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
@utils.authentication_required
|
|
35
|
+
def resolve_list(
|
|
36
|
+
info,
|
|
37
|
+
filter: typing.Optional[inputs.DocumentTemplateFilter] = strawberry.UNSET,
|
|
38
|
+
) -> utils.Connection["DocumentTemplateType"]:
|
|
39
|
+
_filter = filter if filter is not strawberry.UNSET else inputs.DocumentTemplateFilter()
|
|
40
|
+
queryset = filters.DocumentTemplateFilter(
|
|
41
|
+
_filter.to_dict(), models.DocumentTemplate.access_by(info.context.request)
|
|
42
|
+
).qs
|
|
43
|
+
return utils.paginated_connection(queryset, **_filter.pagination())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: karrio_server_documents
|
|
3
|
+
Version: 2025.5rc1
|
|
4
|
+
Summary: Multi-carrier shipping API apps module
|
|
5
|
+
Author-email: karrio <hello@karrio.io>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/karrioapi/karrio
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: pyzint
|
|
12
|
+
Requires-Dist: weasyprint
|
|
13
|
+
Requires-Dist: karrio_server_core
|
|
14
|
+
Requires-Dist: karrio_server_graph
|
|
15
|
+
Requires-Dist: karrio_server_manager
|
|
16
|
+
|
|
17
|
+
# karrio-server
|
|
18
|
+
|
|
19
|
+
Karrio server documents module.
|