291 lines
8.0 KiB
Python
291 lines
8.0 KiB
Python
from pathlib import Path
|
|
|
|
from django.core.validators import FileExtensionValidator, ValidationError
|
|
from django.db import models
|
|
from django.urls import reverse
|
|
|
|
from members.models import Member
|
|
|
|
from .validators import validate_bill_file_extension
|
|
|
|
|
|
class BankData(models.Model):
|
|
# members can be deleted but never their bank datas
|
|
bankdata_creator = models.ForeignKey(
|
|
Member,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
verbose_name="Verknüpfung zum Mitglied",
|
|
)
|
|
name = models.CharField(max_length=128, verbose_name="Kontoinhaber:in")
|
|
iban = models.CharField(max_length=34, verbose_name="IBAN")
|
|
bic = models.CharField(max_length=11, verbose_name="BIC")
|
|
|
|
address = models.TextField(blank=True, verbose_name="Adresse")
|
|
|
|
is_disabled = models.BooleanField(default=False, verbose_name="deaktiviert")
|
|
|
|
class Meta:
|
|
verbose_name = "Bankdaten"
|
|
verbose_name_plural = "Bankdaten"
|
|
|
|
def __str__(self):
|
|
return f"{self.name} - {self.iban}"
|
|
|
|
def clean(self):
|
|
if self.iban:
|
|
self.iban = self.iban.replace(" ", "")
|
|
|
|
if self.bic:
|
|
self.bic = self.bic.replace(" ", "")
|
|
|
|
|
|
class Fee(models.Model):
|
|
fee_creator = models.ForeignKey(
|
|
Member,
|
|
on_delete=models.PROTECT,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Verantwortliche:r",
|
|
)
|
|
|
|
bankdata = models.ForeignKey(
|
|
BankData,
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Bankdaten",
|
|
)
|
|
|
|
job = models.TextField(verbose_name="Tätigkeit")
|
|
date_start = models.DateField(verbose_name="Start 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)")
|
|
|
|
class Status(models.TextChoices):
|
|
SUBMITTED = "S", "Eingereicht"
|
|
APPROVED = "A", "Für Überweisung freigegeben"
|
|
PAYOUT = "P", "Ausbezahlt"
|
|
CLEARED = "C", "An Dekanat verrechnet"
|
|
|
|
status = models.CharField(
|
|
max_length=1,
|
|
choices=Status.choices,
|
|
default=Status.SUBMITTED,
|
|
verbose_name="Status",
|
|
)
|
|
|
|
date_created = models.DateTimeField(auto_now_add=True)
|
|
|
|
file_field = models.FileField(
|
|
upload_to="uploads/finance/fee/",
|
|
validators=[FileExtensionValidator(["pdf"])],
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Honorarnote",
|
|
)
|
|
|
|
comment = models.TextField(blank=True, default="", verbose_name="Kommentar")
|
|
|
|
class Meta:
|
|
verbose_name = "Honorar"
|
|
verbose_name_plural = "Honorare"
|
|
|
|
def __str__(self):
|
|
return f"Honorar #{self.id} / {self.job}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.date_end:
|
|
self.date_end = self.date_start
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
@property
|
|
def filename(self):
|
|
return Path(self.file_field.name).name
|
|
|
|
|
|
class Resolution(models.Model):
|
|
id = models.CharField(primary_key=True, max_length=128, verbose_name="Beschlussnummer")
|
|
name = models.CharField(max_length=128, verbose_name="Bezeichnung")
|
|
|
|
is_visible = models.BooleanField(default=False, verbose_name="sichtbar")
|
|
|
|
class Option(models.TextChoices):
|
|
NORMAL = "B", "normaler Beschluss"
|
|
PERMANENT = "D", "Dauerbeschluss"
|
|
FINANCE = "F", "Finanzbeschluss"
|
|
FSREF = "R", "FsRef-Beschluss"
|
|
|
|
option = models.CharField(max_length=1, choices=Option.choices, verbose_name="Beschluss")
|
|
|
|
date = models.DateField(verbose_name="Datum")
|
|
voting = models.CharField(max_length=15, verbose_name="Abstimmungsverhalten")
|
|
voting_text = models.TextField(verbose_name="Abstimmungstext")
|
|
|
|
budget = models.DecimalField(
|
|
max_digits=7,
|
|
decimal_places=2,
|
|
default=0.00,
|
|
verbose_name="Budget (EUR)",
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = "Beschluss"
|
|
verbose_name_plural = "Beschlüsse"
|
|
|
|
def __str__(self):
|
|
return f"{self.id} / {self.name}"
|
|
|
|
def get_absolute_url(self):
|
|
return reverse("finance:resolution_detail", kwargs={"id": self.id})
|
|
|
|
def clean(self):
|
|
if self.id == "":
|
|
year = self.date.strftime("%y")
|
|
week = self.date.strftime("%W")
|
|
|
|
_id = ""
|
|
for nmb in range(99):
|
|
_id = f"{year}{week}-{nmb + 1:02d}"
|
|
if not Resolution.objects.filter(id=_id).exists():
|
|
break
|
|
else:
|
|
raise ValidationError(
|
|
f"Es wurden zu viele Beschlüsse in dieser Woche angelegt. (ID: {_id})"
|
|
)
|
|
|
|
self.id = _id
|
|
|
|
if self.name == "":
|
|
self.name = self.id
|
|
|
|
|
|
class Wiref(models.Model):
|
|
wiref_id = models.CharField(max_length=10, blank=True, default="")
|
|
|
|
file_field = models.FileField(
|
|
upload_to="uploads/finance/wiref/",
|
|
validators=[FileExtensionValidator(["pdf"])],
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Wiref Formular",
|
|
)
|
|
|
|
class Status(models.TextChoices):
|
|
OPENED = "O", "Offen"
|
|
SUBMITTED = "S", "Eingereicht"
|
|
TRANSFERRED = "T", "Überwiesen"
|
|
|
|
status = models.CharField(
|
|
max_length=1,
|
|
choices=Status.choices,
|
|
default=Status.OPENED,
|
|
verbose_name="Status",
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = "Wiref Formular"
|
|
verbose_name_plural = "Wiref Formulare"
|
|
|
|
def __str__(self):
|
|
return f"{self.wiref_id}"
|
|
|
|
|
|
class Bill(models.Model):
|
|
# members can be deleted but never their bills
|
|
bill_creator = models.ForeignKey(
|
|
Member,
|
|
on_delete=models.PROTECT,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Verantwortliche:r",
|
|
)
|
|
|
|
bankdata = models.ForeignKey(
|
|
BankData,
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Bankdaten",
|
|
)
|
|
|
|
resolution = models.ForeignKey(
|
|
Resolution,
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Beschlussnummer",
|
|
)
|
|
|
|
date = models.DateField()
|
|
invoice = models.TextField()
|
|
amount = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="Betrag (EUR)")
|
|
purpose = models.CharField(max_length=140, verbose_name="Verwendungszweck")
|
|
|
|
class Affiliation(models.TextChoices):
|
|
VEREIN = "V", "Vereinsbudget"
|
|
OFFICIAL = "B", "Wiref-Budget"
|
|
REPRESENTATION = "R", "Bundesvertretung"
|
|
|
|
affiliation = models.CharField(
|
|
max_length=1,
|
|
choices=Affiliation.choices,
|
|
verbose_name="Abrechnungsbudget",
|
|
)
|
|
|
|
class Payer(models.TextChoices):
|
|
ME = "M", "Privat"
|
|
VEREIN = "V", "Verein (Safe/Kreditkarte)"
|
|
|
|
payer = models.CharField(
|
|
max_length=1,
|
|
choices=Payer.choices,
|
|
verbose_name="Wie wurde die Rechnung bezahlt?",
|
|
)
|
|
|
|
only_digital = models.BooleanField(default=False, verbose_name="Digitale Rechnung")
|
|
|
|
file_field = models.FileField(
|
|
upload_to="uploads/finance/bills/",
|
|
validators=[validate_bill_file_extension],
|
|
blank=True,
|
|
null=True,
|
|
)
|
|
|
|
comment = models.TextField(blank=True, default="")
|
|
|
|
class Status(models.TextChoices):
|
|
SUBMITTED = "S", "Eingereicht"
|
|
INCOMPLETED = "I", "Unvollständig / Abgelehnt"
|
|
CLEARED = "C", "Für Überweisung freigegeben"
|
|
FINISHED = "F", "Abgeschlossen / Überwiesen"
|
|
|
|
status = models.CharField(
|
|
max_length=1,
|
|
choices=Status.choices,
|
|
default=Status.SUBMITTED,
|
|
verbose_name="Status",
|
|
)
|
|
|
|
wiref = models.ForeignKey(
|
|
Wiref,
|
|
on_delete=models.SET_NULL,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name="Wiref",
|
|
)
|
|
|
|
date_created = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
verbose_name = "Rechnung"
|
|
verbose_name_plural = "Rechnungen"
|
|
|
|
def __str__(self):
|
|
return f"{self.purpose}"
|
|
|
|
def clean(self):
|
|
if self.status is None:
|
|
self.status = Bill.Status.SUBMITTED
|