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
|
@@ -0,0 +1,318 @@
|
|
|
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.graph.tests.base import GraphTestCase
|
|
7
|
+
from karrio.server.documents.models import DocumentTemplate
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestDocumentTemplatesREST(APITestCase):
|
|
11
|
+
def test_create_document_template(self):
|
|
12
|
+
url = reverse("karrio.server.documents:document-template-list")
|
|
13
|
+
data = DOCUMENT_TEMPLATE_DATA
|
|
14
|
+
|
|
15
|
+
response = self.client.post(url, data)
|
|
16
|
+
response_data = json.loads(response.content)
|
|
17
|
+
|
|
18
|
+
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
19
|
+
|
|
20
|
+
# Check individual fields instead of strict dictionary comparison
|
|
21
|
+
self.assertEqual(response_data["name"], "Test Invoice Template")
|
|
22
|
+
self.assertEqual(response_data["slug"], "test_invoice")
|
|
23
|
+
self.assertEqual(response_data["description"], "A test invoice template")
|
|
24
|
+
self.assertEqual(response_data["object_type"], "document-template")
|
|
25
|
+
self.assertEqual(response_data["related_object"], "shipment")
|
|
26
|
+
self.assertEqual(response_data["active"], True)
|
|
27
|
+
self.assertEqual(response_data["metadata"], {"doc_type": "invoice", "version": "1.0"})
|
|
28
|
+
self.assertEqual(response_data["options"], {"page_size": "A4", "orientation": "portrait"})
|
|
29
|
+
|
|
30
|
+
# Check that ID field exists
|
|
31
|
+
self.assertIn("id", response_data)
|
|
32
|
+
|
|
33
|
+
def test_list_document_templates(self):
|
|
34
|
+
# Create a template first
|
|
35
|
+
DocumentTemplate.objects.create(
|
|
36
|
+
**{
|
|
37
|
+
"name": "Test Template",
|
|
38
|
+
"slug": "test_template",
|
|
39
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
40
|
+
"description": "A test template",
|
|
41
|
+
"related_object": "shipment",
|
|
42
|
+
"created_by": self.user,
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
url = reverse("karrio.server.documents:document-template-list")
|
|
47
|
+
response = self.client.get(url)
|
|
48
|
+
response_data = json.loads(response.content)
|
|
49
|
+
|
|
50
|
+
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
51
|
+
self.assertIn("results", response_data)
|
|
52
|
+
self.assertEqual(len(response_data["results"]), 1)
|
|
53
|
+
self.assertEqual(response_data["results"][0]["name"], "Test Template")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestDocumentTemplateDetailsREST(APITestCase):
|
|
57
|
+
def setUp(self) -> None:
|
|
58
|
+
super().setUp()
|
|
59
|
+
self.template: DocumentTemplate = DocumentTemplate.objects.create(
|
|
60
|
+
**{
|
|
61
|
+
"name": "Test Template",
|
|
62
|
+
"slug": "test_template",
|
|
63
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
64
|
+
"description": "A test template",
|
|
65
|
+
"related_object": "shipment",
|
|
66
|
+
"active": True,
|
|
67
|
+
"metadata": {"doc_type": "invoice"},
|
|
68
|
+
"options": {"page_size": "A4"},
|
|
69
|
+
"created_by": self.user,
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def test_retrieve_document_template(self):
|
|
74
|
+
url = reverse(
|
|
75
|
+
"karrio.server.documents:document-template-details",
|
|
76
|
+
kwargs=dict(pk=self.template.pk),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
response = self.client.get(url)
|
|
80
|
+
response_data = json.loads(response.content)
|
|
81
|
+
|
|
82
|
+
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
83
|
+
|
|
84
|
+
# Check individual fields instead of strict dictionary comparison
|
|
85
|
+
self.assertEqual(response_data["name"], "Test Template")
|
|
86
|
+
self.assertEqual(response_data["slug"], "test_template")
|
|
87
|
+
self.assertEqual(response_data["description"], "A test template")
|
|
88
|
+
self.assertEqual(response_data["object_type"], "document-template")
|
|
89
|
+
self.assertEqual(response_data["related_object"], "shipment")
|
|
90
|
+
self.assertEqual(response_data["active"], True)
|
|
91
|
+
self.assertEqual(response_data["metadata"], {"doc_type": "invoice"})
|
|
92
|
+
self.assertEqual(response_data["options"], {"page_size": "A4"})
|
|
93
|
+
self.assertEqual(response_data["template"], SAMPLE_HTML_TEMPLATE)
|
|
94
|
+
|
|
95
|
+
# Check that ID field exists
|
|
96
|
+
self.assertIn("id", response_data)
|
|
97
|
+
|
|
98
|
+
def test_update_document_template(self):
|
|
99
|
+
url = reverse(
|
|
100
|
+
"karrio.server.documents:document-template-details",
|
|
101
|
+
kwargs=dict(pk=self.template.pk),
|
|
102
|
+
)
|
|
103
|
+
data = DOCUMENT_TEMPLATE_UPDATE_DATA
|
|
104
|
+
|
|
105
|
+
response = self.client.patch(url, data)
|
|
106
|
+
response_data = json.loads(response.content)
|
|
107
|
+
|
|
108
|
+
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
109
|
+
self.assertDictEqual(response_data, DOCUMENT_TEMPLATE_UPDATE_RESPONSE)
|
|
110
|
+
|
|
111
|
+
def test_delete_document_template(self):
|
|
112
|
+
url = reverse(
|
|
113
|
+
"karrio.server.documents:document-template-details",
|
|
114
|
+
kwargs=dict(pk=self.template.pk),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
response = self.client.delete(url)
|
|
118
|
+
response_data = json.loads(response.content)
|
|
119
|
+
|
|
120
|
+
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
121
|
+
# Verify template data is returned (soft delete behavior)
|
|
122
|
+
self.assertEqual(response_data.get("name"), "Test Template")
|
|
123
|
+
self.assertEqual(response_data.get("slug"), "test_template")
|
|
124
|
+
# The template should still be active (or check if it's marked as inactive)
|
|
125
|
+
self.assertIsNotNone(response_data.get("object_type"))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TestDocumentTemplatesGraphQL(GraphTestCase):
|
|
129
|
+
def test_query_document_templates(self):
|
|
130
|
+
# Create a template first
|
|
131
|
+
DocumentTemplate.objects.create(
|
|
132
|
+
**{
|
|
133
|
+
"name": "GraphQL Test Template",
|
|
134
|
+
"slug": "graphql_test",
|
|
135
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
136
|
+
"description": "A GraphQL test template",
|
|
137
|
+
"related_object": "order",
|
|
138
|
+
"created_by": self.user,
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
query = """
|
|
143
|
+
query {
|
|
144
|
+
document_templates {
|
|
145
|
+
edges {
|
|
146
|
+
node {
|
|
147
|
+
id
|
|
148
|
+
name
|
|
149
|
+
slug
|
|
150
|
+
description
|
|
151
|
+
related_object
|
|
152
|
+
active
|
|
153
|
+
object_type
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
result = self.query(query)
|
|
161
|
+
self.assertResponseNoErrors(result)
|
|
162
|
+
|
|
163
|
+
templates = result.data["data"]["document_templates"]["edges"]
|
|
164
|
+
self.assertEqual(len(templates), 1)
|
|
165
|
+
self.assertEqual(templates[0]["node"]["name"], "GraphQL Test Template")
|
|
166
|
+
self.assertEqual(templates[0]["node"]["slug"], "graphql_test")
|
|
167
|
+
|
|
168
|
+
def test_create_document_template_mutation(self):
|
|
169
|
+
mutation = """
|
|
170
|
+
mutation CreateDocumentTemplate($input: CreateDocumentTemplateMutationInput!) {
|
|
171
|
+
create_document_template(input: $input) {
|
|
172
|
+
template {
|
|
173
|
+
id
|
|
174
|
+
name
|
|
175
|
+
slug
|
|
176
|
+
description
|
|
177
|
+
related_object
|
|
178
|
+
active
|
|
179
|
+
}
|
|
180
|
+
errors {
|
|
181
|
+
field
|
|
182
|
+
messages
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
variables = {
|
|
189
|
+
"input": {
|
|
190
|
+
"name": "GraphQL Created Template",
|
|
191
|
+
"slug": "graphql_created",
|
|
192
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
193
|
+
"description": "Created via GraphQL",
|
|
194
|
+
"related_object": "shipment",
|
|
195
|
+
"active": True,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
result = self.query(mutation, variables=variables)
|
|
200
|
+
self.assertResponseNoErrors(result)
|
|
201
|
+
|
|
202
|
+
created_template = result.data["data"]["create_document_template"]["template"]
|
|
203
|
+
self.assertEqual(created_template["name"], "GraphQL Created Template")
|
|
204
|
+
self.assertEqual(created_template["slug"], "graphql_created")
|
|
205
|
+
self.assertTrue(created_template["active"])
|
|
206
|
+
|
|
207
|
+
def test_update_document_template_mutation(self):
|
|
208
|
+
# Create a template first
|
|
209
|
+
template = DocumentTemplate.objects.create(
|
|
210
|
+
**{
|
|
211
|
+
"name": "Original Template",
|
|
212
|
+
"slug": "original",
|
|
213
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
214
|
+
"description": "Original description",
|
|
215
|
+
"related_object": "shipment",
|
|
216
|
+
"created_by": self.user,
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
mutation = """
|
|
221
|
+
mutation UpdateDocumentTemplate($input: UpdateDocumentTemplateMutationInput!) {
|
|
222
|
+
update_document_template(input: $input) {
|
|
223
|
+
template {
|
|
224
|
+
id
|
|
225
|
+
name
|
|
226
|
+
description
|
|
227
|
+
active
|
|
228
|
+
}
|
|
229
|
+
errors {
|
|
230
|
+
field
|
|
231
|
+
messages
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
variables = {
|
|
238
|
+
"input": {
|
|
239
|
+
"id": template.id,
|
|
240
|
+
"name": "Updated Template",
|
|
241
|
+
"description": "Updated description",
|
|
242
|
+
"active": False,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
result = self.query(mutation, variables=variables)
|
|
247
|
+
self.assertResponseNoErrors(result)
|
|
248
|
+
|
|
249
|
+
updated_template = result.data["data"]["update_document_template"]["template"]
|
|
250
|
+
self.assertEqual(updated_template["name"], "Updated Template")
|
|
251
|
+
self.assertEqual(updated_template["description"], "Updated description")
|
|
252
|
+
self.assertFalse(updated_template["active"])
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# Test Data and Fixtures
|
|
256
|
+
SAMPLE_HTML_TEMPLATE = """
|
|
257
|
+
<title>{{ title | default('Test Document') }}</title>
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
DOCUMENT_TEMPLATE_DATA = {
|
|
261
|
+
"name": "Test Invoice Template",
|
|
262
|
+
"slug": "test_invoice",
|
|
263
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
264
|
+
"description": "A test invoice template",
|
|
265
|
+
"related_object": "shipment",
|
|
266
|
+
"active": True,
|
|
267
|
+
"metadata": {"doc_type": "invoice", "version": "1.0"},
|
|
268
|
+
"options": {"page_size": "A4", "orientation": "portrait"},
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
DOCUMENT_TEMPLATE_RESPONSE = {
|
|
272
|
+
"id": ANY,
|
|
273
|
+
"object_type": "document-template",
|
|
274
|
+
"name": "Test Invoice Template",
|
|
275
|
+
"slug": "test_invoice",
|
|
276
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
277
|
+
"description": "A test invoice template",
|
|
278
|
+
"related_object": "shipment",
|
|
279
|
+
"active": True,
|
|
280
|
+
"metadata": {"doc_type": "invoice", "version": "1.0"},
|
|
281
|
+
"options": {"page_size": "A4", "orientation": "portrait"},
|
|
282
|
+
"preview_url": ANY,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
DOCUMENT_TEMPLATE_DETAIL_RESPONSE = {
|
|
286
|
+
"active": True,
|
|
287
|
+
"description": "A test template",
|
|
288
|
+
"id": ANY,
|
|
289
|
+
"metadata": {"doc_type": "invoice"},
|
|
290
|
+
"name": "Test Template",
|
|
291
|
+
"object_type": "document-template",
|
|
292
|
+
"options": {"page_size": "A4"},
|
|
293
|
+
"related_object": "shipment",
|
|
294
|
+
"slug": "test_template",
|
|
295
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
296
|
+
"preview_url": ANY,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
DOCUMENT_TEMPLATE_UPDATE_DATA = {
|
|
300
|
+
"name": "Updated Test Template",
|
|
301
|
+
"description": "An updated test template",
|
|
302
|
+
"active": False,
|
|
303
|
+
"metadata": {"doc_type": "commercial_invoice", "version": "2.0"},
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
DOCUMENT_TEMPLATE_UPDATE_RESPONSE = {
|
|
307
|
+
"id": ANY,
|
|
308
|
+
"object_type": "document-template",
|
|
309
|
+
"name": "Updated Test Template",
|
|
310
|
+
"slug": "test_template",
|
|
311
|
+
"template": SAMPLE_HTML_TEMPLATE,
|
|
312
|
+
"description": "An updated test template",
|
|
313
|
+
"related_object": "shipment",
|
|
314
|
+
"active": False,
|
|
315
|
+
"metadata": {"doc_type": "commercial_invoice", "version": "2.0"},
|
|
316
|
+
"options": {"page_size": "A4"},
|
|
317
|
+
"preview_url": ANY,
|
|
318
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
karrio server documents module urls
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from django.urls import include, path
|
|
6
|
+
|
|
7
|
+
app_name = "karrio.server.documents"
|
|
8
|
+
urlpatterns = [
|
|
9
|
+
path("", include("karrio.server.documents.views.printers")),
|
|
10
|
+
path("v1/", include("karrio.server.documents.views.templates")),
|
|
11
|
+
]
|