diff --git a/jdav_web/contrib/media.py b/jdav_web/contrib/media.py
index 4ff27c9..d5e873b 100644
--- a/jdav_web/contrib/media.py
+++ b/jdav_web/contrib/media.py
@@ -1,9 +1,20 @@
import os
from django.conf import settings
from django.http import HttpResponse
+from django import template
+from django.template.loader import get_template
from wsgiref.util import FileWrapper
+def find_template(template_name):
+ for engine in template.engines.all():
+ for loader in engine.engine.template_loaders:
+ for origin in loader.get_template_sources(template_name):
+ if os.path.exists(origin.name):
+ return origin.name
+ raise template.TemplateDoesNotExist(f"Could not find template: {template_name}")
+
+
def media_path(fp):
return os.path.join(os.path.join(settings.MEDIA_ROOT, "memberlists"), fp)
diff --git a/jdav_web/locale/de/LC_MESSAGES/django.po b/jdav_web/locale/de/LC_MESSAGES/django.po
index cafea14..4f805db 100644
--- a/jdav_web/locale/de/LC_MESSAGES/django.po
+++ b/jdav_web/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-02-01 14:54+0100\n"
+"POT-Creation-Date: 2025-02-02 00:45+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -260,6 +260,10 @@ msgstr "SJR Antrag erstellen"
msgid "Generate seminar report"
msgstr "Landesjugendplan Antrag erstellen"
+#: templates/admin/members/freizeit/change_form_object_tools.html
+msgid "Generate LJP V-BK form"
+msgstr "Erzeuge LJP V-BK Formular"
+
#: templates/admin/members/freizeit/change_form_object_tools.html
msgid "Generate overview"
msgstr "Hinweise für Jugendleiter*innen erstellen"
diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py
index 2ca8b50..548bd5f 100644
--- a/jdav_web/members/admin.py
+++ b/jdav_web/members/admin.py
@@ -29,7 +29,7 @@ from django.forms import Textarea, RadioSelect, TypedChoiceField, CheckboxInput
from django.shortcuts import render
from django.core.exceptions import PermissionDenied, ValidationError
from .pdf import render_tex, fill_pdf_form, merge_pdfs, serve_pdf
-from .excel import generate_group_overview
+from .excel import generate_group_overview, VBK_3_1, VBK_3_2, generate_ljp_vbk
from contrib.admin import CommonAdminInlineMixin, CommonAdminMixin
@@ -844,7 +844,7 @@ class GroupAdmin(CommonAdminMixin, admin.ModelAdmin):
class ActivityCategoryAdmin(admin.ModelAdmin):
- fields = ['name', 'description']
+ fields = ['name', 'ljp_category', 'description']
class FreizeitAdminForm(forms.ModelForm):
@@ -1045,6 +1045,13 @@ class GenerateSeminarReportForm(forms.Form):
widget=CheckboxInput(attrs={'style': 'display: inherit'}),
required=False)
+
+class GenerateVBKForm(forms.Form):
+ categories = ((VBK_3_1, _('Staff training')),
+ (VBK_3_2, _('Educational programme')))
+ category = forms.ChoiceField(choices=categories, label=_('Category'))
+
+
class GenerateSjrForm(forms.Form):
def __init__(self, *args, **kwargs):
@@ -1052,7 +1059,6 @@ class GenerateSjrForm(forms.Form):
super(GenerateSjrForm,self).__init__(*args,**kwargs)
self.fields['invoice'] = forms.ChoiceField(choices=self.attachments, label=_('Invoice'))
-
class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
@@ -1116,6 +1122,29 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
return render_tex(memberlist.name + "_Notizen", 'members/notes_list.tex', context)
notes_list.short_description = _('Generate overview')
+ def render_seminar_vbk_options(self, request, memberlist, form):
+ context = dict(self.admin_site.each_context(request),
+ title=_('Generate LJP V-BK form'),
+ opts=self.opts,
+ memberlist=memberlist,
+ form=form,
+ object=memberlist)
+ return render(request, 'admin/generate_seminar_vbk.html', context=context)
+
+ def seminar_vbk(self, request, memberlist):
+ if not self.may_view_excursion(request, memberlist):
+ return self.not_allowed_view(request, memberlist)
+ if "apply" in request.POST:
+ form = GenerateVBKForm(request.POST)
+ if not form.is_valid():
+ messages.error(request, _('Please select a category.'))
+ return self.render_seminar_vbk_options(request, memberlist, form)
+ category = int(form.cleaned_data['category'])
+ title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name
+ fp = generate_ljp_vbk(memberlist, category)
+ return serve_media(fp, 'application/xlsx')
+ return self.render_seminar_vbk_options(request, memberlist, GenerateVBKForm())
+
def render_seminar_report_options(self, request, memberlist, form):
context = dict(self.admin_site.each_context(request),
title=_('Generate seminar report'),
@@ -1233,6 +1262,8 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
def action_view(self, request, object_id):
if "sjr_application" in request.POST:
return self.sjr_application(request, Freizeit.objects.get(pk=object_id))
+ if "seminar_vbk" in request.POST:
+ return self.seminar_vbk(request, Freizeit.objects.get(pk=object_id))
if "seminar_report" in request.POST:
return self.seminar_report(request, Freizeit.objects.get(pk=object_id))
if "notes_list" in request.POST:
diff --git a/jdav_web/members/excel.py b/jdav_web/members/excel.py
index 6fa8ff7..4b9e83d 100644
--- a/jdav_web/members/excel.py
+++ b/jdav_web/members/excel.py
@@ -1,8 +1,9 @@
from datetime import datetime
import os
import xlsxwriter
+import openpyxl
from django.conf import settings
-from contrib.media import media_path
+from contrib.media import media_path, find_template
from .models import WEEKDAYS
def generate_group_overview(all_groups, limit_to_public = True):
@@ -67,3 +68,49 @@ def generate_group_overview(all_groups, limit_to_public = True):
workbook.close()
return filename
+
+
+VBK_3_1, VBK_3_2 = 1, 2
+VBK_TEMPLATES = {
+ VBK_3_1: 'members/LJP_VBK_3-1.xlsx',
+ VBK_3_2: 'members/LJP_VBK_3-2.xlsx',
+}
+
+
+def generate_ljp_vbk(excursion, mode):
+ """
+ Generate the VBK forms for LJP given an excursion. Returns the filename to the filled excel file.
+ """
+ print(mode, VBK_TEMPLATES, mode in VBK_TEMPLATES)
+ if not mode in VBK_TEMPLATES:
+ raise ValueError(f"Invalid mode {mode}.")
+ template_path = VBK_TEMPLATES[mode]
+ path = find_template(template_path)
+ workbook = openpyxl.load_workbook(path)
+
+ sheet = workbook.active
+ title = excursion.ljpproposal.title if hasattr(excursion, 'ljpproposal') else excursion.name
+
+ sheet['I6'] = settings.SEKTION_IBAN
+ sheet['I8'] = settings.SEKTION_ACCOUNT_HOLDER
+ sheet['P3'] = excursion.end.year
+ sheet['B4'] = f"Sektion {settings.SEKTION}"
+ sheet['B5'] = settings.SEKTION_STREET
+ sheet['B6'] = settings.SEKTION_TOWN
+ sheet['B7'] = settings.RESPONSIBLE_MAIL
+ sheet['B36'] = f"{settings.SEKTION}, {datetime.today():%d.%m.%Y}"
+ sheet['F19'] = f"B {excursion.date:%y}-{excursion.pk}"
+ sheet['D19'] = settings.SEKTION
+ sheet['G19'] = title
+ sheet['I19'] = f"von {excursion.date:%d.%m.%y} bis {excursion.end:%d.%m.%y}"
+ sheet['J19'] = f"{excursion.duration}"
+ sheet['L19'] = f"{excursion.ljp_participant_count}"
+ sheet['H19'] = excursion.get_ljp_activity_category()
+ sheet['M19'] = excursion.place
+
+ if hasattr(excursion, 'statement'):
+ sheet['Q19'] = f"{excursion.statement.total_theoretic}"
+
+ filename = f"LJP_V-BK_3.{mode}_{title}.xlsx"
+ workbook.save(media_path(filename))
+ return filename
diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po
index fc719e6..cefb895 100644
--- a/jdav_web/members/locale/de/LC_MESSAGES/django.po
+++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-02-02 00:32+0100\n"
+"POT-Creation-Date: 2025-02-02 00:45+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -351,6 +351,18 @@ msgstr "Modus"
msgid "Prepend V32"
msgstr "V32 Formblatt einfügen"
+#: members/admin.py
+msgid "Staff training"
+msgstr "Jugendleiter*innenweiterbildung"
+
+#: members/admin.py
+msgid "Educational programme"
+msgstr "Themenorientierte Bildungsmaßnahme"
+
+#: members/admin.py members/models.py
+msgid "Category"
+msgstr "Kategorie"
+
#: members/admin.py
msgid "Invoice"
msgstr "Beleg"
@@ -379,6 +391,14 @@ msgstr "Kriseninterventionsliste erstellen"
msgid "Generate overview"
msgstr "Hinweise für Jugendleiter erstellen"
+#: members/admin.py members/templates/admin/generate_seminar_vbk.html
+msgid "Generate LJP V-BK form"
+msgstr "Erzeuge LJP V-BK Formular"
+
+#: members/admin.py
+msgid "Please select a category."
+msgstr "Bitte wähle eine Kategorie aus."
+
#: members/admin.py members/templates/admin/generate_seminar_report.html
msgid "Generate seminar report"
msgstr "Landesjugendplan Antrag erstellen"
@@ -491,8 +511,7 @@ msgstr "LJP Spielart"
#: members/models.py
msgid ""
"The official category for LJP applications associated with this activity."
-msgstr ""
-"Die offizielle Spielart für LJP Anträge mit dieser Aktivität."
+msgstr "Die offizielle Spielart für LJP Anträge mit dieser Aktivität."
#: members/models.py
msgid "Description"
@@ -1046,10 +1065,6 @@ msgstr "Fortbildungstyp"
msgid "Training categories"
msgstr "Fortbildungstypen"
-#: members/models.py
-msgid "Category"
-msgstr "Kategorien"
-
#: members/models.py
msgid "Comments"
msgstr "Kommentar"
@@ -1073,6 +1088,7 @@ msgstr "Fortbildungen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
@@ -1098,6 +1114,7 @@ msgstr "Zurück auf die Warteliste setzen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
@@ -1389,10 +1406,36 @@ msgstr ""
"Felder im Formblatt selbst aus und unterschreibe das PDF."
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_seminar_vbk.html
#: members/templates/admin/generate_sjr_application.html
msgid "Generate"
msgstr "Erstellen"
+#: members/templates/admin/generate_seminar_vbk.html
+msgid ""
+"Every LJP application needs a V-BK form containing the most important facts "
+"about the seminar.\n"
+"Here you can automatically generate such a form in Excel format."
+msgstr ""
+"Jeder LJP Antrag benötigt ein V-BK Formular, das die wichtigsten Randdaten "
+"des Seminars enthält. Hier kannst du automatisch ein solches Formular im "
+"Excel Format erstellen."
+
+#: members/templates/admin/generate_seminar_vbk.html
+msgid ""
+"Your excursion currently has no cost-plan attached, hence the total costs "
+"can't be automatically\n"
+"calculated and added to the form."
+msgstr ""
+"Deine Ausfahrt hat zur Zeit keinen Kostenplan. Daher können die Gesamtkosten "
+"nicht automatisch berechnet und dem Formular hinzugefügt werden."
+
+#: members/templates/admin/generate_seminar_vbk.html
+msgid ""
+"Depending on the type of seminar, please select one of the two options below."
+msgstr ""
+"Bitte wähle aus, um welche Art von Seminar es sich handelt."
+
#: members/templates/admin/generate_sjr_application.html members/tests.py
msgid "Here you can generate an allowance application for the SJR."
msgstr "Hier kannst du einen SJR-Zuschussantrag erstellen."
diff --git a/jdav_web/members/pdf.py b/jdav_web/members/pdf.py
index ff43d94..9ffb88a 100644
--- a/jdav_web/members/pdf.py
+++ b/jdav_web/members/pdf.py
@@ -11,19 +11,10 @@ from django.template.loader import get_template
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect
from wsgiref.util import FileWrapper
-from contrib.media import media_path, media_dir, serve_media, ensure_media_dir
+from contrib.media import media_path, media_dir, serve_media, ensure_media_dir, find_template
from PIL import Image
-def find_template(template_name):
- for engine in template.engines.all():
- for loader in engine.engine.template_loaders:
- for origin in loader.get_template_sources(template_name):
- if os.path.exists(origin.name):
- return origin.name
- raise template.TemplateDoesNotExist(f"Could not find template: {template_name}")
-
-
def serve_pdf(filename_pdf):
return serve_media(filename_pdf, 'application/pdf')
diff --git a/jdav_web/members/templates/admin/generate_seminar_vbk.html b/jdav_web/members/templates/admin/generate_seminar_vbk.html
new file mode 100644
index 0000000..f028989
--- /dev/null
+++ b/jdav_web/members/templates/admin/generate_seminar_vbk.html
@@ -0,0 +1,56 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_urls static %}
+
+{% block extrahead %}
+ {{ block.super }}
+ {{ media }}
+
+
+
+{% endblock %}
+
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter
+{% endblock %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+
+{% blocktrans %}Every LJP application needs a V-BK form containing the most important facts about the seminar.
+Here you can automatically generate such a form in Excel format.{% endblocktrans %}
+
+{% if not memberlist.statement %}
+
+{% blocktrans %}Your excursion currently has no cost-plan attached, hence the total costs can't be automatically
+calculated and added to the form.{% endblocktrans %}
+
+{% endif %}
+
+{% blocktrans %}Depending on the type of seminar, please select one of the two options below.{% endblocktrans %}
+
+
+
+
+
+
+
+ {% translate "Cancel" %}
+
+
+{% endblock %}
diff --git a/jdav_web/members/templates/members/LJP_VBK_3-1.xlsx b/jdav_web/members/templates/members/LJP_VBK_3-1.xlsx
new file mode 100644
index 0000000..a02224a
Binary files /dev/null and b/jdav_web/members/templates/members/LJP_VBK_3-1.xlsx differ
diff --git a/jdav_web/members/templates/members/LJP_VBK_3-2.xlsx b/jdav_web/members/templates/members/LJP_VBK_3-2.xlsx
new file mode 100644
index 0000000..1b21d3d
Binary files /dev/null and b/jdav_web/members/templates/members/LJP_VBK_3-2.xlsx differ
diff --git a/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html b/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html
index f27115c..c52ac75 100644
--- a/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html
+++ b/jdav_web/templates/admin/members/freizeit/change_form_object_tools.html
@@ -24,6 +24,13 @@
+
+
+
+