673 lines
14 KiB
Python
673 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, InvoicePayment
|
|
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 InvoicePaymentForm(BaseForm):
|
|
class Meta:
|
|
model = InvoicePayment
|
|
fields = [
|
|
"amount",
|
|
"date",
|
|
"payment_method",
|
|
"note",
|
|
]
|
|
|
|
widgets = {
|
|
"date": forms.DateInput(
|
|
attrs={
|
|
"type": "date"
|
|
}
|
|
),
|
|
}
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields["date"].initial = timezone.now().date()
|
|
self.apply_style()
|
|
|
|
|
|
|
|
|
|
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",
|
|
"category",
|
|
"unit",
|
|
"price_net",
|
|
"tax_rate",
|
|
"price_buy",
|
|
"supplier",
|
|
]
|
|
|
|
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() |