remove feature 'fee'

This commit is contained in:
2025-06-25 23:30:15 +02:00
parent 0ae2c86e90
commit ead98785de
9 changed files with 433 additions and 460 deletions

View File

@@ -15,12 +15,11 @@ from .forms import (
BankDataAdminForm, BankDataAdminForm,
BillAdminForm, BillAdminForm,
BillInlineForm, BillInlineForm,
FeeAdminForm,
ResolutionAdminForm, ResolutionAdminForm,
WirefAdminForm, WirefAdminForm,
) )
from .models import BankData, Bill, Fee, Resolution, Wiref from .models import BankData, Bill, Resolution, Wiref
from .utils import generate_fee_pdf, generate_pdf from .utils import generate_pdf
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -381,150 +380,150 @@ class BillAdmin(admin.ModelAdmin):
) )
@admin.register(Fee) # @admin.register(Fee)
class FeeAdmin(admin.ModelAdmin): # class FeeAdmin(admin.ModelAdmin):
form = FeeAdminForm # form = FeeAdminForm
model = Fee # model = Fee
list_display = ["id", "amount", "job", "fix_name_desc", "status_colored"] # list_display = ["id", "amount", "job", "fix_name_desc", "status_colored"]
list_filter = ["status"] # list_filter = ["status"]
show_facets = admin.ShowFacets.ALWAYS # show_facets = admin.ShowFacets.ALWAYS
ordering = ["-id"] # ordering = ["-id"]
readonly_fields = [ # readonly_fields = [
"address", # "address",
"get_qrcode", # "get_qrcode",
] # ]
fieldsets = ( # fieldsets = (
( # (
None, # None,
{ # {
"fields": ( # "fields": (
"fee_creator", # "fee_creator",
"bankdata", # "bankdata",
"address", # "address",
"get_qrcode", # "get_qrcode",
), # ),
}, # },
), # ),
( # (
"Tätigkeit", # "Tätigkeit",
{ # {
"fields": ( # "fields": (
"job", # "job",
"date_start", # "date_start",
"date_end", # "date_end",
"amount", # "amount",
), # ),
}, # },
), # ),
( # (
"Sonstiges", # "Sonstiges",
{ # {
"fields": ( # "fields": (
"comment", # "comment",
"status", # "status",
"file_field", # "file_field",
), # ),
}, # },
), # ),
) # )
def add_view(self, request, form_url="", extra_context=None): # def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {} # extra_context = extra_context or {}
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder." # extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
return super().add_view( # return super().add_view(
request, # request,
form_url, # form_url,
extra_context=extra_context, # extra_context=extra_context,
) # )
def change_view(self, request, object_id, form_url="", extra_context=None): # def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {} # extra_context = extra_context or {}
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder." # extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
extra_context["generate_fee_pdf"] = True # extra_context["generate_fee_pdf"] = True
return super().change_view( # return super().change_view(
request, # request,
object_id, # object_id,
form_url, # form_url,
extra_context=extra_context, # extra_context=extra_context,
) # )
def response_change(self, request, obj): # def response_change(self, request, obj):
if "_generate_fee_pdf" in request.POST: # if "_generate_fee_pdf" in request.POST:
if generate_fee_pdf(obj): # if generate_fee_pdf(obj):
self.message_user( # self.message_user(
request, # request,
"Neue Honorarnote wurde generiert.", # "Neue Honorarnote wurde generiert.",
messages.SUCCESS, # messages.SUCCESS,
) # )
else: # else:
self.message_user( # self.message_user(
request, # request,
( # (
"Das PDF-Dokument konnte nicht generiert werden, da der Status nicht auf " # "Das PDF-Dokument konnte nicht generiert werden, da der Status nicht auf "
"'Eingereicht' gesetzt ist." # "'Eingereicht' gesetzt ist."
), # ),
messages.WARNING, # messages.WARNING,
) # )
return HttpResponseRedirect(".") # return HttpResponseRedirect(".")
return super().response_change(request, obj) # return super().response_change(request, obj)
def save_model(self, request, obj, form, change): # def save_model(self, request, obj, form, change):
# set status to submitted, if a file exists and status is opened. # # set status to submitted, if a file exists and status is opened.
if ( # if (
change # change
and obj.file_field # and obj.file_field
and obj.status == Fee.Status.SUBMITTED # and obj.status == Fee.Status.SUBMITTED
and "_generate_fee_pdf" not in request.POST # and "_generate_fee_pdf" not in request.POST
): # ):
obj.status = Fee.Status.APPROVED # obj.status = Fee.Status.APPROVED
super().save_model(request, obj, form, change) # super().save_model(request, obj, form, change)
@admin.display(description="Adresse") # @admin.display(description="Adresse")
def address(self, obj): # def address(self, obj):
return obj.bankdata.address # return obj.bankdata.address
@admin.display(description="QR Code") # @admin.display(description="QR Code")
def get_qrcode(self, obj): # def get_qrcode(self, obj):
# QR Code is only set if status is approved. # # QR Code is only set if status is approved.
if obj.status != Fee.Status.APPROVED: # if obj.status != Fee.Status.APPROVED:
return "-" # return "-"
try: # try:
qrcode = helpers.make_epc_qr( # qrcode = helpers.make_epc_qr(
name=obj.bankdata.name, # name=obj.bankdata.name,
iban=obj.bankdata.iban, # iban=obj.bankdata.iban,
amount=obj.amount, # amount=obj.amount,
text=f"Honorarnote Nr.{obj.id}", # text=f"Honorarnote Nr.{obj.id}",
bic=obj.bankdata.bic, # bic=obj.bankdata.bic,
encoding="utf-8", # encoding="utf-8",
) # )
except Exception: # except Exception:
return "Daten für QR Code ungültig" # return "Daten für QR Code ungültig"
uri = qrcode.png_data_uri(scale=3.0) # uri = qrcode.png_data_uri(scale=3.0)
return format_html('<img src="{}">', uri) # return format_html('<img src="{}">', uri)
@admin.display(description="Name") # @admin.display(description="Name")
def fix_name_desc(self, obj): # def fix_name_desc(self, obj):
return obj.bankdata.name # return obj.bankdata.name
@admin.display(description="Status") # @admin.display(description="Status")
def status_colored(self, obj): # def status_colored(self, obj):
# TODO: if there is a status without color, set nothing. # # TODO: if there is a status without color, set nothing.
colors = { # colors = {
Fee.Status.SUBMITTED: "red", # Fee.Status.SUBMITTED: "red",
Fee.Status.APPROVED: "darkorange", # Fee.Status.APPROVED: "darkorange",
Fee.Status.PAYOUT: "green", # Fee.Status.PAYOUT: "green",
Fee.Status.CLEARED: "DarkMagenta", # Fee.Status.CLEARED: "DarkMagenta",
} # }
return format_html( # return format_html(
'<b style="background:{color};">{status}</b>', # '<b style="background:{color};">{status}</b>',
color=colors[obj.status], # color=colors[obj.status],
status=obj.get_status_display(), # status=obj.get_status_display(),
) # )
@admin.register(Resolution) @admin.register(Resolution)

View File

@@ -9,7 +9,7 @@ from django.forms import DateInput
from members.models import Member from members.models import Member
from .models import BankData, Bill, Fee, Resolution, Wiref from .models import BankData, Bill, Resolution, Wiref
class DateInput(DateInput): class DateInput(DateInput):
@@ -283,169 +283,169 @@ class BillUpdateForm(forms.ModelForm):
return get_cleaned_data(super().clean()) return get_cleaned_data(super().clean())
class FeeCreateForm(forms.ModelForm): # class FeeCreateForm(forms.ModelForm):
# Bank data # # Bank data
name_text = forms.CharField(required=True, label="Kontoinhaber:in", initial="", max_length=128) # name_text = forms.CharField(required=True, label="Kontoinhaber:in", initial="", max_length=128)
iban_text = forms.CharField(required=True, label="IBAN", initial="", max_length=34) # iban_text = forms.CharField(required=True, label="IBAN", initial="", max_length=34)
bic_text = forms.CharField(required=True, label="BIC", initial="", max_length=11) # bic_text = forms.CharField(required=True, label="BIC", initial="", max_length=11)
address_text = forms.CharField( # address_text = forms.CharField(
required=True, widget=forms.Textarea, label="Adresse", initial="" # required=True, widget=forms.Textarea, label="Adresse", initial=""
) # )
saving = forms.BooleanField( # saving = forms.BooleanField(
required=False, # required=False,
label="Bankdaten für die nächsten Rechnungen speichern.", # label="Bankdaten für die nächsten Rechnungen speichern.",
initial=False, # initial=False,
) # )
# Conformation # # Conformation
conformation = forms.BooleanField( # conformation = forms.BooleanField(
required=True, # required=True,
label=( # label=(
"Hiermit bestätige ich, dass mir die relevanten rechtlichen und steuerlichen " # "Hiermit bestätige ich, dass mir die relevanten rechtlichen und steuerlichen "
"Bestimmungen im Zusammenhang mit Honorarnoten bekannt sind. Ich verpflichte " # "Bestimmungen im Zusammenhang mit Honorarnoten bekannt sind. Ich verpflichte "
"mich, diese in Übereinstimmung mit den geltenden steuerlichen Vorschriften " # "mich, diese in Übereinstimmung mit den geltenden steuerlichen Vorschriften "
"ordnungsgemäß zu melden." # "ordnungsgemäß zu melden."
), # ),
initial=False, # initial=False,
) # )
class Meta: # class Meta:
model = Fee # model = Fee
fields = [ # fields = [
"fee_creator", # "fee_creator",
"job", # "job",
"date_start", # "date_start",
"date_end", # "date_end",
"amount", # "amount",
"comment", # "comment",
] # ]
help_texts = { # help_texts = {
"date_end": "Bei einer leeren Eingabe Eingabe wird automatisch das Startdatum gesetzt." # "date_end": "Bei einer leeren Eingabe Eingabe wird automatisch das Startdatum gesetzt."
} # }
labels = { # labels = {
"job": "Tätigkeitsbeschreibung", # "job": "Tätigkeitsbeschreibung",
} # }
widgets = { # widgets = {
"date_start": DateInput(format=("%Y-%m-%d")), # "date_start": DateInput(format=("%Y-%m-%d")),
"date_end": DateInput(format=("%Y-%m-%d")), # "date_end": DateInput(format=("%Y-%m-%d")),
} # }
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
user = kwargs.pop("user") if "user" in kwargs else None # user = kwargs.pop("user") if "user" in kwargs else None
super().__init__(*args, **kwargs) # to get the self.fields set # super().__init__(*args, **kwargs) # to get the self.fields set
member = Member.objects.get(username=user.username) # member = Member.objects.get(username=user.username)
self.fields["fee_creator"].initial = member # self.fields["fee_creator"].initial = member
self.fields["fee_creator"].disabled = True # self.fields["fee_creator"].disabled = True
self.fields["fee_creator"].required = True # self.fields["fee_creator"].required = True
self.fields["date_end"].required = False # self.fields["date_end"].required = False
self.fields["address_text"].placeholder = "Straße\nPLZ und Ort" # self.fields["address_text"].placeholder = "Straße\nPLZ und Ort"
# Bank data fields # # Bank data fields
bank_data = BankData.objects.filter( # bank_data = BankData.objects.filter(
Q(bankdata_creator=member) & Q(is_disabled=False), # Q(bankdata_creator=member) & Q(is_disabled=False),
).first() # ).first()
if bank_data: # if bank_data:
self.fields["name_text"].initial = bank_data.name # self.fields["name_text"].initial = bank_data.name
self.fields["iban_text"].initial = bank_data.iban # self.fields["iban_text"].initial = bank_data.iban
self.fields["bic_text"].initial = bank_data.bic # self.fields["bic_text"].initial = bank_data.bic
self.fields["address_text"].initial = bank_data.address # self.fields["address_text"].initial = bank_data.address
self.fields["saving"].initial = True # self.fields["saving"].initial = True
class FeeUpdateForm(forms.ModelForm): # class FeeUpdateForm(forms.ModelForm):
# Bank data # # Bank data
name_text = forms.CharField(required=False, label="Kontoinhaber:in", initial="", max_length=128) # name_text = forms.CharField(required=False, label="Kontoinhaber:in", initial="", max_length=128)
iban_text = forms.CharField(required=False, label="IBAN", initial="", max_length=34) # iban_text = forms.CharField(required=False, label="IBAN", initial="", max_length=34)
bic_text = forms.CharField(required=False, label="BIC", initial="", max_length=11) # bic_text = forms.CharField(required=False, label="BIC", initial="", max_length=11)
address_text = forms.CharField( # address_text = forms.CharField(
required=False, widget=forms.Textarea, label="Adresse", initial="" # required=False, widget=forms.Textarea, label="Adresse", initial=""
) # )
saving = forms.BooleanField( # saving = forms.BooleanField(
required=False, # required=False,
label="Bankdaten für die nächsten Rechnungen speichern.", # label="Bankdaten für die nächsten Rechnungen speichern.",
initial=False, # initial=False,
) # )
class Meta: # class Meta:
model = Fee # model = Fee
fields = [ # fields = [
"fee_creator", # "fee_creator",
"job", # "job",
"date_start", # "date_start",
"date_end", # "date_end",
"amount", # "amount",
"status", # "status",
"comment", # "comment",
] # ]
labels = { # labels = {
"job": "Tätigkeitsbeschreibung", # "job": "Tätigkeitsbeschreibung",
} # }
widgets = { # widgets = {
"date_start": DateInput(format=("%Y-%m-%d")), # "date_start": DateInput(format=("%Y-%m-%d")),
"date_end": DateInput(format=("%Y-%m-%d")), # "date_end": DateInput(format=("%Y-%m-%d")),
} # }
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set # super().__init__(*args, **kwargs) # to get the self.fields set
self.fields["fee_creator"].initial = kwargs["instance"].fee_creator # self.fields["fee_creator"].initial = kwargs["instance"].fee_creator
self.fields["fee_creator"].disabled = True # self.fields["fee_creator"].disabled = True
self.fields["fee_creator"].required = True # self.fields["fee_creator"].required = True
self.fields["status"].disabled = True # self.fields["status"].disabled = True
# Config for textarea of job. Calc rows for a better view. # # Config for textarea of job. Calc rows for a better view.
if (rows := kwargs["instance"].job.count("\n") + 1) < 3: # if (rows := kwargs["instance"].job.count("\n") + 1) < 3:
rows = 3 # rows = 3
self.fields["job"].rows = rows # self.fields["job"].rows = rows
self.fields["job"].disabled = True # self.fields["job"].disabled = True
self.fields["date_start"].disabled = True # self.fields["date_start"].disabled = True
self.fields["date_end"].disabled = True # self.fields["date_end"].disabled = True
self.fields["amount"].disabled = True # self.fields["amount"].disabled = True
# Bank data fields # # Bank data fields
if kwargs["instance"].bankdata: # if kwargs["instance"].bankdata:
self.fields["name_text"].initial = kwargs["instance"].bankdata.name # self.fields["name_text"].initial = kwargs["instance"].bankdata.name
self.fields["name_text"].required = True # self.fields["name_text"].required = True
self.fields["iban_text"].initial = kwargs["instance"].bankdata.iban # self.fields["iban_text"].initial = kwargs["instance"].bankdata.iban
self.fields["iban_text"].required = True # self.fields["iban_text"].required = True
self.fields["bic_text"].initial = kwargs["instance"].bankdata.bic # self.fields["bic_text"].initial = kwargs["instance"].bankdata.bic
self.fields["bic_text"].required = True # self.fields["bic_text"].required = True
self.fields["address_text"].initial = kwargs["instance"].bankdata.address # self.fields["address_text"].initial = kwargs["instance"].bankdata.address
self.fields["saving"].initial = not kwargs["instance"].bankdata.is_disabled # self.fields["saving"].initial = not kwargs["instance"].bankdata.is_disabled
self.fields["name_text"].disabled = True # self.fields["name_text"].disabled = True
self.fields["iban_text"].disabled = True # self.fields["iban_text"].disabled = True
self.fields["bic_text"].disabled = True # self.fields["bic_text"].disabled = True
self.fields["address_text"].disabled = True # self.fields["address_text"].disabled = True
self.fields["saving"].disabled = True # self.fields["saving"].disabled = True
# Config for textarea of comment. Calc rows for a better view. # # Config for textarea of comment. Calc rows for a better view.
rows = kwargs["instance"].comment.count("\n") + 1 # rows = kwargs["instance"].comment.count("\n") + 1
self.fields["comment"].rows = rows # self.fields["comment"].rows = rows
# Comment disabled when bill is cleared or finished # # Comment disabled when bill is cleared or finished
if kwargs["instance"].status != Bill.Status.SUBMITTED: # if kwargs["instance"].status != Bill.Status.SUBMITTED:
self.fields["comment"].disabled = True # self.fields["comment"].disabled = True
self.fields["comment"].autofocus = True # self.fields["comment"].autofocus = True
class ResolutionCreateForm(forms.ModelForm): class ResolutionCreateForm(forms.ModelForm):
@@ -603,26 +603,26 @@ class BillAdminForm(forms.ModelForm):
self.fields["wiref"].queryset = qs.order_by("-wiref_id") self.fields["wiref"].queryset = qs.order_by("-wiref_id")
class FeeAdminForm(forms.ModelForm): # class FeeAdminForm(forms.ModelForm):
class Meta: # class Meta:
model = Fee # model = Fee
fields = "__all__" # fields = "__all__"
help_texts = { # help_texts = {
"date_end": "Bei einer leeren Eingabe Eingabe wird automatisch das Startdatum gesetzt." # "date_end": "Bei einer leeren Eingabe Eingabe wird automatisch das Startdatum gesetzt."
} # }
widgets = { # widgets = {
"date": DateInput(format=("%Y-%m-%d")), # "date": DateInput(format=("%Y-%m-%d")),
} # }
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # super().__init__(*args, **kwargs)
self.fields["bankdata"].required = True # self.fields["bankdata"].required = True
self.fields["date_end"].required = False # self.fields["date_end"].required = False
class ResolutionAdminForm(forms.ModelForm): class ResolutionAdminForm(forms.ModelForm):

View File

@@ -1,5 +1,3 @@
from pathlib import Path
from django.core.validators import FileExtensionValidator, ValidationError from django.core.validators import FileExtensionValidator, ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
@@ -40,69 +38,69 @@ class BankData(models.Model):
self.bic = self.bic.replace(" ", "") self.bic = self.bic.replace(" ", "")
class Fee(models.Model): # class Fee(models.Model):
fee_creator = models.ForeignKey( # fee_creator = models.ForeignKey(
Member, # Member,
on_delete=models.PROTECT, # on_delete=models.PROTECT,
blank=True, # blank=True,
null=True, # null=True,
verbose_name="Verantwortliche:r", # verbose_name="Verantwortliche:r",
) # )
bankdata = models.ForeignKey( # bankdata = models.ForeignKey(
BankData, # BankData,
on_delete=models.SET_NULL, # on_delete=models.SET_NULL,
blank=True, # blank=True,
null=True, # null=True,
verbose_name="Bankdaten", # verbose_name="Bankdaten",
) # )
job = models.TextField(verbose_name="Tätigkeit") # job = models.TextField(verbose_name="Tätigkeit")
date_start = models.DateField(verbose_name="Start der Tätigkeit") # date_start = models.DateField(verbose_name="Start der Tätigkeit")
date_end = models.DateField(verbose_name="Ende der Tätigkeit") # date_end = models.DateField(verbose_name="Ende der Tätigkeit")
amount = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="Betrag (EUR)") # amount = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="Betrag (EUR)")
class Status(models.TextChoices): # class Status(models.TextChoices):
SUBMITTED = "S", "Eingereicht" # SUBMITTED = "S", "Eingereicht"
APPROVED = "A", "Für Überweisung freigegeben" # APPROVED = "A", "Für Überweisung freigegeben"
PAYOUT = "P", "Ausbezahlt" # PAYOUT = "P", "Ausbezahlt"
CLEARED = "C", "An Dekanat verrechnet" # CLEARED = "C", "An Dekanat verrechnet"
status = models.CharField( # status = models.CharField(
max_length=1, # max_length=1,
choices=Status.choices, # choices=Status.choices,
default=Status.SUBMITTED, # default=Status.SUBMITTED,
verbose_name="Status", # verbose_name="Status",
) # )
date_created = models.DateTimeField(auto_now_add=True) # date_created = models.DateTimeField(auto_now_add=True)
file_field = models.FileField( # file_field = models.FileField(
upload_to="uploads/finance/fee/", # upload_to="uploads/finance/fee/",
validators=[FileExtensionValidator(["pdf"])], # validators=[FileExtensionValidator(["pdf"])],
blank=True, # blank=True,
null=True, # null=True,
verbose_name="Honorarnote", # verbose_name="Honorarnote",
) # )
comment = models.TextField(blank=True, default="", verbose_name="Kommentar") # comment = models.TextField(blank=True, default="", verbose_name="Kommentar")
class Meta: # class Meta:
verbose_name = "Honorar" # verbose_name = "Honorar"
verbose_name_plural = "Honorare" # verbose_name_plural = "Honorare"
def __str__(self): # def __str__(self):
return f"Honorar #{self.id} / {self.job}" # return f"Honorar #{self.id} / {self.job}"
def save(self, *args, **kwargs): # def save(self, *args, **kwargs):
if not self.date_end: # if not self.date_end:
self.date_end = self.date_start # self.date_end = self.date_start
super().save(*args, **kwargs) # super().save(*args, **kwargs)
@property # @property
def filename(self): # def filename(self):
return Path(self.file_field.name).name # return Path(self.file_field.name).name
class Resolution(models.Model): class Resolution(models.Model):

View File

@@ -6,9 +6,6 @@ from .views import (
BillCreateView, BillCreateView,
BillListView, BillListView,
BillUpdateView, BillUpdateView,
FeeCreateDoneView,
FeeCreateView,
FeeUpdateView,
ResolutionCreateView, ResolutionCreateView,
ResolutionDetailView, ResolutionDetailView,
ResolutionListView, ResolutionListView,
@@ -27,14 +24,14 @@ urlpatterns = [
BillCreateDoneView.as_view(), BillCreateDoneView.as_view(),
name="bill_create_done", name="bill_create_done",
), ),
# Fee views # # Fee views
path("create-fee/", FeeCreateView.as_view(), name="fee_create"), # path("create-fee/", FeeCreateView.as_view(), name="fee_create"),
path( # path(
"create-fee/<int:pk>/done/", # "create-fee/<int:pk>/done/",
FeeCreateDoneView.as_view(), # FeeCreateDoneView.as_view(),
name="fee_create_done", # name="fee_create_done",
), # ),
path("fee/<int:pk>/", FeeUpdateView.as_view(), name="fee_update"), # path("fee/<int:pk>/", FeeUpdateView.as_view(), name="fee_update"),
# Resolution views # Resolution views
path("create-resolution/", ResolutionCreateView.as_view(), name="resolution_create"), path("create-resolution/", ResolutionCreateView.as_view(), name="resolution_create"),
path("resolutions/", ResolutionListView.as_view(), name="resolution_list"), path("resolutions/", ResolutionListView.as_view(), name="resolution_list"),

View File

@@ -6,7 +6,7 @@ from django.core.files import File
from pypdf import PdfReader, PdfWriter from pypdf import PdfReader, PdfWriter
from pypdf.constants import FieldDictionaryAttributes as FA # noqa: N814 from pypdf.constants import FieldDictionaryAttributes as FA # noqa: N814
from .models import Bill, Fee, Wiref from .models import Bill, Wiref
def generate_pdf(wiref): def generate_pdf(wiref):
@@ -82,56 +82,56 @@ def generate_pdf(wiref):
return True return True
def generate_fee_pdf(fee: Fee): # def generate_fee_pdf(fee: Fee):
if not fee or fee.status != Fee.Status.SUBMITTED: # if not fee or fee.status != Fee.Status.SUBMITTED:
return False # return False
# Get data for pdf # # Get data for pdf
data = {} # data = {}
data.update( # data.update(
{ # {
"Full_Name": fee.bankdata.name, # "Full_Name": fee.bankdata.name,
"Adresse": fee.bankdata.address, # "Adresse": fee.bankdata.address,
# Change to the correct date format # # Change to the correct date format
"Date": str(fee.date_created.strftime("%d.%m.%Y")), # "Date": str(fee.date_created.strftime("%d.%m.%Y")),
"Honorarnoten-Nummer": str(fee.pk), # "Honorarnoten-Nummer": str(fee.pk),
"Taetigkeit_1": fee.job, # "Taetigkeit_1": fee.job,
# Change to the correct date format # # Change to the correct date format
"Date_1": str(fee.date_start.strftime("%d.%m.%Y")), # "Date_1": str(fee.date_start.strftime("%d.%m.%Y")),
# Change to the correct date format # # Change to the correct date format
"Date_2": str(fee.date_end.strftime("%d.%m.%Y")), # "Date_2": str(fee.date_end.strftime("%d.%m.%Y")),
# Replace decimal separator from '.' to ',' # # Replace decimal separator from '.' to ','
"EUR_1": str(fee.amount).replace(".", ","), # "EUR_1": str(fee.amount).replace(".", ","),
"IBAN": fee.bankdata.iban, # "IBAN": fee.bankdata.iban,
"BIC": fee.bankdata.bic, # "BIC": fee.bankdata.bic,
}, # },
) # )
# Add mail only if a fet user create the fee # # Add mail only if a fet user create the fee
if fee.fee_creator: # if fee.fee_creator:
mail = fee.fee_creator.mailaccount # mail = fee.fee_creator.mailaccount
data.update( # data.update(
{ # {
"Email": mail, # "Email": mail,
}, # },
) # )
# Write data in pdf # # Write data in pdf
pdf_path = Path(Path(__file__).parent) / "static/fee/Honorarnote-Vorlage.pdf" # pdf_path = Path(Path(__file__).parent) / "static/fee/Honorarnote-Vorlage.pdf"
reader = PdfReader(pdf_path) # reader = PdfReader(pdf_path)
writer = PdfWriter() # writer = PdfWriter()
writer.append(reader) # writer.append(reader)
writer.update_page_form_field_values( # writer.update_page_form_field_values(
writer.pages[0], # writer.pages[0],
data, # data,
) # )
with io.BytesIO() as bytes_stream: # with io.BytesIO() as bytes_stream:
writer.write(bytes_stream) # writer.write(bytes_stream)
# Save pdf in fee # # Save pdf in fee
fee_name = f"Honorarnote-{fee.pk}.pdf" # fee_name = f"Honorarnote-{fee.pk}.pdf"
fee.file_field.save(fee_name, File(bytes_stream, fee_name)) # fee.file_field.save(fee_name, File(bytes_stream, fee_name))
return True # return True

View File

@@ -1,5 +1,5 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.db.models import CharField, F, Q, Value from django.db.models import CharField, Q, Value
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.views.generic import ListView, TemplateView from django.views.generic import ListView, TemplateView
@@ -12,12 +12,10 @@ from posts.models import FetMeeting
from .forms import ( from .forms import (
BillCreateForm, BillCreateForm,
BillUpdateForm, BillUpdateForm,
FeeCreateForm,
FeeUpdateForm,
ResolutionCreateForm, ResolutionCreateForm,
ResolutionUpdateForm, ResolutionUpdateForm,
) )
from .models import BankData, Bill, Fee, Resolution from .models import BankData, Bill, Resolution
def set_bankdata( def set_bankdata(
@@ -91,26 +89,26 @@ class BillListView(LoginRequiredMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["fee_status"] = Fee.Status # context["fee_status"] = Fee.Status
context["bill_status"] = Bill.Status context["bill_status"] = Bill.Status
return context return context
def get_queryset(self): def get_queryset(self):
qs1 = ( # qs1 = (
Fee.objects.filter(fee_creator__username=self.request.user) # Fee.objects.filter(fee_creator__username=self.request.user)
.values("amount", "status", "id") # .values("amount", "status", "id")
.annotate( # .annotate(
date=F("date_start"), purpose=F("job"), model=Value("FEE", output_field=CharField()) # date=F("date_start"), purpose=F("job"), model=Value("FEE", output_field=CharField())
) # )
) # )
qs2 = ( qs = (
Bill.objects.filter(bill_creator__username=self.request.user) Bill.objects.filter(bill_creator__username=self.request.user)
.values("amount", "status", "id", "date", "purpose") .values("amount", "status", "id", "date", "purpose")
.annotate(model=Value("BILL", output_field=CharField())) .annotate(model=Value("BILL", output_field=CharField()))
) )
qs = qs1.union(qs2, all=True) # qs = qs1.union(qs2, all=True)
return qs.order_by("-date", "purpose") return qs.order_by("-date", "purpose")
@@ -148,55 +146,55 @@ class BillUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
return redirect("finance:bill_list") return redirect("finance:bill_list")
class FeeCreateView(LoginRequiredMixin, CreateView): # class FeeCreateView(LoginRequiredMixin, CreateView):
form_class = FeeCreateForm # form_class = FeeCreateForm
model = Fee # model = Fee
template_name = "finance/fee/create.html" # template_name = "finance/fee/create.html"
def form_valid(self, form): # def form_valid(self, form):
# Get or create bankdata object. # # Get or create bankdata object.
creator = form.cleaned_data["fee_creator"] # creator = form.cleaned_data["fee_creator"]
name = form.cleaned_data["name_text"] # name = form.cleaned_data["name_text"]
iban = form.cleaned_data["iban_text"] # iban = form.cleaned_data["iban_text"]
bic = form.cleaned_data["bic_text"] # bic = form.cleaned_data["bic_text"]
address = form.cleaned_data["address_text"] # address = form.cleaned_data["address_text"]
saving = form.cleaned_data["saving"] # saving = form.cleaned_data["saving"]
form.instance.bankdata = set_bankdata(creator, name, iban, bic, saving, address) # form.instance.bankdata = set_bankdata(creator, name, iban, bic, saving, address)
add_log_action(self.request, form, "finance", "fee", True) # add_log_action(self.request, form, "finance", "fee", True)
return super().form_valid(form) # return super().form_valid(form)
def get_form_kwargs(self): # def get_form_kwargs(self):
kwargs = super().get_form_kwargs() # kwargs = super().get_form_kwargs()
# Request user for fee creator. # # Request user for fee creator.
kwargs["user"] = self.request.user # kwargs["user"] = self.request.user
return kwargs # return kwargs
def get_success_url(self): # def get_success_url(self):
return reverse("finance:fee_create_done", kwargs={"pk": self.object.pk}) # return reverse("finance:fee_create_done", kwargs={"pk": self.object.pk})
class FeeCreateDoneView(LoginRequiredMixin, TemplateView): # class FeeCreateDoneView(LoginRequiredMixin, TemplateView):
template_name = "finance/fee/create_done.html" # template_name = "finance/fee/create_done.html"
class FeeUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): # class FeeUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
form_class = FeeUpdateForm # form_class = FeeUpdateForm
model = Fee # model = Fee
success_url = reverse_lazy("finance:bill_list") # success_url = reverse_lazy("finance:bill_list")
template_name = "finance/fee/update.html" # template_name = "finance/fee/update.html"
def form_valid(self, form): # def form_valid(self, form):
add_log_action(self.request, form, "finance", "fee", False) # add_log_action(self.request, form, "finance", "fee", False)
return super().form_valid(form) # return super().form_valid(form)
# Call fee if it's only yours. # # Call fee if it's only yours.
def test_func(self): # def test_func(self):
return self.get_object().fee_creator.username == self.request.user.username # return self.get_object().fee_creator.username == self.request.user.username
def handle_no_permission(self): # def handle_no_permission(self):
return redirect("finance:bill_list") # return redirect("finance:bill_list")
class ResolutionCreateView(LoginRequiredMixin, CreateView): class ResolutionCreateView(LoginRequiredMixin, CreateView):

View File

@@ -7,6 +7,5 @@
<a href="{% add_preserved_filters changelist_url %}" class="closelink">{% translate 'Close' %}</a> <a href="{% add_preserved_filters changelist_url %}" class="closelink">{% translate 'Close' %}</a>
{% endif %} {% endif %}
{% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %} {% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %}
{% if generate_fee_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_fee_pdf">{% endif %}
{{ block.super }} {{ block.super }}
{% endblock submit-row %} {% endblock submit-row %}

View File

@@ -39,23 +39,11 @@
{% elif result.status == bill_status.FINISHED %} {% elif result.status == bill_status.FINISHED %}
<span class="badge badge-success">{{ bill_status.FINISHED.label }}</span> <span class="badge badge-success">{{ bill_status.FINISHED.label }}</span>
{% endif %} {% endif %}
{% elif result.model == "FEE" %}
{% if result.status == fee_status.SUBMITTED %}
<span class="badge badge-info">{{ fee_status.SUBMITTED.label }}</span>
{% elif result.status == fee_status.APPROVED %}
<span class="badge badge-warning">{{ fee_status.APPROVED.label }}</span>
{% elif result.status == fee_status.PAYOUT %}
<span class="badge badge-success">{{ fee_status.PAYOUT.label }}</span>
{% elif result.status == fee_status.CLEARED %}
<span class="badge badge-success">{{ fee_status.CLEARED.label }}</span>
{% endif %}
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if result.model == "BILL" %} {% if result.model == "BILL" %}
<a href="{% url 'finance:bill_update' result.id %}" class="btn btn-small btn-tertiary"><i class="fa-solid fa-pen-to-square" aria-label="Bearbeiten" title="Bearbeiten"></i></a> <a href="{% url 'finance:bill_update' result.id %}" class="btn btn-small btn-tertiary"><i class="fa-solid fa-pen-to-square" aria-label="Bearbeiten" title="Bearbeiten"></i></a>
{% elif result.model == "FEE" %}
<a href="{% url 'finance:fee_update' result.id %}" class="btn btn-small btn-tertiary"><i class="fa-solid fa-pen-to-square" aria-label="Bearbeiten" title="Bearbeiten"></i></a>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@@ -49,12 +49,6 @@
<span class="text-sm font-medium">Neue Rechnung einreichen</span> <span class="text-sm font-medium">Neue Rechnung einreichen</span>
</a> </a>
</li> </li>
<li>
<a href="{% url 'finance:fee_create' %}" class="flex items-center py-2 px-5 hover:bg-gray-100 dark:hover:bg-gray-600 hover:text-gray-900 dark:hover:text-white">
<i class="fa-solid fa-plus mr-2"></i>
<span class="text-sm font-medium">Neue Honorarnote einreichen</span>
</a>
</li>
<li> <li>
<a href="{% url 'finance:resolution_create' %}" class="flex items-center py-2 px-5 hover:bg-gray-100 dark:hover:bg-gray-600 hover:text-gray-900 dark:hover:text-white"> <a href="{% url 'finance:resolution_create' %}" class="flex items-center py-2 px-5 hover:bg-gray-100 dark:hover:bg-gray-600 hover:text-gray-900 dark:hover:text-white">
<i class="fa-solid fa-plus mr-2"></i> <i class="fa-solid fa-plus mr-2"></i>