Erstellen von eigenen Rechnungen in der Adminoberfläche
This commit is contained in:
parent
6f4389999d
commit
571cffaf0e
|
|
@ -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",
|
||||
}
|
||||
|
|
@ -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 %}
|
||||
<div class="content-section col-12">
|
||||
<h4>Rechnung manuell erstellen</h4>
|
||||
<hr>
|
||||
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!
|
||||
<hr>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
{{form|crispy}}
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="" id="confirmDel" onclick="javascript:toggleEntryOkButton()">
|
||||
<label class="form-check-label" for="confirmDel">
|
||||
Bestätigung, dass die Rechnungsinformationenx korrekt sind.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<a class="btn btn-secondary" href="{% url 'adm-agencys' %}" name="action" disabled="true">Abbrechen</a>
|
||||
<button style="float: right" class="btn btn-primary" type="submit" name="action" disabled="true" id="doaddbill"><b>Rechnung inkl. PDF-Generierung erstellen und an Agentur schicken.</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggleEntryOkButton(){
|
||||
if($('#confirmDel').prop('checked')){
|
||||
$('#doaddbill').attr('disabled', false);
|
||||
}
|
||||
else{
|
||||
$('#doaddbill').attr('disabled', true);
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
|
@ -5,7 +5,11 @@
|
|||
{% load humanize %}
|
||||
{% load counter_tag %}
|
||||
<div class="content-section col-12">
|
||||
<h4>Rechungsübersicht</h4>
|
||||
<h4>Rechungsübersicht
|
||||
<span style="float: right;">
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'admbill-add' %}" style="float: right;"><i class="fas fa-plus"></i> Rechnung</a>
|
||||
</span>
|
||||
</h4>
|
||||
<hr>
|
||||
<table class="table table-hover" id="agdata" >
|
||||
<thead>
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@ urlpatterns = [
|
|||
path('ag/bills/', AdmBills.as_view(), name="adm-bills"),
|
||||
path('usersingle/<int:uspk>', AdmUserSingle.as_view(), name="adm-user-single"),
|
||||
path('cron/<slug:code>', statisticCronJob, name="adm-cron"),
|
||||
path('getorders/', getCSVRDOrders, name="getorders")
|
||||
path('getorders/', getCSVRDOrders, name="getorders"),
|
||||
path('adm/addbill', AdmAddBill.as_view(), name="admbill-add")
|
||||
]
|
||||
|
|
|
|||
186
adm/views.py
186
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
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue