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,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,240 @@
|
|
|
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 Documents:
|
|
41
|
+
@staticmethod
|
|
42
|
+
def generate(
|
|
43
|
+
template: str,
|
|
44
|
+
data: dict = {},
|
|
45
|
+
related_object: str = None,
|
|
46
|
+
**kwargs,
|
|
47
|
+
) -> io.BytesIO:
|
|
48
|
+
options = kwargs.get("options") or {}
|
|
49
|
+
metadata = kwargs.get("metadata") or {}
|
|
50
|
+
shipment_contexts = data.get("shipments_context") or lib.identity(
|
|
51
|
+
get_shipments_context(data["shipments"])
|
|
52
|
+
if "shipments" in data and related_object == "shipment"
|
|
53
|
+
else []
|
|
54
|
+
)
|
|
55
|
+
order_contexts = data.get("orders_context") or lib.identity(
|
|
56
|
+
get_orders_context(data["orders"])
|
|
57
|
+
if "orders" in data and related_object == "order"
|
|
58
|
+
else []
|
|
59
|
+
)
|
|
60
|
+
generic_contexts = data.get("generic_context") or lib.identity(
|
|
61
|
+
[{"data": data}] if related_object is None else []
|
|
62
|
+
)
|
|
63
|
+
filename = lib.identity(
|
|
64
|
+
dict(filename=kwargs.get("doc_name")) if kwargs.get("doc_name") else {}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
prefetch = lambda ctx: {
|
|
68
|
+
k: v
|
|
69
|
+
for o in lib.run_concurently(
|
|
70
|
+
lambda _: {
|
|
71
|
+
_[0]: str(
|
|
72
|
+
lib.failsafe(
|
|
73
|
+
lambda: _[1].render(
|
|
74
|
+
**ctx,
|
|
75
|
+
metadata=metadata,
|
|
76
|
+
units=UNITS,
|
|
77
|
+
utils=utils,
|
|
78
|
+
lib=lib,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
or ""
|
|
82
|
+
)
|
|
83
|
+
},
|
|
84
|
+
[
|
|
85
|
+
(key, jinja2.Template(value))
|
|
86
|
+
for key, value in options.get("prefetch", {}).items()
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
for k, v in o.items()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
jinja_template = jinja2.Template(template)
|
|
93
|
+
all_contexts = shipment_contexts + order_contexts + generic_contexts
|
|
94
|
+
rendered_pages = lib.run_asynchronously(
|
|
95
|
+
lambda ctx: jinja_template.render(
|
|
96
|
+
**ctx,
|
|
97
|
+
metadata=metadata,
|
|
98
|
+
units=UNITS,
|
|
99
|
+
utils=utils,
|
|
100
|
+
lib=lib,
|
|
101
|
+
prefetch=prefetch(ctx),
|
|
102
|
+
),
|
|
103
|
+
all_contexts,
|
|
104
|
+
)
|
|
105
|
+
content = PAGE_SEPARATOR.join(rendered_pages)
|
|
106
|
+
|
|
107
|
+
buffer = io.BytesIO()
|
|
108
|
+
html = weasyprint.HTML(string=content, encoding="utf-8")
|
|
109
|
+
html.write_pdf(
|
|
110
|
+
buffer,
|
|
111
|
+
stylesheets=STYLESHEETS,
|
|
112
|
+
font_config=FONT_CONFIG,
|
|
113
|
+
optimize_size=("fonts", "images"),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return buffer
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def generate_template(
|
|
120
|
+
document: document_models.DocumentTemplate,
|
|
121
|
+
data: dict,
|
|
122
|
+
**kwargs,
|
|
123
|
+
) -> io.BytesIO:
|
|
124
|
+
return Documents.generate(
|
|
125
|
+
template=document.template,
|
|
126
|
+
data=data,
|
|
127
|
+
options=document.options,
|
|
128
|
+
metadata=document.metadata,
|
|
129
|
+
related_object=document.related_object,
|
|
130
|
+
**kwargs,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def generate_shipment_document(slug: str, shipment, **kwargs) -> dict:
|
|
135
|
+
template = document_models.DocumentTemplate.objects.get(slug=slug)
|
|
136
|
+
carrier = kwargs.get("carrier") or getattr(
|
|
137
|
+
shipment, "selected_rate_carrier", None
|
|
138
|
+
)
|
|
139
|
+
params = dict(
|
|
140
|
+
shipments_context=[
|
|
141
|
+
dict(
|
|
142
|
+
shipment=manager_serializers.Shipment(shipment).data,
|
|
143
|
+
line_items=get_shipment_item_contexts(shipment),
|
|
144
|
+
carrier=get_carrier_context(carrier),
|
|
145
|
+
orders=orders_serializers.Order(
|
|
146
|
+
get_shipment_order_contexts(shipment),
|
|
147
|
+
many=True,
|
|
148
|
+
).data,
|
|
149
|
+
)
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
document = Documents.generate_template(template, params).getvalue()
|
|
153
|
+
|
|
154
|
+
return dict(
|
|
155
|
+
doc_format="PDF",
|
|
156
|
+
doc_name=f"{template.name}.pdf",
|
|
157
|
+
doc_type=(template.metadata or {}).get("doc_type") or "commercial_invoice",
|
|
158
|
+
doc_file=base64.b64encode(document).decode("utf-8"),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# -----------------------------------------------------------
|
|
163
|
+
# contexts data parsers
|
|
164
|
+
# -----------------------------------------------------------
|
|
165
|
+
# region
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_shipments_context(shipment_ids: str) -> typing.List[dict]:
|
|
169
|
+
if shipment_ids == "sample":
|
|
170
|
+
return [utils.SHIPMENT_SAMPLE]
|
|
171
|
+
|
|
172
|
+
ids = shipment_ids.split(",")
|
|
173
|
+
shipments = manager_models.Shipment.objects.filter(id__in=ids)
|
|
174
|
+
|
|
175
|
+
return [
|
|
176
|
+
dict(
|
|
177
|
+
shipment=manager_serializers.Shipment(shipment).data,
|
|
178
|
+
line_items=get_shipment_item_contexts(shipment),
|
|
179
|
+
carrier=get_carrier_context(shipment.selected_rate_carrier),
|
|
180
|
+
orders=orders_serializers.Order(
|
|
181
|
+
get_shipment_order_contexts(shipment), many=True
|
|
182
|
+
).data,
|
|
183
|
+
)
|
|
184
|
+
for shipment in shipments
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def get_shipment_item_contexts(shipment):
|
|
189
|
+
items = order_models.LineItem.objects.filter(
|
|
190
|
+
commodity_parcel__parcel_shipment=shipment
|
|
191
|
+
)
|
|
192
|
+
distinct_items = [
|
|
193
|
+
__ for _, __ in ({item.parent_id: item for item in items}).items()
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
**orders_serializers.LineItem(item.parent or item).data,
|
|
199
|
+
"ship_quantity": items.filter(parent_id=item.parent_id).aggregate(
|
|
200
|
+
Sum("quantity")
|
|
201
|
+
)["quantity__sum"],
|
|
202
|
+
"order": (orders_serializers.Order(item.order).data if item.order else {}),
|
|
203
|
+
}
|
|
204
|
+
for item in distinct_items
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_shipment_order_contexts(shipment):
|
|
209
|
+
return (
|
|
210
|
+
order_models.Order.objects.filter(
|
|
211
|
+
line_items__children__commodity_parcel__parcel_shipment=shipment
|
|
212
|
+
)
|
|
213
|
+
.order_by("-order_date")
|
|
214
|
+
.distinct()
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_carrier_context(carrier=None):
|
|
219
|
+
if carrier is None:
|
|
220
|
+
return {}
|
|
221
|
+
|
|
222
|
+
return carrier.data.to_dict()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def get_orders_context(order_ids: str) -> typing.List[dict]:
|
|
226
|
+
if order_ids == "sample":
|
|
227
|
+
return [utils.ORDER_SAMPLE]
|
|
228
|
+
|
|
229
|
+
ids = order_ids.split(",")
|
|
230
|
+
orders = order_models.Order.objects.filter(id__in=ids)
|
|
231
|
+
|
|
232
|
+
return [
|
|
233
|
+
dict(
|
|
234
|
+
order=orders_serializers.Order(order).data,
|
|
235
|
+
)
|
|
236
|
+
for order in orders
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# 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
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import django.urls as urls
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.core.validators import RegexValidator
|
|
5
|
+
|
|
6
|
+
import karrio.server.core.models as core
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@core.register_model
|
|
10
|
+
class DocumentTemplate(core.OwnedEntity):
|
|
11
|
+
class Meta:
|
|
12
|
+
db_table = "document-template"
|
|
13
|
+
verbose_name = "Document Template"
|
|
14
|
+
verbose_name_plural = "Document Templates"
|
|
15
|
+
ordering = ["-created_at"]
|
|
16
|
+
|
|
17
|
+
id = models.CharField(
|
|
18
|
+
max_length=50,
|
|
19
|
+
primary_key=True,
|
|
20
|
+
default=functools.partial(core.uuid, prefix="doc_"),
|
|
21
|
+
editable=False,
|
|
22
|
+
)
|
|
23
|
+
name = models.CharField(max_length=50, db_index=True)
|
|
24
|
+
slug = models.SlugField(
|
|
25
|
+
max_length=20,
|
|
26
|
+
validators=[RegexValidator(r"^[a-z0-9_]+$")],
|
|
27
|
+
db_index=True,
|
|
28
|
+
)
|
|
29
|
+
template = models.TextField()
|
|
30
|
+
description = models.CharField(
|
|
31
|
+
max_length=50,
|
|
32
|
+
null=True,
|
|
33
|
+
blank=True,
|
|
34
|
+
db_index=True,
|
|
35
|
+
)
|
|
36
|
+
related_object = models.CharField(max_length=25, blank=True, null=True)
|
|
37
|
+
active = models.BooleanField(
|
|
38
|
+
default=True,
|
|
39
|
+
help_text="disable template flag. to filter out from active document downloads",
|
|
40
|
+
)
|
|
41
|
+
metadata = models.JSONField(
|
|
42
|
+
blank=True,
|
|
43
|
+
null=True,
|
|
44
|
+
default=core.field_default({}),
|
|
45
|
+
)
|
|
46
|
+
options = models.JSONField(
|
|
47
|
+
blank=True,
|
|
48
|
+
null=True,
|
|
49
|
+
default=core.field_default({}),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def object_type(self):
|
|
54
|
+
return "document-template"
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def preview_url(self):
|
|
58
|
+
return urls.reverse(
|
|
59
|
+
"karrio.server.documents:templates-documents-print",
|
|
60
|
+
kwargs=dict(pk=self.pk, slug=self.slug),
|
|
61
|
+
) + f"?{self.related_object or 'shipment'}s=sample"
|