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 humanize %}
|
||||||
{% load counter_tag %}
|
{% load counter_tag %}
|
||||||
<div class="content-section col-12">
|
<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>
|
<hr>
|
||||||
<table class="table table-hover" id="agdata" >
|
<table class="table table-hover" id="agdata" >
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,6 @@ urlpatterns = [
|
||||||
path('ag/bills/', AdmBills.as_view(), name="adm-bills"),
|
path('ag/bills/', AdmBills.as_view(), name="adm-bills"),
|
||||||
path('usersingle/<int:uspk>', AdmUserSingle.as_view(), name="adm-user-single"),
|
path('usersingle/<int:uspk>', AdmUserSingle.as_view(), name="adm-user-single"),
|
||||||
path('cron/<slug:code>', statisticCronJob, name="adm-cron"),
|
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
|
import json
|
||||||
from users.models import UserYearAbsenceInfo, UserTime
|
from users.models import UserYearAbsenceInfo, UserTime
|
||||||
from timemanagement.models import Workday, Absence
|
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!
|
Prüfung, ob angemeldeter User Mitarbeiterstatus hat. IMMER PER DISPATCH EINBAUEN!
|
||||||
'''
|
'''
|
||||||
|
|
@ -121,6 +132,181 @@ class AdmBills(TemplateView):
|
||||||
return context
|
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
|
Gesamtansicht der Agenturen
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1480,11 +1480,9 @@ def cronactionsdaily(request, code):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
for user in allusers:
|
for user in allusers:
|
||||||
|
|
||||||
mailstatus += "\n USER: " + user.first_name + " " + user.last_name + " ID: (" + str(user.pk) + ")"
|
mailstatus += "\n USER: " + user.first_name + " " + user.last_name + " ID: (" + str(user.pk) + ")"
|
||||||
|
|
||||||
# REST URLAUB BERECHNUNG
|
# REST URLAUB BERECHNUNG
|
||||||
#try:
|
try:
|
||||||
usertimedata = UserTime.objects.get(user=user)
|
usertimedata = UserTime.objects.get(user=user)
|
||||||
day_tocheck = usertimedata.loose_holidedate.split(".")[0]
|
day_tocheck = usertimedata.loose_holidedate.split(".")[0]
|
||||||
month_tocheck = usertimedata.loose_holidedate.split(".")[1]
|
month_tocheck = usertimedata.loose_holidedate.split(".")[1]
|
||||||
|
|
@ -1513,7 +1511,6 @@ def cronactionsdaily(request, code):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Arbeitstage beenden
|
# Arbeitstage beenden
|
||||||
try:
|
|
||||||
if(user.usertime.usetime):
|
if(user.usertime.usetime):
|
||||||
try:
|
try:
|
||||||
workdays = Workday.objects.filter(user=user, end=None)
|
workdays = Workday.objects.filter(user=user, end=None)
|
||||||
|
|
@ -1578,6 +1575,7 @@ def cronactionsdaily(request, code):
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
data.update({"status" + str(user.pk) : "no usertime found for " + user.get_full_name()})
|
data.update({"status" + str(user.pk) : "no usertime found for " + user.get_full_name()})
|
||||||
mailstatus += "USER HAS NO USERTIMEOBJECT USER-ID: " + str(user.pk)
|
mailstatus += "USER HAS NO USERTIMEOBJECT USER-ID: " + str(user.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
data.update({"status" : "failed"})
|
data.update({"status" : "failed"})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue