diff --git a/adm/forms.py b/adm/forms.py new file mode 100644 index 0000000..55b6422 --- /dev/null +++ b/adm/forms.py @@ -0,0 +1,13 @@ +from django import forms +from django.forms import ModelForm +from users.models import AgencyBills + +class AgencyBillForm(forms.ModelForm): + + class Meta: + model = AgencyBills + fields = ['agency', 'start'] + labels = { + 'agency' : "Agentur", + 'start' : "Leistungszeitraum Start", + } diff --git a/adm/templates/adm/adm_addbill.html b/adm/templates/adm/adm_addbill.html new file mode 100644 index 0000000..7d291f4 --- /dev/null +++ b/adm/templates/adm/adm_addbill.html @@ -0,0 +1,42 @@ +{% extends "adm/adm_base.html" %} +{% block content %} +{% load adm_tags %} +{% load mathfilters %} +{% load crispy_forms_tags %} +{% load humanize %} +{% load counter_tag %} +
+

Rechnung manuell erstellen

+
+Achtung! Sie sind in Begriff, eine Rechnung manuell zu erstellen! Achten Sie auf die korrekte Agentur sowie Start- und Endtermin! Prüfen Sie vor Anlagen Ihre Eingaben! +
+ +
+ {% csrf_token %} + {{form.media}} + {{form|crispy}} + +
+ + +
+ +
+ Abbrechen + + + + +{% endblock content %} diff --git a/adm/templates/adm/adm_bills.html b/adm/templates/adm/adm_bills.html index ef12bb2..6afd416 100644 --- a/adm/templates/adm/adm_bills.html +++ b/adm/templates/adm/adm_bills.html @@ -5,7 +5,11 @@ {% load humanize %} {% load counter_tag %}
-

Rechungsübersicht

+

Rechungsübersicht + +  Rechnung + +


diff --git a/adm/urls.py b/adm/urls.py index d633db6..5fcaf1e 100644 --- a/adm/urls.py +++ b/adm/urls.py @@ -16,5 +16,6 @@ urlpatterns = [ path('ag/bills/', AdmBills.as_view(), name="adm-bills"), path('usersingle/', AdmUserSingle.as_view(), name="adm-user-single"), path('cron/', statisticCronJob, name="adm-cron"), - path('getorders/', getCSVRDOrders, name="getorders") + path('getorders/', getCSVRDOrders, name="getorders"), + path('adm/addbill', AdmAddBill.as_view(), name="admbill-add") ] diff --git a/adm/views.py b/adm/views.py index 7187342..e459bae 100644 --- a/adm/views.py +++ b/adm/views.py @@ -15,6 +15,17 @@ from datetime import date, datetime import json from users.models import UserYearAbsenceInfo, UserTime from timemanagement.models import Workday, Absence +from .forms import AgencyBillForm +from datetime import date, timedelta + +from django.core.mail import EmailMessage +from django.core.mail import EmailMultiAlternatives +import io as BytesIO +import base64 +from django.http import HttpResponse +from dateutil.relativedelta import * +import requests +from django.template.loader import render_to_string ''' Prüfung, ob angemeldeter User Mitarbeiterstatus hat. IMMER PER DISPATCH EINBAUEN! ''' @@ -121,6 +132,181 @@ class AdmBills(TemplateView): return context +''' +Erstellen einer neuen Rechnung +''' +class AdmAddBill(CreateView): + template_name = "adm/adm_addbill.html" + model = AgencyBills + success_url = reverse_lazy('adm-bills') + form_class = AgencyBillForm + + def dispatch(self, *args, **kwargs): + if(checkForStuffUser(self.request)): + return super().dispatch(*args, **kwargs) + else: + messages.warning(self.request, f'Sie benötigen einen Mitarbeiter-Account, um diese Seiten aufzurufen!') + return redirect("login") + + def form_valid(self, form): + + today = date.today() + + agency = form.cleaned_data['agency'] + + # USERCOUNT BERECHNEN + usercount = len(User.objects.filter(profile__agency=agency)) + + if(usercount < 4): + usercount = 0 + else: + usercount = usercount - 3 + + # HEADERS CURL + headers = { + 'Authorization': 'Bearer ' + settings.LEX_API, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + + plan = 1 + + start_date = form.cleaned_data["start"] + start_date_string = start_date.strftime("%d.%m.%Y") + end_date = start_date + relativedelta(days=30) + end_date_string= end_date.strftime("%d.%m.%Y") + + # Rechnungsdatum passt ja + voucher_date_today = date.today().strftime("%Y-%m-%d") + + monthword = "Monat" + + lexdata = { + "voucherDate": voucher_date_today + "T00:00:00.000+00:00", + "address" : { + "name" : agency.name, + "street": agency.street, + "zip": agency.plz, + "city": agency.city, + "countryCode" : "DE" + }, + "totalPrice" : { + "currency" : "EUR", + }, + "lineItems" : [ + { + "type" : "custom", + "name" : "Digitale Agentur: Grundbetrag für " + str(plan) + " " + monthword, + "quantity" : 1, + "unitName" : "Stück", + "description" : "Zeitraum " + start_date_string + " - " + end_date_string, + "unitPrice" : + { + "currency" : "EUR", + "netAmount" : 21.00, + "taxRatePercentage" : 19 + }, + }, + { + "type" : "custom", + "name" : "Digitale Agentur: Zusätzliche Nutzer", + "description" : "Zeitraum " + start_date_string + " - " + end_date_string, + "quantity" : usercount, + "unitName" : "Stück", + "unitPrice" : + { + "currency" : "EUR", + "netAmount" : 3, + "taxRatePercentage" : 19 + }, + } + ], + "taxConditions": { + "taxType": "net" + }, + #"paymentConditions": { + # "paymentTermLabel": "Bitte zahlen Sie innerhalb von 14 Tagen.", + # "paymentTermDuration": 14, + #}, + "shippingConditions": { + #"shippingDate": voucher_date_today + "T00:00:00.000+00:00", + "shippingType": "none" + } + } + json_data = json.dumps(lexdata) + # WIEDER RAUSNEHMEN + # NEUE RECHNUNG ALs ENTWURF + + #r = requests.post("https://api.lexoffice.io/v1/invoices/", data=json_data, headers=headers) + # RICHTIGE RECHNUNG + r = requests.post("https://api.lexoffice.io/v1/invoices/?finalize=true", data=json_data, headers=headers) + + if(r.status_code == 201): + + response_text = json.loads(r.text) + newbill_id = response_text["id"] + + # OrganizationId berechnen, wenn noch nicht gesetzt + r = requests.get("https://api.lexoffice.io/v1/invoices/" + response_text["id"], data=json_data, headers=headers) + response_text = json.loads(r.text) + + newbill = AgencyBills(agency=agency, lexid=newbill_id, billtype="invoice", billnumber=response_text["voucherNumber"], billstatus=response_text["voucherStatus"], start=start_date, end=end_date, plan=plan, usercount=usercount) + newbill.save() + + mail_to_send = "" + if(agency.payment_address == None): + mail_to_send = agency.agency_email + else: + mail_to_send = agency.payment_address + + + + # BCC Mail with Object - NICHT DEN IMPORT VERGESSEN!!! + email = EmailMultiAlternatives( + 'Digitale Agentur | Rechnung ' + str(response_text["voucherNumber"]), + 'Sehr geehrte Nutzer, hiermit erhalten Sie eine neue Rechnung für die Digitale Agentur. Ihr Team der Digitalen Agentur', + 'noreply@digitale-agentur.com', + [mail_to_send], + ['info@digitale-agentur.com'], + headers={}, + ) + + headers = { + 'Authorization': 'Bearer ' + settings.LEX_API, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + + lexdata = { + "renderType" : "pdf" + } + + json_data = json.dumps(lexdata) + + r_final = requests.get("https://api.lexoffice.io/v1/invoices/"+newbill_id+"/document", data=json_data, headers=headers) + json.loads(r_final.text) + + base64String = requests.get("https://api.lexoffice.io/v1/files/"+json.loads(r_final.text)["documentFileId"]+"/", data=json_data, headers=headers) + + content = base64.b64decode(base64String.text) + + msg_html = render_to_string('users/newbill_mail.html', {}) + email.attach_alternative(msg_html, "text/html") + email.attach('Rechnung_' + str(response_text["voucherNumber"]) + '.pdf', content, "application/pdf") + email.send() + + return super(AdmAddBill, self).form_valid(form) + + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + context.update({'active_link' : "adm-agencys"}) + + #context.update({'agencys' : Agency.objects.all()}) + + return context + ''' Gesamtansicht der Agenturen diff --git a/digitaleagentur/__pycache__/settings.cpython-38.pyc b/digitaleagentur/__pycache__/settings.cpython-38.pyc index e4934a8..af75947 100644 Binary files a/digitaleagentur/__pycache__/settings.cpython-38.pyc and b/digitaleagentur/__pycache__/settings.cpython-38.pyc differ diff --git a/dump.rdb b/dump.rdb index fbe4324..46ac46e 100644 Binary files a/dump.rdb and b/dump.rdb differ diff --git a/users/views.py b/users/views.py index e028568..3761647 100644 --- a/users/views.py +++ b/users/views.py @@ -1480,40 +1480,37 @@ def cronactionsdaily(request, code): ''' for user in allusers: - mailstatus += "\n USER: " + user.first_name + " " + user.last_name + " ID: (" + str(user.pk) + ")" - # REST URLAUB BERECHNUNG - #try: - usertimedata = UserTime.objects.get(user=user) - day_tocheck = usertimedata.loose_holidedate.split(".")[0] - month_tocheck = usertimedata.loose_holidedate.split(".")[1] - month = today.month - day = today.day - if month < 10: - month = "0" + str(month) - day = today.day - else: - month = month - - if day < 10: - day = "0" + str(day) - else: - day = day - - # Restetag erreicht, Reste ins nächste Jahr übertragen - ''' DAS IST UNNÖTIG ''' - ''' - if(str(day_tocheck) == str(day) and str(month_tocheck) == str(month)): - sourceyear = today.year - this_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear, user=user))[0] - next_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear+1, user=user))[0] - next_year.restdays = this_year.days - this_year.days_inuse - next_year.save() - ''' - - # Arbeitstage beenden try: + usertimedata = UserTime.objects.get(user=user) + day_tocheck = usertimedata.loose_holidedate.split(".")[0] + month_tocheck = usertimedata.loose_holidedate.split(".")[1] + month = today.month + day = today.day + if month < 10: + month = "0" + str(month) + day = today.day + else: + month = month + + if day < 10: + day = "0" + str(day) + else: + day = day + + # Restetag erreicht, Reste ins nächste Jahr übertragen + ''' DAS IST UNNÖTIG ''' + ''' + if(str(day_tocheck) == str(day) and str(month_tocheck) == str(month)): + sourceyear = today.year + this_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear, user=user))[0] + next_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear+1, user=user))[0] + next_year.restdays = this_year.days - this_year.days_inuse + next_year.save() + ''' + + # Arbeitstage beenden if(user.usertime.usetime): try: workdays = Workday.objects.filter(user=user, end=None) @@ -1578,6 +1575,7 @@ def cronactionsdaily(request, code): except ObjectDoesNotExist: data.update({"status" + str(user.pk) : "no usertime found for " + user.get_full_name()}) mailstatus += "USER HAS NO USERTIMEOBJECT USER-ID: " + str(user.pk) + else: data.update({"status" : "failed"})