karrio-server-documents 2025.5.2__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.
- 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 +298 -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 +29 -0
- karrio/server/documents/tests/__init__.py +6 -0
- karrio/server/documents/tests/test_generator.py +474 -0
- karrio/server/documents/tests/test_templates.py +318 -0
- karrio/server/documents/tests.py +182 -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 +223 -0
- karrio/server/documents/views/templates.py +255 -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 +41 -0
- karrio/server/graph/schemas/documents/mutations.py +51 -0
- karrio/server/graph/schemas/documents/types.py +45 -0
- karrio_server_documents-2025.5.2.dist-info/METADATA +19 -0
- karrio_server_documents-2025.5.2.dist-info/RECORD +37 -0
- karrio_server_documents-2025.5.2.dist-info/WHEEL +5 -0
- karrio_server_documents-2025.5.2.dist-info/top_level.txt +2 -0
|
File without changes
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import django.forms as forms
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.utils.translation import gettext_lazy as _
|
|
5
|
+
from django.utils.html import format_html
|
|
6
|
+
from django.urls import reverse
|
|
7
|
+
|
|
8
|
+
import karrio.server.documents.models as models
|
|
9
|
+
import karrio.server.documents.serializers.base as serializers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DocumentTemplateAdminForm(forms.ModelForm):
|
|
13
|
+
class Meta:
|
|
14
|
+
model = models.DocumentTemplate
|
|
15
|
+
fields = "__all__"
|
|
16
|
+
widgets = {
|
|
17
|
+
"template": forms.Textarea(attrs={"rows": 30, "cols": 100}),
|
|
18
|
+
"related_object": forms.Select(
|
|
19
|
+
choices=[
|
|
20
|
+
(c.name, c.name) for c in list(serializers.TemplateRelatedObject)
|
|
21
|
+
]
|
|
22
|
+
),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DocumentTemplateAdmin(admin.ModelAdmin):
|
|
27
|
+
form = DocumentTemplateAdminForm
|
|
28
|
+
list_display = ("slug", "name", "related_object", "active", "created_at", "preview_link")
|
|
29
|
+
search_fields = ("name", "description", "slug", "related_object")
|
|
30
|
+
list_filter = ("slug", "active")
|
|
31
|
+
readonly_fields = ("created_by", "full_preview_url")
|
|
32
|
+
|
|
33
|
+
def preview_link(self, obj):
|
|
34
|
+
"""Return a clickable preview link that opens in a new tab"""
|
|
35
|
+
url = obj.preview_url
|
|
36
|
+
return format_html(
|
|
37
|
+
'<a href="{}" target="_blank" rel="noopener noreferrer">Preview</a>',
|
|
38
|
+
url
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
preview_link.short_description = "Preview"
|
|
42
|
+
|
|
43
|
+
def full_preview_url(self, obj):
|
|
44
|
+
"""Return the full absolute URL for the preview"""
|
|
45
|
+
if obj.pk:
|
|
46
|
+
request = self.request if hasattr(self, 'request') else None
|
|
47
|
+
if request:
|
|
48
|
+
relative_url = obj.preview_url
|
|
49
|
+
absolute_url = request.build_absolute_uri(relative_url)
|
|
50
|
+
return format_html(
|
|
51
|
+
'<a href="{}" target="_blank" rel="noopener noreferrer">{}</a>',
|
|
52
|
+
absolute_url, absolute_url
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
# Fallback if request is not available
|
|
56
|
+
relative_url = obj.preview_url
|
|
57
|
+
return f"(Relative URL: {relative_url})"
|
|
58
|
+
return "Save the template first to generate preview URL"
|
|
59
|
+
|
|
60
|
+
full_preview_url.short_description = "Full Preview URL"
|
|
61
|
+
|
|
62
|
+
def get_queryset(self, request):
|
|
63
|
+
if settings.MULTI_ORGANIZATIONS:
|
|
64
|
+
return models.DocumentTemplate.objects.all().filter(
|
|
65
|
+
link__org__users__id=request.user.id
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return super().get_queryset(request)
|
|
69
|
+
|
|
70
|
+
def save_model(self, request, obj, form, change):
|
|
71
|
+
# Store request for use in full_preview_url method
|
|
72
|
+
self.request = request
|
|
73
|
+
obj.created_by = request.user
|
|
74
|
+
super().save_model(request, obj, form, change)
|
|
75
|
+
|
|
76
|
+
if settings.MULTI_ORGANIZATIONS:
|
|
77
|
+
import karrio.server.serializers as serializers
|
|
78
|
+
|
|
79
|
+
serializers.link_org(obj, request)
|
|
80
|
+
|
|
81
|
+
def change_view(self, request, object_id, form_url='', extra_context=None):
|
|
82
|
+
# Store request for use in full_preview_url method
|
|
83
|
+
self.request = request
|
|
84
|
+
return super().change_view(request, object_id, form_url, extra_context)
|
|
85
|
+
|
|
86
|
+
def add_view(self, request, form_url='', extra_context=None):
|
|
87
|
+
# Store request for use in full_preview_url method
|
|
88
|
+
self.request = request
|
|
89
|
+
return super().add_view(request, form_url, extra_context)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Register your models here.
|
|
93
|
+
admin.site.register(models.DocumentTemplate, DocumentTemplateAdmin)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DocumentsConfig(AppConfig):
|
|
6
|
+
name = "karrio.server.documents"
|
|
7
|
+
verbose_name = _("Documents")
|
|
8
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
9
|
+
|
|
10
|
+
def ready(self):
|
|
11
|
+
from karrio.server.documents import signals
|
|
12
|
+
|
|
13
|
+
signals.register_all()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import karrio.server.filters as filters
|
|
2
|
+
import karrio.server.documents.models as models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DocumentTemplateFilter(filters.FilterSet):
|
|
6
|
+
name = filters.CharFilter(field_name="name", lookup_expr="icontains")
|
|
7
|
+
related_object = filters.CharFilter(
|
|
8
|
+
field_name="related_object", lookup_expr="icontains"
|
|
9
|
+
)
|
|
10
|
+
active = filters.BooleanFilter(field_name="active")
|
|
11
|
+
|
|
12
|
+
class Meta:
|
|
13
|
+
model = models.DocumentTemplate
|
|
14
|
+
fields: list = []
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import typing
|
|
3
|
+
import base64
|
|
4
|
+
import jinja2
|
|
5
|
+
import weasyprint
|
|
6
|
+
from django.db.models import Sum
|
|
7
|
+
import weasyprint.text.fonts as fonts
|
|
8
|
+
|
|
9
|
+
import karrio.lib as lib
|
|
10
|
+
import karrio.server.documents.utils as utils
|
|
11
|
+
import karrio.server.core.dataunits as dataunits
|
|
12
|
+
import karrio.server.orders.models as order_models
|
|
13
|
+
import karrio.server.manager.models as manager_models
|
|
14
|
+
import karrio.server.documents.models as document_models
|
|
15
|
+
import karrio.server.orders.serializers as orders_serializers
|
|
16
|
+
import karrio.server.manager.serializers as manager_serializers
|
|
17
|
+
|
|
18
|
+
FONT_CONFIG = fonts.FontConfiguration()
|
|
19
|
+
PAGE_SEPARATOR = '<p style="page-break-before: always"></p>'
|
|
20
|
+
STYLESHEETS = [
|
|
21
|
+
weasyprint.CSS(url="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"),
|
|
22
|
+
weasyprint.CSS(
|
|
23
|
+
string="""
|
|
24
|
+
@page { margin: 1cm }
|
|
25
|
+
@font-face {
|
|
26
|
+
font-family: 'system';
|
|
27
|
+
src: local('Arial');
|
|
28
|
+
}
|
|
29
|
+
body { font-family: 'system', sans-serif; }
|
|
30
|
+
"""
|
|
31
|
+
),
|
|
32
|
+
]
|
|
33
|
+
UNITS = {
|
|
34
|
+
"PAGE_SEPARATOR": PAGE_SEPARATOR,
|
|
35
|
+
"CountryISO": lib.units.CountryISO.as_dict(),
|
|
36
|
+
**dataunits.REFERENCE_MODELS,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TemplateRenderingError(Exception):
|
|
41
|
+
"""Custom exception for template rendering errors"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message, line_number=None, template_error=None):
|
|
44
|
+
self.message = message
|
|
45
|
+
self.line_number = line_number
|
|
46
|
+
self.template_error = template_error
|
|
47
|
+
super().__init__(self.message)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Documents:
|
|
51
|
+
@staticmethod
|
|
52
|
+
def generate(
|
|
53
|
+
template: str,
|
|
54
|
+
data: dict = {},
|
|
55
|
+
related_object: str = None,
|
|
56
|
+
**kwargs,
|
|
57
|
+
) -> io.BytesIO:
|
|
58
|
+
options = kwargs.get("options") or {}
|
|
59
|
+
metadata = kwargs.get("metadata") or {}
|
|
60
|
+
|
|
61
|
+
# Build contexts based on related_object and provided data
|
|
62
|
+
shipment_contexts = data.get("shipments_context") or lib.identity(
|
|
63
|
+
get_shipments_context(data["shipments"])
|
|
64
|
+
if "shipments" in data and related_object == "shipment"
|
|
65
|
+
else []
|
|
66
|
+
)
|
|
67
|
+
order_contexts = data.get("orders_context") or lib.identity(
|
|
68
|
+
get_orders_context(data["orders"])
|
|
69
|
+
if "orders" in data and related_object == "order"
|
|
70
|
+
else []
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# For generic contexts, include the full data structure
|
|
74
|
+
generic_contexts = data.get("generic_context") or lib.identity(
|
|
75
|
+
[{"data": data}] if related_object is None else []
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# If no specific contexts are provided but we have a related_object, create a context with the data
|
|
79
|
+
if not shipment_contexts and not order_contexts and not generic_contexts:
|
|
80
|
+
# For fallback cases, always wrap data to maintain legacy behavior
|
|
81
|
+
generic_contexts = [{"data": data}]
|
|
82
|
+
|
|
83
|
+
filename = lib.identity(
|
|
84
|
+
dict(filename=kwargs.get("doc_name")) if kwargs.get("doc_name") else {}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def safe_render_prefetch(ctx):
|
|
88
|
+
"""Safely render prefetch templates with error handling"""
|
|
89
|
+
result = {}
|
|
90
|
+
for key, value in options.get("prefetch", {}).items():
|
|
91
|
+
try:
|
|
92
|
+
template_obj = jinja2.Template(value)
|
|
93
|
+
rendered = template_obj.render(
|
|
94
|
+
**ctx,
|
|
95
|
+
metadata=metadata,
|
|
96
|
+
units=UNITS,
|
|
97
|
+
utils=utils,
|
|
98
|
+
lib=lib,
|
|
99
|
+
)
|
|
100
|
+
result[key] = str(rendered or "")
|
|
101
|
+
except jinja2.TemplateError as e:
|
|
102
|
+
# Log the error but continue with empty string
|
|
103
|
+
result[key] = ""
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
jinja_template = jinja2.Template(template)
|
|
108
|
+
except jinja2.TemplateSyntaxError as e:
|
|
109
|
+
raise TemplateRenderingError(
|
|
110
|
+
f"Template syntax error: {e.message}",
|
|
111
|
+
line_number=e.lineno,
|
|
112
|
+
template_error=e,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
all_contexts = shipment_contexts + order_contexts + generic_contexts
|
|
116
|
+
|
|
117
|
+
# If no contexts are available, create a default one
|
|
118
|
+
if not all_contexts:
|
|
119
|
+
all_contexts = [{"data": data} if data else {}]
|
|
120
|
+
|
|
121
|
+
rendered_pages = []
|
|
122
|
+
for ctx in all_contexts:
|
|
123
|
+
try:
|
|
124
|
+
# Add prefetch data safely
|
|
125
|
+
ctx_with_prefetch = {**ctx, "prefetch": safe_render_prefetch(ctx)}
|
|
126
|
+
|
|
127
|
+
rendered_page = jinja_template.render(
|
|
128
|
+
**ctx_with_prefetch,
|
|
129
|
+
metadata=metadata,
|
|
130
|
+
units=UNITS,
|
|
131
|
+
utils=utils,
|
|
132
|
+
lib=lib,
|
|
133
|
+
)
|
|
134
|
+
rendered_pages.append(rendered_page)
|
|
135
|
+
except jinja2.UndefinedError as e:
|
|
136
|
+
raise TemplateRenderingError(
|
|
137
|
+
f"Template variable error: {str(e)}. Available variables: {list(ctx.keys())}",
|
|
138
|
+
template_error=e,
|
|
139
|
+
)
|
|
140
|
+
except jinja2.TemplateRuntimeError as e:
|
|
141
|
+
raise TemplateRenderingError(
|
|
142
|
+
f"Template runtime error: {str(e)}",
|
|
143
|
+
line_number=getattr(e, "lineno", None),
|
|
144
|
+
template_error=e,
|
|
145
|
+
)
|
|
146
|
+
except jinja2.TemplateError as e:
|
|
147
|
+
raise TemplateRenderingError(
|
|
148
|
+
f"Template error: {str(e)}",
|
|
149
|
+
line_number=getattr(e, "lineno", None),
|
|
150
|
+
template_error=e,
|
|
151
|
+
)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
raise TemplateRenderingError(
|
|
154
|
+
f"Unexpected error during template rendering: {str(e)}",
|
|
155
|
+
template_error=e,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
content = PAGE_SEPARATOR.join(rendered_pages)
|
|
159
|
+
|
|
160
|
+
# Handle PDF generation errors
|
|
161
|
+
try:
|
|
162
|
+
buffer = io.BytesIO()
|
|
163
|
+
html = weasyprint.HTML(string=content, encoding="utf-8")
|
|
164
|
+
html.write_pdf(
|
|
165
|
+
buffer,
|
|
166
|
+
stylesheets=STYLESHEETS,
|
|
167
|
+
font_config=FONT_CONFIG,
|
|
168
|
+
optimize_size=("fonts", "images"),
|
|
169
|
+
)
|
|
170
|
+
return buffer
|
|
171
|
+
except Exception as e:
|
|
172
|
+
raise TemplateRenderingError(
|
|
173
|
+
f"PDF generation error: {str(e)}", template_error=e
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def generate_template(
|
|
178
|
+
document: document_models.DocumentTemplate,
|
|
179
|
+
data: dict,
|
|
180
|
+
**kwargs,
|
|
181
|
+
) -> io.BytesIO:
|
|
182
|
+
return Documents.generate(
|
|
183
|
+
template=document.template,
|
|
184
|
+
data=data,
|
|
185
|
+
options=document.options,
|
|
186
|
+
metadata=document.metadata,
|
|
187
|
+
related_object=document.related_object,
|
|
188
|
+
**kwargs,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def generate_shipment_document(slug: str, shipment, **kwargs) -> dict:
|
|
193
|
+
template = document_models.DocumentTemplate.objects.get(slug=slug)
|
|
194
|
+
carrier = kwargs.get("carrier") or getattr(
|
|
195
|
+
shipment, "selected_rate_carrier", None
|
|
196
|
+
)
|
|
197
|
+
params = dict(
|
|
198
|
+
shipments_context=[
|
|
199
|
+
dict(
|
|
200
|
+
shipment=manager_serializers.Shipment(shipment).data,
|
|
201
|
+
line_items=get_shipment_item_contexts(shipment),
|
|
202
|
+
carrier=get_carrier_context(carrier),
|
|
203
|
+
orders=orders_serializers.Order(
|
|
204
|
+
get_shipment_order_contexts(shipment),
|
|
205
|
+
many=True,
|
|
206
|
+
).data,
|
|
207
|
+
)
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
document = Documents.generate_template(template, params).getvalue()
|
|
211
|
+
|
|
212
|
+
return dict(
|
|
213
|
+
doc_format="PDF",
|
|
214
|
+
doc_name=f"{template.name}.pdf",
|
|
215
|
+
doc_type=(template.metadata or {}).get("doc_type") or "commercial_invoice",
|
|
216
|
+
doc_file=base64.b64encode(document).decode("utf-8"),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# -----------------------------------------------------------
|
|
221
|
+
# contexts data parsers
|
|
222
|
+
# -----------------------------------------------------------
|
|
223
|
+
# region
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def get_shipments_context(shipment_ids: str) -> typing.List[dict]:
|
|
227
|
+
if shipment_ids == "sample":
|
|
228
|
+
return [utils.SHIPMENT_SAMPLE]
|
|
229
|
+
|
|
230
|
+
ids = shipment_ids.split(",")
|
|
231
|
+
shipments = manager_models.Shipment.objects.filter(id__in=ids)
|
|
232
|
+
|
|
233
|
+
return [
|
|
234
|
+
dict(
|
|
235
|
+
shipment=manager_serializers.Shipment(shipment).data,
|
|
236
|
+
line_items=get_shipment_item_contexts(shipment),
|
|
237
|
+
carrier=get_carrier_context(shipment.selected_rate_carrier),
|
|
238
|
+
orders=orders_serializers.Order(
|
|
239
|
+
get_shipment_order_contexts(shipment), many=True
|
|
240
|
+
).data,
|
|
241
|
+
)
|
|
242
|
+
for shipment in shipments
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def get_shipment_item_contexts(shipment):
|
|
247
|
+
items = order_models.LineItem.objects.filter(
|
|
248
|
+
commodity_parcel__parcel_shipment=shipment
|
|
249
|
+
)
|
|
250
|
+
distinct_items = [
|
|
251
|
+
__ for _, __ in ({item.parent_id: item for item in items}).items()
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
return [
|
|
255
|
+
{
|
|
256
|
+
**orders_serializers.LineItem(item.parent or item).data,
|
|
257
|
+
"ship_quantity": items.filter(parent_id=item.parent_id).aggregate(
|
|
258
|
+
Sum("quantity")
|
|
259
|
+
)["quantity__sum"],
|
|
260
|
+
"order": (orders_serializers.Order(item.order).data if item.order else {}),
|
|
261
|
+
}
|
|
262
|
+
for item in distinct_items
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def get_shipment_order_contexts(shipment):
|
|
267
|
+
return (
|
|
268
|
+
order_models.Order.objects.filter(
|
|
269
|
+
line_items__children__commodity_parcel__parcel_shipment=shipment
|
|
270
|
+
)
|
|
271
|
+
.order_by("-order_date")
|
|
272
|
+
.distinct()
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_carrier_context(carrier=None):
|
|
277
|
+
if carrier is None:
|
|
278
|
+
return {}
|
|
279
|
+
|
|
280
|
+
return carrier.data.to_dict()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_orders_context(order_ids: str) -> typing.List[dict]:
|
|
284
|
+
if order_ids == "sample":
|
|
285
|
+
return [utils.ORDER_SAMPLE]
|
|
286
|
+
|
|
287
|
+
ids = order_ids.split(",")
|
|
288
|
+
orders = order_models.Order.objects.filter(id__in=ids)
|
|
289
|
+
|
|
290
|
+
return [
|
|
291
|
+
dict(
|
|
292
|
+
order=orders_serializers.Order(order).data,
|
|
293
|
+
)
|
|
294
|
+
for order in orders
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# endregion
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Generated by Django 3.2.11 on 2022-03-14 14:49
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
import django.contrib.postgres.fields
|
|
5
|
+
import django.core.validators
|
|
6
|
+
from django.db import migrations, models
|
|
7
|
+
import django.db.models.deletion
|
|
8
|
+
import functools
|
|
9
|
+
import karrio.server.core.models.base
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Migration(migrations.Migration):
|
|
13
|
+
|
|
14
|
+
initial = True
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
operations = [
|
|
21
|
+
migrations.CreateModel(
|
|
22
|
+
name="DocumentTemplate",
|
|
23
|
+
fields=[
|
|
24
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
25
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
26
|
+
(
|
|
27
|
+
"id",
|
|
28
|
+
models.CharField(
|
|
29
|
+
default=functools.partial(
|
|
30
|
+
karrio.server.core.models.base.uuid,
|
|
31
|
+
*(),
|
|
32
|
+
**{"prefix": "doc_"}
|
|
33
|
+
),
|
|
34
|
+
editable=False,
|
|
35
|
+
max_length=50,
|
|
36
|
+
primary_key=True,
|
|
37
|
+
serialize=False,
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
(
|
|
41
|
+
"slug",
|
|
42
|
+
models.SlugField(
|
|
43
|
+
max_length=20,
|
|
44
|
+
validators=[
|
|
45
|
+
django.core.validators.RegexValidator("^[a-z0-9_]+$")
|
|
46
|
+
],
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
("name", models.CharField(max_length=50)),
|
|
50
|
+
("template", models.TextField()),
|
|
51
|
+
("description", models.CharField(blank=True, max_length=50, null=True)),
|
|
52
|
+
("related_objects", models.JSONField(blank=True, null=True)),
|
|
53
|
+
(
|
|
54
|
+
"created_by",
|
|
55
|
+
models.ForeignKey(
|
|
56
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
57
|
+
to=settings.AUTH_USER_MODEL,
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
options={
|
|
62
|
+
"verbose_name": "Document Template",
|
|
63
|
+
"verbose_name_plural": "Document Templates",
|
|
64
|
+
"db_table": "document-template",
|
|
65
|
+
"ordering": ["-created_at"],
|
|
66
|
+
},
|
|
67
|
+
bases=(karrio.server.core.models.base.ControlledAccessModel, models.Model),
|
|
68
|
+
),
|
|
69
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 3.2.12 on 2022-03-21 20:47
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('documents', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='documenttemplate',
|
|
15
|
+
name='related_objects',
|
|
16
|
+
field=models.CharField(max_length=25),
|
|
17
|
+
),
|
|
18
|
+
]
|
karrio/server/documents/migrations/0003_rename_related_objects_documenttemplate_related_object.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 3.2.12 on 2022-03-21 20:47
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('documents', '0002_alter_documenttemplate_related_objects'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RenameField(
|
|
14
|
+
model_name='documenttemplate',
|
|
15
|
+
old_name='related_objects',
|
|
16
|
+
new_name='related_object',
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 3.2.13 on 2022-06-18 09:09
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('documents', '0003_rename_related_objects_documenttemplate_related_object'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='documenttemplate',
|
|
15
|
+
name='active',
|
|
16
|
+
field=models.BooleanField(default=True, help_text='disable template flag. to filter out from active document downloads'),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated by Django 4.1.3 on 2022-12-10 17:09
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("documents", "0004_documenttemplate_active"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name="documenttemplate",
|
|
15
|
+
name="description",
|
|
16
|
+
field=models.CharField(blank=True, db_index=True, max_length=50, null=True),
|
|
17
|
+
),
|
|
18
|
+
migrations.AlterField(
|
|
19
|
+
model_name="documenttemplate",
|
|
20
|
+
name="name",
|
|
21
|
+
field=models.CharField(db_index=True, max_length=50),
|
|
22
|
+
),
|
|
23
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Generated by Django 4.1.7 on 2023-02-22 15:35
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import functools
|
|
5
|
+
import karrio.server.core.models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
dependencies = [
|
|
10
|
+
("documents", "0005_alter_documenttemplate_description_and_more"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="documenttemplate",
|
|
16
|
+
name="metadata",
|
|
17
|
+
field=models.JSONField(
|
|
18
|
+
blank=True,
|
|
19
|
+
default=functools.partial(
|
|
20
|
+
karrio.server.core.models._identity, *(), **{"value": {}}
|
|
21
|
+
),
|
|
22
|
+
null=True,
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-05-27 17:53
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("documents", "0006_documenttemplate_metadata"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name="documenttemplate",
|
|
15
|
+
name="related_object",
|
|
16
|
+
field=models.CharField(blank=True, max_length=25, null=True),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Generated by Django 4.2.16 on 2024-10-17 04:48
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import functools
|
|
5
|
+
import karrio.server.core.models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("documents", "0007_alter_documenttemplate_related_object"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AddField(
|
|
16
|
+
model_name="documenttemplate",
|
|
17
|
+
name="options",
|
|
18
|
+
field=models.JSONField(
|
|
19
|
+
blank=True,
|
|
20
|
+
default=functools.partial(
|
|
21
|
+
karrio.server.core.models._identity, *(), **{"value": {}}
|
|
22
|
+
),
|
|
23
|
+
null=True,
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
]
|
|
File without changes
|