First Upload
commit
d5a8c8e9f4
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.12 (Shopware-API)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.12 (Shopware-API)" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Shopware-API.iml" filepath="$PROJECT_DIR$/.idea/Shopware-API.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
%PDF-1.3
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 8 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 7 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 7 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Author (anonymous) /CreationDate (D:20250217154506+01'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20250217154506+01'00') /Producer (ReportLab PDF Library - www.reportlab.com)
|
||||||
|
/Subject (unspecified) /Title (Lieferschein 17.02.2025) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/Count 1 /Kids [ 4 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 582
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau0@b>,r/&A7TLHNYUG]sd6[,GK.N!mq9+>ccM4TI(f%'mr2_*c5S"LJ"4-OlFS[B%Ji95hnf/k9:/@9]>o<!_#XeJF\L`H4/3oW*2]aPA37;E48R]$:,d]"O\9Mk:4gNlKgFX0A;YK=.L'ROM4#,]&ak07u'aW2:p?B<j,O><.4jI7Kaq]./*IUPY]eVQb)0,ekVrZN._a#+HBE3_ei$>K5.CPg8Bp0<=_#mOWiRQ,I*i0=rL\4qVjb[P=F?@[bAN\2MF%R.Crp@Cm[*FR5(Z]/+]K(;PQf7OMKX4Ksh(9.a00pk]=5\0^fWFpJKKEO32"5Yjc)XQg9>GrZR/)_>NP43kO/5Q&+WXS9<a2#U^W`&g[iH,9a`1Gc67,Me4FhIN`(bb7BFGj5M!`:ILG'@J(I8cOQL1ld$XT/[4a\=CD-C9/;jj,koAL_SX:l,alb=82AqGm0dNA-d^qS.(neTc"(U'\P'<EU.N,C4;ZAqWF[a4(^E`g8O!jWRG$3:bD.u6e"tr1D=u1_1?*b<[;_052\'UHbW.3qbQrsK.$TK4*.%oi+Q5;/4LL:0KYu1rVKfuB~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 9
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000073 00000 n
|
||||||
|
0000000114 00000 n
|
||||||
|
0000000221 00000 n
|
||||||
|
0000000333 00000 n
|
||||||
|
0000000536 00000 n
|
||||||
|
0000000604 00000 n
|
||||||
|
0000000915 00000 n
|
||||||
|
0000000974 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<52cc85184a6e7bde8ef927cb4ba48ccd><52cc85184a6e7bde8ef927cb4ba48ccd>]
|
||||||
|
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
|
||||||
|
|
||||||
|
/Info 6 0 R
|
||||||
|
/Root 5 0 R
|
||||||
|
/Size 9
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
1646
|
||||||
|
%%EOF
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
import os
|
||||||
|
import customtkinter as ctk
|
||||||
|
import winreg
|
||||||
|
from PIL import Image
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
REG_PATH = r"Software\ShopwareDeliver"
|
||||||
|
|
||||||
|
def rerun():
|
||||||
|
"""Löscht Registry-Werte und ruft run() auf, falls Werte fehlen"""
|
||||||
|
try:
|
||||||
|
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0, winreg.KEY_SET_VALUE) as key:
|
||||||
|
all_deleted = True # Flag, um zu prüfen, ob alle Werte gelöscht wurden
|
||||||
|
|
||||||
|
for name in ["CLIENT_ID", "CLIENT_SECRET", "API_URL"]:
|
||||||
|
try:
|
||||||
|
winreg.DeleteValue(key, name)
|
||||||
|
print(f"✅ {name} wurde gelöscht.")
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"⚠️ {name} war nicht vorhanden.")
|
||||||
|
all_deleted = False # Mindestens ein Wert fehlte bereits
|
||||||
|
|
||||||
|
if all_deleted:
|
||||||
|
print("✅ Alle Werte erfolgreich gelöscht.")
|
||||||
|
run() # Falls alles erfolgreich gelöscht wurde, rufe run() auf
|
||||||
|
else:
|
||||||
|
print("⚠️ Einige Werte waren nicht vorhanden, run() wird nicht aufgerufen.")
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("⚠️ Fehler: Registry-Schlüssel nicht gefunden!")
|
||||||
|
run() # Falls die gesamte Registry fehlt, rufe run() auf
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
REG_PATH = r"Software\ShopwareDeliver"
|
||||||
|
|
||||||
|
def get_registry_value(name):
|
||||||
|
"""Liest einen Wert aus der Windows-Registry"""
|
||||||
|
try:
|
||||||
|
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0, winreg.KEY_READ) as key:
|
||||||
|
value, _ = winreg.QueryValueEx(key, name)
|
||||||
|
return value
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_registry_value(name, value):
|
||||||
|
"""Speichert einen Wert in der Windows-Registry"""
|
||||||
|
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, REG_PATH) as key:
|
||||||
|
winreg.SetValueEx(key, name, 0, winreg.REG_SZ, value)
|
||||||
|
|
||||||
|
def open_link(event=None):
|
||||||
|
webbrowser.open("https://itdata-gera.de")
|
||||||
|
|
||||||
|
# Prüfe, ob die Werte existieren
|
||||||
|
CLIENT_ID = get_registry_value("CLIENT_ID")
|
||||||
|
CLIENT_SECRET = get_registry_value("CLIENT_SECRET")
|
||||||
|
API_URL = get_registry_value("API_URL")
|
||||||
|
|
||||||
|
# Falls Werte fehlen, öffne das Eingabe-Fenster
|
||||||
|
if not CLIENT_ID or not CLIENT_SECRET or not API_URL:
|
||||||
|
|
||||||
|
class SecretInputApp(ctk.CTk):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.title("API-Konfiguration")
|
||||||
|
self.geometry("400x300")
|
||||||
|
|
||||||
|
ctk.CTkLabel(self, text="Shopware Deliver", font=("Arial", 20)).pack(pady=5)
|
||||||
|
ctk.CTkLabel(self, text="Bitte geben Sie die API-Daten ein:", font=("Arial", 16)).pack(pady=10)
|
||||||
|
|
||||||
|
self.client_id_entry = ctk.CTkEntry(self, placeholder_text="Client ID")
|
||||||
|
self.client_id_entry.pack(pady=5, padx=10, fill="x")
|
||||||
|
|
||||||
|
self.client_secret_entry = ctk.CTkEntry(self, placeholder_text="Client Secret", show="*")
|
||||||
|
self.client_secret_entry.pack(pady=5, padx=10, fill="x")
|
||||||
|
|
||||||
|
self.api_url_entry = ctk.CTkEntry(self, placeholder_text="Shopware URL")
|
||||||
|
self.api_url_entry.pack(pady=5, padx=10, fill="x")
|
||||||
|
|
||||||
|
self.save_button = ctk.CTkButton(self, text="Speichern", command=self.save_keys)
|
||||||
|
self.save_button.pack(pady=20)
|
||||||
|
|
||||||
|
def save_keys(self):
|
||||||
|
client_id = self.client_id_entry.get()
|
||||||
|
client_secret = self.client_secret_entry.get()
|
||||||
|
api_url = self.api_url_entry.get()
|
||||||
|
|
||||||
|
if client_id and client_secret and api_url:
|
||||||
|
# Speichere die Werte in der Windows-Registry
|
||||||
|
set_registry_value("CLIENT_ID", client_id)
|
||||||
|
set_registry_value("CLIENT_SECRET", client_secret)
|
||||||
|
set_registry_value("API_URL", api_url)
|
||||||
|
|
||||||
|
self.destroy() # Fenster schließen nach dem Speichern
|
||||||
|
else:
|
||||||
|
ctk.CTkLabel(self, text="Alle Felder müssen ausgefüllt sein!", text_color="red").pack(pady=5)
|
||||||
|
|
||||||
|
# Starte die GUI
|
||||||
|
app = SecretInputApp()
|
||||||
|
app.mainloop()
|
||||||
|
|
||||||
|
# Jetzt sind die Werte sicher gespeichert und können im restlichen Skript genutzt werden
|
||||||
|
CLIENT_ID = get_registry_value("CLIENT_ID")
|
||||||
|
CLIENT_SECRET = get_registry_value("CLIENT_SECRET")
|
||||||
|
API_URL = get_registry_value("API_URL")
|
||||||
|
|
||||||
|
return CLIENT_ID, CLIENT_SECRET, API_URL
|
||||||
|
|
@ -0,0 +1,342 @@
|
||||||
|
import tkinter
|
||||||
|
from PIL import Image
|
||||||
|
import requests
|
||||||
|
import webbrowser
|
||||||
|
import customtkinter as ctk
|
||||||
|
from tkinter import messagebox
|
||||||
|
from tkinter import ttk
|
||||||
|
from reportlab.lib.pagesizes import A4
|
||||||
|
from reportlab.pdfgen import canvas
|
||||||
|
from datetime import datetime
|
||||||
|
import datetime
|
||||||
|
import check_api
|
||||||
|
|
||||||
|
STATUS_PAID = "in_progress"
|
||||||
|
keys = check_api.run()
|
||||||
|
CLIENT_ID = keys[0]
|
||||||
|
CLIENT_SECRET = keys[1]
|
||||||
|
SHOPWARE_URL = keys[2]
|
||||||
|
|
||||||
|
current_order_id = None
|
||||||
|
|
||||||
|
def get_order_line_items(access_token, order_id):
|
||||||
|
headers = {
|
||||||
|
"sw-access-key": CLIENT_ID,
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
items_url = f"{SHOPWARE_URL}/api/order/{order_id}/line-items"
|
||||||
|
response = requests.get(items_url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["data"]
|
||||||
|
|
||||||
|
def get_order_customer(access_token, customer_id):
|
||||||
|
headers = {
|
||||||
|
"sw-access-key": CLIENT_ID,
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
customer_url = f"{SHOPWARE_URL}/api/order-customer/{customer_id}"
|
||||||
|
response = requests.get(customer_url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["data"]
|
||||||
|
|
||||||
|
def get_order_billing_address(access_token, order_id):
|
||||||
|
headers = {
|
||||||
|
"sw-access-key": CLIENT_ID,
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
address_url = f"{SHOPWARE_URL}/api/order/{order_id}/billing-address/"
|
||||||
|
response = requests.get(address_url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["data"][0]
|
||||||
|
|
||||||
|
def get_access_token():
|
||||||
|
token_url = f"{SHOPWARE_URL}/api/oauth/token"
|
||||||
|
data = {
|
||||||
|
"client_id": CLIENT_ID,
|
||||||
|
"client_secret": CLIENT_SECRET,
|
||||||
|
"grant_type": "client_credentials"
|
||||||
|
}
|
||||||
|
response = requests.post(token_url, data=data)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["access_token"]
|
||||||
|
|
||||||
|
def get_paid_orders(access_token):
|
||||||
|
headers = {
|
||||||
|
"sw-access-key": CLIENT_ID,
|
||||||
|
"authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
orders_url = f"{SHOPWARE_URL}/api/search/order"
|
||||||
|
query = {
|
||||||
|
"limit": 100,
|
||||||
|
"page": 1,
|
||||||
|
"filter": [{
|
||||||
|
"type": "equals",
|
||||||
|
"field": "stateMachineState.technicalName",
|
||||||
|
"value": STATUS_PAID
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
response = requests.post(orders_url, headers=headers, json=query)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()["data"]
|
||||||
|
|
||||||
|
def get_order_details(access_token, order_id):
|
||||||
|
headers = {
|
||||||
|
"sw-access-key": CLIENT_ID,
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
items_url = f"{SHOPWARE_URL}/api/order/{order_id}/line-items"
|
||||||
|
address_url = f"{SHOPWARE_URL}/api/order/{order_id}/billing-address/"
|
||||||
|
|
||||||
|
response_items = requests.get(items_url, headers=headers)
|
||||||
|
response_address = requests.get(address_url, headers=headers)
|
||||||
|
|
||||||
|
response_items.raise_for_status()
|
||||||
|
response_address.raise_for_status()
|
||||||
|
|
||||||
|
return response_items.json()["data"], response_address.json()["data"][0]
|
||||||
|
|
||||||
|
def show_order_details(event):
|
||||||
|
global current_order_id
|
||||||
|
|
||||||
|
selected_item = tree_orders.focus()
|
||||||
|
if not selected_item:
|
||||||
|
return
|
||||||
|
|
||||||
|
values = tree_orders.item(selected_item, "values")
|
||||||
|
order_number = values[0]
|
||||||
|
customer_name = values[1]
|
||||||
|
|
||||||
|
access_token = get_access_token()
|
||||||
|
orders = get_paid_orders(access_token)
|
||||||
|
selected_order = next((o for o in orders if o['attributes']['orderNumber'] == order_number), None)
|
||||||
|
|
||||||
|
if selected_order:
|
||||||
|
order_id = selected_order['id']
|
||||||
|
current_order_id = order_id
|
||||||
|
|
||||||
|
product_line, billing_address = get_order_details(access_token, order_id)
|
||||||
|
|
||||||
|
frame_orders.pack_forget()
|
||||||
|
tree_details.delete(*tree_details.get_children())
|
||||||
|
|
||||||
|
for product in product_line:
|
||||||
|
label = product['attributes'].get('label', 'Unbekanntes Produkt')
|
||||||
|
quantity = product['attributes']['quantity']
|
||||||
|
price = product['attributes']['totalPrice']
|
||||||
|
tree_details.insert("", "end", values=(label, quantity, f"{price:.2f} €"))
|
||||||
|
|
||||||
|
label_details.configure(text=f"Bestellung: {order_number} - {customer_name}")
|
||||||
|
frame_details.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
def back_to_orders():
|
||||||
|
frame_details.pack_forget()
|
||||||
|
frame_orders.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
def print_single_order():
|
||||||
|
if not current_order_id:
|
||||||
|
messagebox.showerror("Fehler", "Keine Bestellung ausgewählt!")
|
||||||
|
return
|
||||||
|
|
||||||
|
access_token = get_access_token()
|
||||||
|
orders = get_paid_orders(access_token)
|
||||||
|
selected_order = next((o for o in orders if o['id'] == current_order_id), None)
|
||||||
|
|
||||||
|
if selected_order:
|
||||||
|
filename = generate_combined_pdf([selected_order], access_token)
|
||||||
|
messagebox.showinfo("Erfolg", f"Lieferschein {filename} wurde erstellt!")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Fehler", "Bestellung nicht gefunden!")
|
||||||
|
|
||||||
|
def generate_combined_pdf(orders, access_token):
|
||||||
|
current_date = datetime.datetime.today().strftime("%d.%m.%Y")
|
||||||
|
filename = f"Lieferschein_{current_date}.pdf"
|
||||||
|
|
||||||
|
pdf = canvas.Canvas(filename, pagesize=A4)
|
||||||
|
pdf.setTitle(f"Lieferschein {current_date}")
|
||||||
|
pdf.setFont("Helvetica-Bold", 16)
|
||||||
|
pdf.drawString(50, 800, "Sammel-Lieferschein")
|
||||||
|
|
||||||
|
pdf.setFont("Helvetica", 12)
|
||||||
|
pdf.drawString(50, 780, f"Erstellt am: {current_date}")
|
||||||
|
|
||||||
|
y_position = 750
|
||||||
|
for order in orders:
|
||||||
|
try:
|
||||||
|
customer_id = order["relationships"]["orderCustomer"]["data"]["id"]
|
||||||
|
customer = get_order_customer(access_token, customer_id)
|
||||||
|
billing_address = get_order_billing_address(access_token, order["id"])
|
||||||
|
product_line = get_order_line_items(access_token, order["id"])
|
||||||
|
|
||||||
|
# Bestell-Header
|
||||||
|
pdf.setFont("Helvetica-Bold", 12)
|
||||||
|
pdf.drawString(50, y_position, f"Bestellnummer: {order['attributes']['orderNumber']}")
|
||||||
|
|
||||||
|
date_obj = datetime.datetime.strptime(order['attributes']['orderDate'], "%Y-%m-%dT%H:%M:%S.%f%z")
|
||||||
|
date_str = date_obj.strftime("%d.%m.%Y")
|
||||||
|
pdf.setFont("Helvetica", 10)
|
||||||
|
pdf.drawString(50, y_position - 20, f"Bestelldatum: {date_str}")
|
||||||
|
|
||||||
|
# Rahmen um Lieferadresse
|
||||||
|
pdf.setStrokeColorRGB(0, 0, 0) # Schwarz
|
||||||
|
#pdf.rect(45, y_position - 110, 300, 80, stroke=1, fill=0) # Rechteck um Adresse
|
||||||
|
|
||||||
|
# Lieferadresse
|
||||||
|
pdf.setFont("Helvetica-Bold", 10)
|
||||||
|
pdf.drawString(50, y_position - 100, "Lieferadresse:")
|
||||||
|
pdf.setFont("Helvetica", 10)
|
||||||
|
pdf.drawString(50, y_position - 120, f"{customer['attributes']['firstName']} {customer['attributes']['lastName']}")
|
||||||
|
pdf.drawString(50, y_position - 140, f"{billing_address['attributes']['street']}")
|
||||||
|
pdf.drawString(50, y_position - 160, f"{billing_address['attributes']['zipcode']} {billing_address['attributes']['city']}")
|
||||||
|
|
||||||
|
y_position -= 180 # Platz nach Adresse
|
||||||
|
|
||||||
|
# Produktliste
|
||||||
|
for product in product_line:
|
||||||
|
pdf.rect(50, y_position - 2, 10, 10, stroke=1, fill=0) # Checkbox für Abhaken
|
||||||
|
label = product['attributes'].get('label', 'Unbekanntes Produkt')
|
||||||
|
product_number = product['attributes']['payload'].get('productNumber', 'Keine Nummer')
|
||||||
|
quantity = product['attributes']['quantity']
|
||||||
|
price = product['attributes']['totalPrice']
|
||||||
|
|
||||||
|
pdf.drawString(70, y_position, f"{label}")
|
||||||
|
pdf.drawString(250, y_position, f"{product_number}")
|
||||||
|
pdf.drawString(350, y_position, f"{quantity}")
|
||||||
|
pdf.drawString(400, y_position, f"{price:.2f} €")
|
||||||
|
|
||||||
|
y_position -= 20
|
||||||
|
|
||||||
|
# Falls die Seite voll ist, neue Seite beginnen
|
||||||
|
if y_position < 100:
|
||||||
|
pdf.showPage()
|
||||||
|
y_position = 800
|
||||||
|
|
||||||
|
y_position -= 40 # Platz nach jeder Bestellung
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Erstellen des Lieferscheins für Bestellung {order['attributes']['orderNumber']}: {str(e)}")
|
||||||
|
|
||||||
|
pdf.save()
|
||||||
|
print(f"Lieferschein gespeichert: {filename}")
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def print_all_orders():
|
||||||
|
access_token = get_access_token()
|
||||||
|
orders = get_paid_orders(access_token)
|
||||||
|
|
||||||
|
if not orders:
|
||||||
|
messagebox.showinfo("Info", "Keine Bestellungen gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = generate_combined_pdf(orders, access_token)
|
||||||
|
messagebox.showinfo("Erfolg", f"Gesammelter Lieferschein {filename} wurde erstellt!")
|
||||||
|
|
||||||
|
def update_table():
|
||||||
|
access_token = get_access_token()
|
||||||
|
orders = get_paid_orders(access_token)
|
||||||
|
# Tabelle leeren
|
||||||
|
for row in tree_orders.get_children():
|
||||||
|
tree_orders.delete(row)
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
order_number = order['attributes']['orderNumber']
|
||||||
|
_, billing_address = get_order_details(access_token, order['id'])
|
||||||
|
|
||||||
|
tree_orders.insert("", "end", values=(
|
||||||
|
order_number,
|
||||||
|
f"{billing_address['attributes']['firstName']} {billing_address['attributes']['lastName']}",
|
||||||
|
billing_address['attributes']['street'],
|
||||||
|
f"{billing_address['attributes']['zipcode']} {billing_address['attributes']['city']}"
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def open_link(event=None):
|
||||||
|
webbrowser.open("https://itdata-gera.de")
|
||||||
|
|
||||||
|
def execute_with_error_handling(func, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs) # Die gewünschte Funktion ausführen
|
||||||
|
except Exception as e:
|
||||||
|
root.after(0, show_error_message, str(e)) # Fehler in GUI anzeigen
|
||||||
|
|
||||||
|
# GUI-Fehlermeldung anzeigen
|
||||||
|
def show_error_message(error_text):
|
||||||
|
tkinter.messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten:\n{error_text}")
|
||||||
|
|
||||||
|
# GUI-Erstellung
|
||||||
|
root = ctk.CTk()
|
||||||
|
root.title("Lieferschein-Drucker")
|
||||||
|
root.resizable(False, False)
|
||||||
|
|
||||||
|
window_width = 900
|
||||||
|
window_height = 500
|
||||||
|
|
||||||
|
# Bildschirmgröße abrufen
|
||||||
|
screen_width = root.winfo_screenwidth()
|
||||||
|
screen_height = root.winfo_screenheight()
|
||||||
|
|
||||||
|
# Position berechnen (Mitte des Bildschirms)
|
||||||
|
x_position = (screen_width // 2) - (window_width // 2)
|
||||||
|
y_position = (screen_height // 2) - (window_height // 2)
|
||||||
|
|
||||||
|
# Fenstergröße & Position setzen
|
||||||
|
root.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
|
||||||
|
|
||||||
|
# **Bestellübersicht**
|
||||||
|
frame_orders = ctk.CTkFrame(root)
|
||||||
|
button_all_print = ctk.CTkButton(frame_orders, text="API ändern", command=lambda: execute_with_error_handling(check_api.rerun),fg_color="#2b2b2b",text_color="white",anchor=tkinter.RIGHT)
|
||||||
|
button_all_print.pack(pady=5,anchor="ne")
|
||||||
|
|
||||||
|
label_orders = ctk.CTkLabel(frame_orders, text=f"Bezahlte Bestellungen - {datetime.datetime.now().strftime("%d.%m.%Y")}", font=("Helvetica", 15))
|
||||||
|
label_orders.pack(pady=5)
|
||||||
|
|
||||||
|
columns_orders = ("Bestellnummer", "Kunde", "Straße", "PLZ/Stadt")
|
||||||
|
tree_orders = ttk.Treeview(frame_orders, columns=columns_orders, show="headings", cursor="hand2")
|
||||||
|
for col in columns_orders:
|
||||||
|
tree_orders.heading(col, text=col)
|
||||||
|
tree_orders.column(col, width=150)
|
||||||
|
tree_orders.pack(expand=True, fill="both")
|
||||||
|
tree_orders.bind("<<TreeviewSelect>>", show_order_details)
|
||||||
|
|
||||||
|
button_update = ctk.CTkButton(frame_orders, text="Bestellungen laden", command=lambda: execute_with_error_handling(update_table), fg_color="lightblue",text_color="black",anchor=tkinter.RIGHT)
|
||||||
|
button_update.pack(side="left",pady=5,padx=10)
|
||||||
|
|
||||||
|
logo = ctk.CTkImage(dark_image=Image.open('logo.png'),size=(130,30))
|
||||||
|
logo_label = ctk.CTkLabel(root, text="", image=logo, fg_color="#2b2b2b",cursor="hand2")
|
||||||
|
logo_label.bind("<Button-1>", open_link)
|
||||||
|
logo_label.place(relx=0.5, rely=1.0, anchor="s",y=-15)
|
||||||
|
|
||||||
|
button_all_print = ctk.CTkButton(frame_orders, text="Gesammelten Lieferschein drucken", command=lambda: execute_with_error_handling(print_all_orders),fg_color="orange",text_color="black")
|
||||||
|
button_all_print.pack(side="right",pady=15, padx=10)
|
||||||
|
|
||||||
|
frame_orders.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
# **Detailansicht**
|
||||||
|
frame_details = ctk.CTkFrame(root)
|
||||||
|
label_details = ctk.CTkLabel(frame_details, text="", font=("Helvetica", 12))
|
||||||
|
label_details.pack(pady=5)
|
||||||
|
|
||||||
|
columns_details = ("Produkt", "Anzahl", "Preis")
|
||||||
|
tree_details = ttk.Treeview(frame_details, columns=columns_details, show="headings")
|
||||||
|
for col in columns_details:
|
||||||
|
tree_details.heading(col, text=col)
|
||||||
|
tree_details.pack(expand=True, fill="both")
|
||||||
|
|
||||||
|
button_back = ctk.CTkButton(frame_details, text="Zurück", command=back_to_orders)
|
||||||
|
button_back.pack(side="left", padx=20, pady=15)
|
||||||
|
|
||||||
|
logo_label_1 = ctk.CTkLabel(root, text="", image=logo, fg_color="#2b2b2b",cursor="hand2")
|
||||||
|
logo_label_1.bind("<Button-1>", open_link)
|
||||||
|
logo_label_1.place(relx=0.5, rely=1.0, anchor="s",y=-15)
|
||||||
|
|
||||||
|
button_print = ctk.CTkButton(frame_details, text="Diese Bestellung drucken", command=print_single_order, fg_color="lightgreen", text_color="black")
|
||||||
|
button_print.pack(side="right", padx=20,pady=15)
|
||||||
|
|
||||||
|
# Error Handling für Timeout
|
||||||
|
root.after(100, lambda: execute_with_error_handling(update_table))
|
||||||
|
root.mainloop()
|
||||||
Loading…
Reference in New Issue