Files
ProjectApp/invoices/forms.py
T
Felix Baumann fb47c53d52 Initial commit
2026-06-27 17:07:34 +00:00

641 lines
14 KiB
Python

from django import forms
from core.forms.base import BaseForm
from .models import Customer, Item, Invoice, InvoiceItem, CustomerContact, CustomerLocation, Offer, OfferItem, Reminder
from projects.models import Project
from time_tracking.models import TimeEntry
from finance.models import Expense
from django.utils import timezone
from django.db.models import Q, Max
class CustomerForm(BaseForm):
class Meta:
model = Customer
fields = [
"customer_number",
"customer_type",
"company_name",
"tax_number",
"vat_number",
"phone",
"email",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_style()
class ItemForm(BaseForm):
class Meta:
model = Item
fields = [
"item_type",
"description",
"unit",
"price_net",
"tax_rate",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_style()
class CustomerInvoiceForm(BaseForm):
contact = forms.ModelChoiceField(
queryset=CustomerContact.objects.none(),
label="Ansprechpartner",
required=True
)
location = forms.ModelChoiceField(
queryset=CustomerLocation.objects.none(),
label="Standort",
required=True
)
class Meta:
model = Invoice
fields = [
"customer",
"contact",
"location",
"issue_date",
"due_date",
"note",
]
widgets = {
"issue_date": forms.DateInput(attrs={"type": "date"}),
"due_date": forms.DateInput(attrs={"type": "date"}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_style()
customer_id = (
self.data.get("customer")
or getattr(self.instance, "customer_id", None)
)
if customer_id:
self.fields["contact"].queryset = CustomerContact.objects.filter(customer_id=customer_id)
self.fields["location"].queryset = CustomerLocation.objects.filter(customer_id=customer_id)
else:
self.fields["contact"].queryset = CustomerContact.objects.none()
self.fields["location"].queryset = CustomerLocation.objects.none()
class ProjectInvoiceForm(BaseForm):
contact = forms.ModelChoiceField(
queryset=CustomerContact.objects.none(),
required=False,
label="Ansprechpartner",
widget=forms.Select(attrs={
"class": "customer-field"
})
)
location = forms.ModelChoiceField(
queryset=CustomerLocation.objects.none(),
required=False,
label="Standort",
widget=forms.Select(attrs={
"class": "customer-field"
})
)
contact_name_manual = forms.CharField(
required=False,
label="Ansprechpartner",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
contact_email_manual = forms.EmailField(
required=False,
label="E-Mail",
widget=forms.EmailInput(attrs={
"class": "manual-field"
})
)
contact_phone_manual = forms.CharField(
required=False,
label="Telefon",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
location_name_manual = forms.CharField(
required=False,
label="Standort",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
location_street_manual = forms.CharField(
required=False,
label="Straße",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
location_zip_manual = forms.CharField(
required=False,
label="PLZ",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
location_city_manual = forms.CharField(
required=False,
label="Ort",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
location_country_manual = forms.CharField(
required=False,
label="Land",
widget=forms.TextInput(attrs={
"class": "manual-field"
})
)
class Meta:
model = Invoice
fields = [
"project",
"contact",
"location",
"contact_name_manual",
"contact_email_manual",
"contact_phone_manual",
"location_name_manual",
"location_street_manual",
"location_zip_manual",
"location_city_manual",
"location_country_manual",
"issue_date",
"due_date",
"note",
]
widgets = {
"issue_date": forms.DateInput(
attrs={
"type": "date"
}
),
"due_date": forms.DateInput(
attrs={
"type": "date"
}
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_style()
project_id = self.data.get("project") or getattr(
self.instance,
"project_id",
None
)
if project_id:
project = Project.objects.filter(
id=project_id
).first()
if project and project.customer:
self.fields["contact"].queryset = CustomerContact.objects.filter(
customer=project.customer
)
self.fields["location"].queryset = CustomerLocation.objects.filter(
customer=project.customer
)
class InvoiceItemForm(BaseForm):
class Meta:
model = InvoiceItem
fields = [
"source_type",
"item",
"time_entry",
"expense",
"description",
"quantity",
"unit",
"unit_price_net",
"unit_tax_rate",
'service_period_from',
'service_period_to',
]
widgets = {
"quantity": forms.NumberInput(attrs={
"step": "1.00"
}),
"unit_price_net": forms.NumberInput(attrs={
"step": "1.00"
}),
"unit_tax_rate": forms.NumberInput(attrs={
"step": "1.00"
}),
"issue_date": forms.DateInput(
attrs={
"type": "date"
}
),
"service_period_from": forms.DateInput(
attrs={
"type": "date"
}
),
"service_period_to": forms.DateInput(
attrs={
"type": "date"
}
),
}
def __init__(
self,
*args,
invoice=None,
**kwargs
):
super().__init__(*args, **kwargs)
self.apply_style()
self.invoice = invoice
self.fields["item"].queryset = Item.objects.all()
self.fields["time_entry"].queryset = TimeEntry.objects.none()
self.fields["expense"].queryset = Expense.objects.none()
if invoice and invoice.project:
self.fields["source_type"].choices = [
("item", "Artikel"),
("time", "Zeiterfassung"),
("expense", "Ausgabe"),
]
self.fields["time_entry"].queryset = TimeEntry.objects.filter(
project=invoice.project,end__isnull=False
)
self.fields["expense"].queryset = Expense.objects.filter(
project=invoice.project
)
else:
self.fields["source_type"].choices = [
("item", "Artikel"),
]
self.fields["item"].widget.attrs["class"] += " source-item"
self.fields["time_entry"].widget.attrs["class"] += " source-time"
self.fields["expense"].widget.attrs["class"] += " source-expense"
def save(self, commit=True):
obj = super().save(commit=False)
source = self.cleaned_data.get(
"source_type"
)
# -------------------------
# ARTIKEL
# -------------------------
if source == "item":
item = self.cleaned_data["item"]
obj.item = item
obj.time_entry = None
obj.expense = None
obj.description = self.cleaned_data["description"]
obj.unit = self.cleaned_data["unit"]
obj.unit_price_net = self.cleaned_data["unit_price_net"]
obj.unit_tax_rate = self.cleaned_data["unit_tax_rate"]
# -------------------------
# ZEITERFASSUNG
# -------------------------
elif source == "time":
entry = self.cleaned_data["time_entry"]
obj.item = None
obj.time_entry = entry
obj.expense = None
obj.description = self.cleaned_data["description"]
obj.unit = self.cleaned_data["unit"]
obj.unit_price_net = self.cleaned_data["unit_price_net"]
obj.unit_tax_rate = self.cleaned_data["unit_tax_rate"]
# -------------------------
# AUSGABE
# -------------------------
elif source == "expense":
expense = self.cleaned_data["expense"]
obj.item = None
obj.time_entry = None
obj.expense = expense
obj.description = self.cleaned_data["description"]
obj.unit = self.cleaned_data["unit"]
obj.unit_price_net = self.cleaned_data["unit_price_net"]
obj.unit_tax_rate = self.cleaned_data["unit_tax_rate"]
if commit:
obj.save()
return obj
class OfferForm(BaseForm):
contact = forms.ModelChoiceField(
queryset=CustomerContact.objects.none(),
label="Ansprechpartner",
required=True
)
location = forms.ModelChoiceField(
queryset=CustomerLocation.objects.none(),
label="Standort",
required=True
)
class Meta:
model = Offer
fields = [
"customer",
"contact",
"location",
"issue_date",
"due_date",
"note",
]
widgets = {
"issue_date": forms.DateInput(attrs={"type": "date"}),
"due_date": forms.DateInput(attrs={"type": "date"}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_style()
customer_id = (
self.data.get("customer")
or getattr(self.instance, "customer_id", None)
)
if customer_id:
self.fields["contact"].queryset = CustomerContact.objects.filter(customer_id=customer_id)
self.fields["location"].queryset = CustomerLocation.objects.filter(customer_id=customer_id)
else:
self.fields["contact"].queryset = CustomerContact.objects.none()
self.fields["location"].queryset = CustomerLocation.objects.none()
class OfferItemForm(BaseForm):
class Meta:
model = OfferItem
fields = [
"item",
"description",
"quantity",
"unit",
"unit_price_net",
"unit_tax_rate",
]
widgets = {
"quantity": forms.NumberInput(
attrs={"step": "1.00"}
),
"unit_price_net": forms.NumberInput(
attrs={"step": "1.00"}
),
"unit_tax_rate": forms.NumberInput(
attrs={"step": "1.00"}
),
}
def __init__(
self,
*args,
offer=None,
**kwargs
):
super().__init__(*args, **kwargs)
self.apply_style()
self.offer = offer
self.fields["item"].queryset = (
Item.objects.all()
)
def save(self, commit=True):
obj = super().save(commit=False)
item = self.cleaned_data.get("item")
if item:
obj.item = item
if commit:
obj.save()
return obj
class ReminderForm(BaseForm):
class Meta:
model = Reminder
fields = [
"invoice",
"issue_date",
"due_date",
"fee",
"note",
]
widgets = {
"issue_date": forms.DateInput(attrs={"type": "date"}),
"due_date": forms.DateInput(attrs={"type": "date"}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
possible_invoices = []
invoices = Invoice.objects.filter(
status=Invoice.Status.OVERDUE,
due_date__lt=timezone.now().date(),
).prefetch_related(
"reminders"
)
for invoice in invoices:
reminders = invoice.reminders.exclude(
status=Reminder.Status.CANCELED
).order_by("-level")
# Keine Mahnung vorhanden
if not reminders.exists():
possible_invoices.append(invoice)
continue
last_reminder = reminders.first()
# Nur nach expired möglich
if last_reminder.status != Reminder.Status.EXPIRED:
continue
# Maximal 3 Mahnungen
if last_reminder.level >= 3:
continue
possible_invoices.append(invoice)
self.fields["invoice"].queryset = Invoice.objects.filter(
id__in=[i.id for i in possible_invoices]
).order_by("due_date")
self.apply_style()